Roo/data/DataReader.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * Bootstrap Component base class
227  * @cfg {String} cls css class
228  * @cfg {String} style any extra css
229  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
230  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
231  * @cfg {string} dataId cutomer id
232  * @cfg {string} name Specifies name attribute
233  * @cfg {string} tooltip  Text for the tooltip
234  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
235  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
236  
237  * @constructor
238  * Do not use directly - it does not do anything..
239  * @param {Object} config The config object
240  */
241
242
243
244 Roo.bootstrap.Component = function(config){
245     Roo.bootstrap.Component.superclass.constructor.call(this, config);
246        
247     this.addEvents({
248         /**
249          * @event childrenrendered
250          * Fires when the children have been rendered..
251          * @param {Roo.bootstrap.Component} this
252          */
253         "childrenrendered" : true
254         
255         
256         
257     });
258     
259     
260 };
261
262 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
263     
264     
265     allowDomMove : false, // to stop relocations in parent onRender...
266     
267     cls : false,
268     
269     style : false,
270     
271     autoCreate : false,
272     
273     tooltip : null,
274     /**
275      * Initialize Events for the element
276      */
277     initEvents : function() { },
278     
279     xattr : false,
280     
281     parentId : false,
282     
283     can_build_overlaid : true,
284     
285     container_method : false,
286     
287     dataId : false,
288     
289     name : false,
290     
291     parent: function() {
292         // returns the parent component..
293         return Roo.ComponentMgr.get(this.parentId)
294         
295         
296     },
297     
298     // private
299     onRender : function(ct, position)
300     {
301        // Roo.log("Call onRender: " + this.xtype);
302         
303         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
304         
305         if(this.el){
306             if (this.el.attr('xtype')) {
307                 this.el.attr('xtypex', this.el.attr('xtype'));
308                 this.el.dom.removeAttribute('xtype');
309                 
310                 this.initEvents();
311             }
312             
313             return;
314         }
315         
316          
317         
318         var cfg = Roo.apply({},  this.getAutoCreate());
319         
320         cfg.id = this.id || Roo.id();
321         
322         // fill in the extra attributes 
323         if (this.xattr && typeof(this.xattr) =='object') {
324             for (var i in this.xattr) {
325                 cfg[i] = this.xattr[i];
326             }
327         }
328         
329         if(this.dataId){
330             cfg.dataId = this.dataId;
331         }
332         
333         if (this.cls) {
334             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
335         }
336         
337         if (this.style) { // fixme needs to support more complex style data.
338             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
339         }
340         
341         if(this.name){
342             cfg.name = this.name;
343         }
344         
345         this.el = ct.createChild(cfg, position);
346         
347         if (this.tooltip) {
348             this.tooltipEl().attr('tooltip', this.tooltip);
349         }
350         
351         if(this.tabIndex !== undefined){
352             this.el.dom.setAttribute('tabIndex', this.tabIndex);
353         }
354         
355         this.initEvents();
356         
357     },
358     /**
359      * Fetch the element to add children to
360      * @return {Roo.Element} defaults to this.el
361      */
362     getChildContainer : function()
363     {
364         return this.el;
365     },
366     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367     {
368         return Roo.get(document.body);
369     },
370     
371     /**
372      * Fetch the element to display the tooltip on.
373      * @return {Roo.Element} defaults to this.el
374      */
375     tooltipEl : function()
376     {
377         return this.el;
378     },
379         
380     addxtype  : function(tree,cntr)
381     {
382         var cn = this;
383         
384         cn = Roo.factory(tree);
385         //Roo.log(['addxtype', cn]);
386            
387         cn.parentType = this.xtype; //??
388         cn.parentId = this.id;
389         
390         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
391         if (typeof(cn.container_method) == 'string') {
392             cntr = cn.container_method;
393         }
394         
395         
396         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
397         
398         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
399         
400         var build_from_html =  Roo.XComponent.build_from_html;
401           
402         var is_body  = (tree.xtype == 'Body') ;
403           
404         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405           
406         var self_cntr_el = Roo.get(this[cntr](false));
407         
408         // do not try and build conditional elements 
409         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
410             return false;
411         }
412         
413         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
414             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
415                 return this.addxtypeChild(tree,cntr, is_body);
416             }
417             
418             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
419                 
420             if(echild){
421                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
422             }
423             
424             Roo.log('skipping render');
425             return cn;
426             
427         }
428         
429         var ret = false;
430         if (!build_from_html) {
431             return false;
432         }
433         
434         // this i think handles overlaying multiple children of the same type
435         // with the sam eelement.. - which might be buggy..
436         while (true) {
437             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
438             
439             if (!echild) {
440                 break;
441             }
442             
443             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
444                 break;
445             }
446             
447             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448         }
449        
450         return ret;
451     },
452     
453     
454     addxtypeChild : function (tree, cntr, is_body)
455     {
456         Roo.debug && Roo.log('addxtypeChild:' + cntr);
457         var cn = this;
458         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
459         
460         
461         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
462                     (typeof(tree['flexy:foreach']) != 'undefined');
463           
464     
465         
466         skip_children = false;
467         // render the element if it's not BODY.
468         if (!is_body) {
469             
470             // if parent was disabled, then do not try and create the children..
471             if(!this[cntr](true)){
472                 tree.items = [];
473                 return tree;
474             }
475            
476             cn = Roo.factory(tree);
477            
478             cn.parentType = this.xtype; //??
479             cn.parentId = this.id;
480             
481             var build_from_html =  Roo.XComponent.build_from_html;
482             
483             
484             // does the container contain child eleemnts with 'xtype' attributes.
485             // that match this xtype..
486             // note - when we render we create these as well..
487             // so we should check to see if body has xtype set.
488             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489                
490                 var self_cntr_el = Roo.get(this[cntr](false));
491                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492                 if (echild) { 
493                     //Roo.log(Roo.XComponent.build_from_html);
494                     //Roo.log("got echild:");
495                     //Roo.log(echild);
496                 }
497                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
498                 // and are not displayed -this causes this to use up the wrong element when matching.
499                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
500                 
501                 
502                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
503                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
504                   
505                   
506                   
507                     cn.el = echild;
508                   //  Roo.log("GOT");
509                     //echild.dom.removeAttribute('xtype');
510                 } else {
511                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
512                     Roo.debug && Roo.log(self_cntr_el);
513                     Roo.debug && Roo.log(echild);
514                     Roo.debug && Roo.log(cn);
515                 }
516             }
517            
518             
519            
520             // if object has flexy:if - then it may or may not be rendered.
521             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
522                 // skip a flexy if element.
523                 Roo.debug && Roo.log('skipping render');
524                 Roo.debug && Roo.log(tree);
525                 if (!cn.el) {
526                     Roo.debug && Roo.log('skipping all children');
527                     skip_children = true;
528                 }
529                 
530              } else {
531                  
532                 // actually if flexy:foreach is found, we really want to create 
533                 // multiple copies here...
534                 //Roo.log('render');
535                 //Roo.log(this[cntr]());
536                 // some elements do not have render methods.. like the layouts...
537                 /*
538                 if(this[cntr](true) === false){
539                     cn.items = [];
540                     return cn;
541                 }
542                 */
543                 cn.render && cn.render(this[cntr](true));
544                 
545              }
546             // then add the element..
547         }
548          
549         // handle the kids..
550         
551         var nitems = [];
552         /*
553         if (typeof (tree.menu) != 'undefined') {
554             tree.menu.parentType = cn.xtype;
555             tree.menu.triggerEl = cn.el;
556             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
557             
558         }
559         */
560         if (!tree.items || !tree.items.length) {
561             cn.items = nitems;
562             //Roo.log(["no children", this]);
563             
564             return cn;
565         }
566          
567         var items = tree.items;
568         delete tree.items;
569         
570         //Roo.log(items.length);
571             // add the items..
572         if (!skip_children) {    
573             for(var i =0;i < items.length;i++) {
574               //  Roo.log(['add child', items[i]]);
575                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
576             }
577         }
578         
579         cn.items = nitems;
580         
581         //Roo.log("fire childrenrendered");
582         
583         cn.fireEvent('childrenrendered', this);
584         
585         return cn;
586     },
587     
588     /**
589      * Set the element that will be used to show or hide
590      */
591     setVisibilityEl : function(el)
592     {
593         this.visibilityEl = el;
594     },
595     
596      /**
597      * Get the element that will be used to show or hide
598      */
599     getVisibilityEl : function()
600     {
601         if (typeof(this.visibilityEl) == 'object') {
602             return this.visibilityEl;
603         }
604         
605         if (typeof(this.visibilityEl) == 'string') {
606             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607         }
608         
609         return this.getEl();
610     },
611     
612     /**
613      * Show a component - removes 'hidden' class
614      */
615     show : function()
616     {
617         if(!this.getVisibilityEl()){
618             return;
619         }
620          
621         this.getVisibilityEl().removeClass(['hidden','d-none']);
622         
623         this.fireEvent('show', this);
624         
625         
626     },
627     /**
628      * Hide a component - adds 'hidden' class
629      */
630     hide: function()
631     {
632         if(!this.getVisibilityEl()){
633             return;
634         }
635         
636         this.getVisibilityEl().addClass(['hidden','d-none']);
637         
638         this.fireEvent('hide', this);
639         
640     }
641 });
642
643  /*
644  * - LGPL
645  *
646  * element
647  * 
648  */
649
650 /**
651  * @class Roo.bootstrap.Element
652  * @extends Roo.bootstrap.Component
653  * Bootstrap Element class
654  * @cfg {String} html contents of the element
655  * @cfg {String} tag tag of the element
656  * @cfg {String} cls class of the element
657  * @cfg {Boolean} preventDefault (true|false) default false
658  * @cfg {Boolean} clickable (true|false) default false
659  * @cfg {String} role default blank - set to button to force cursor pointer
660  
661  * 
662  * @constructor
663  * Create a new Element
664  * @param {Object} config The config object
665  */
666
667 Roo.bootstrap.Element = function(config){
668     Roo.bootstrap.Element.superclass.constructor.call(this, config);
669     
670     this.addEvents({
671         // raw events
672         /**
673          * @event click
674          * When a element is chick
675          * @param {Roo.bootstrap.Element} this
676          * @param {Roo.EventObject} e
677          */
678         "click" : true 
679         
680       
681     });
682 };
683
684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
685     
686     tag: 'div',
687     cls: '',
688     html: '',
689     preventDefault: false, 
690     clickable: false,
691     tapedTwice : false,
692     role : false,
693     
694     getAutoCreate : function(){
695         
696         var cfg = {
697             tag: this.tag,
698             // cls: this.cls, double assign in parent class Component.js :: onRender
699             html: this.html
700         };
701         if (this.role !== false) {
702             cfg.role = this.role;
703         }
704         
705         return cfg;
706     },
707     
708     initEvents: function() 
709     {
710         Roo.bootstrap.Element.superclass.initEvents.call(this);
711         
712         if(this.clickable){
713             this.el.on('click', this.onClick, this);
714         }
715         
716         
717     },
718     
719     onClick : function(e)
720     {
721         if(this.preventDefault){
722             e.preventDefault();
723         }
724         
725         this.fireEvent('click', this, e); // why was this double click before?
726     },
727     
728     
729     
730
731     
732     
733     getValue : function()
734     {
735         return this.el.dom.innerHTML;
736     },
737     
738     setValue : function(value)
739     {
740         this.el.dom.innerHTML = value;
741     }
742    
743 });
744
745  
746
747  /*
748  * - LGPL
749  *
750  * dropable area
751  * 
752  */
753
754 /**
755  * @class Roo.bootstrap.DropTarget
756  * @extends Roo.bootstrap.Element
757  * Bootstrap DropTarget class
758  
759  * @cfg {string} name dropable name
760  * 
761  * @constructor
762  * Create a new Dropable Area
763  * @param {Object} config The config object
764  */
765
766 Roo.bootstrap.DropTarget = function(config){
767     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
768     
769     this.addEvents({
770         // raw events
771         /**
772          * @event click
773          * When a element is chick
774          * @param {Roo.bootstrap.Element} this
775          * @param {Roo.EventObject} e
776          */
777         "drop" : true
778     });
779 };
780
781 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
782     
783     
784     getAutoCreate : function(){
785         
786          
787     },
788     
789     initEvents: function() 
790     {
791         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
792         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
793             ddGroup: this.name,
794             listeners : {
795                 drop : this.dragDrop.createDelegate(this),
796                 enter : this.dragEnter.createDelegate(this),
797                 out : this.dragOut.createDelegate(this),
798                 over : this.dragOver.createDelegate(this)
799             }
800             
801         });
802         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
803     },
804     
805     dragDrop : function(source,e,data)
806     {
807         // user has to decide how to impliment this.
808         Roo.log('drop');
809         Roo.log(this);
810         //this.fireEvent('drop', this, source, e ,data);
811         return false;
812     },
813     
814     dragEnter : function(n, dd, e, data)
815     {
816         // probably want to resize the element to match the dropped element..
817         Roo.log("enter");
818         this.originalSize = this.el.getSize();
819         this.el.setSize( n.el.getSize());
820         this.dropZone.DDM.refreshCache(this.name);
821         Roo.log([n, dd, e, data]);
822     },
823     
824     dragOut : function(value)
825     {
826         // resize back to normal
827         Roo.log("out");
828         this.el.setSize(this.originalSize);
829         this.dropZone.resetConstraints();
830     },
831     
832     dragOver : function()
833     {
834         // ??? do nothing?
835     }
836    
837 });
838
839  
840
841  /*
842  * - LGPL
843  *
844  * Body
845  *
846  */
847
848 /**
849  * @class Roo.bootstrap.Body
850  * @extends Roo.bootstrap.Component
851  * 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
7423  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7424  * implemented by descendant classes.  This class should not be directly instantiated.
7425  * @constructor
7426  */
7427 Roo.grid.AbstractSelectionModel = function(){
7428     this.locked = false;
7429     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7430 };
7431
7432 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7433     /** @ignore Called by the grid automatically. Do not call directly. */
7434     init : function(grid){
7435         this.grid = grid;
7436         this.initEvents();
7437     },
7438
7439     /**
7440      * Locks the selections.
7441      */
7442     lock : function(){
7443         this.locked = true;
7444     },
7445
7446     /**
7447      * Unlocks the selections.
7448      */
7449     unlock : function(){
7450         this.locked = false;
7451     },
7452
7453     /**
7454      * Returns true if the selections are locked.
7455      * @return {Boolean}
7456      */
7457     isLocked : function(){
7458         return this.locked;
7459     }
7460 });/*
7461  * Based on:
7462  * Ext JS Library 1.1.1
7463  * Copyright(c) 2006-2007, Ext JS, LLC.
7464  *
7465  * Originally Released Under LGPL - original licence link has changed is not relivant.
7466  *
7467  * Fork - LGPL
7468  * <script type="text/javascript">
7469  */
7470 /**
7471  * @extends Roo.grid.AbstractSelectionModel
7472  * @class Roo.grid.RowSelectionModel
7473  * The default SelectionModel used by {@link Roo.grid.Grid}.
7474  * It supports multiple selections and keyboard selection/navigation. 
7475  * @constructor
7476  * @param {Object} config
7477  */
7478 Roo.grid.RowSelectionModel = function(config){
7479     Roo.apply(this, config);
7480     this.selections = new Roo.util.MixedCollection(false, function(o){
7481         return o.id;
7482     });
7483
7484     this.last = false;
7485     this.lastActive = false;
7486
7487     this.addEvents({
7488         /**
7489         * @event selectionchange
7490         * Fires when the selection changes
7491         * @param {SelectionModel} this
7492         */
7493        "selectionchange" : true,
7494        /**
7495         * @event afterselectionchange
7496         * Fires after the selection changes (eg. by key press or clicking)
7497         * @param {SelectionModel} this
7498         */
7499        "afterselectionchange" : true,
7500        /**
7501         * @event beforerowselect
7502         * Fires when a row is selected being selected, return false to cancel.
7503         * @param {SelectionModel} this
7504         * @param {Number} rowIndex The selected index
7505         * @param {Boolean} keepExisting False if other selections will be cleared
7506         */
7507        "beforerowselect" : true,
7508        /**
7509         * @event rowselect
7510         * Fires when a row is selected.
7511         * @param {SelectionModel} this
7512         * @param {Number} rowIndex The selected index
7513         * @param {Roo.data.Record} r The record
7514         */
7515        "rowselect" : true,
7516        /**
7517         * @event rowdeselect
7518         * Fires when a row is deselected.
7519         * @param {SelectionModel} this
7520         * @param {Number} rowIndex The selected index
7521         */
7522         "rowdeselect" : true
7523     });
7524     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7525     this.locked = false;
7526 };
7527
7528 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7529     /**
7530      * @cfg {Boolean} singleSelect
7531      * True to allow selection of only one row at a time (defaults to false)
7532      */
7533     singleSelect : false,
7534
7535     // private
7536     initEvents : function(){
7537
7538         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7539             this.grid.on("mousedown", this.handleMouseDown, this);
7540         }else{ // allow click to work like normal
7541             this.grid.on("rowclick", this.handleDragableRowClick, this);
7542         }
7543         // bootstrap does not have a view..
7544         var view = this.grid.view ? this.grid.view : this.grid;
7545         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7546             "up" : function(e){
7547                 if(!e.shiftKey){
7548                     this.selectPrevious(e.shiftKey);
7549                 }else if(this.last !== false && this.lastActive !== false){
7550                     var last = this.last;
7551                     this.selectRange(this.last,  this.lastActive-1);
7552                     view.focusRow(this.lastActive);
7553                     if(last !== false){
7554                         this.last = last;
7555                     }
7556                 }else{
7557                     this.selectFirstRow();
7558                 }
7559                 this.fireEvent("afterselectionchange", this);
7560             },
7561             "down" : function(e){
7562                 if(!e.shiftKey){
7563                     this.selectNext(e.shiftKey);
7564                 }else if(this.last !== false && this.lastActive !== false){
7565                     var last = this.last;
7566                     this.selectRange(this.last,  this.lastActive+1);
7567                     view.focusRow(this.lastActive);
7568                     if(last !== false){
7569                         this.last = last;
7570                     }
7571                 }else{
7572                     this.selectFirstRow();
7573                 }
7574                 this.fireEvent("afterselectionchange", this);
7575             },
7576             scope: this
7577         });
7578
7579          
7580         view.on("refresh", this.onRefresh, this);
7581         view.on("rowupdated", this.onRowUpdated, this);
7582         view.on("rowremoved", this.onRemove, this);
7583     },
7584
7585     // private
7586     onRefresh : function(){
7587         var ds = this.grid.ds, i, v = this.grid.view;
7588         var s = this.selections;
7589         s.each(function(r){
7590             if((i = ds.indexOfId(r.id)) != -1){
7591                 v.onRowSelect(i);
7592                 s.add(ds.getAt(i)); // updating the selection relate data
7593             }else{
7594                 s.remove(r);
7595             }
7596         });
7597     },
7598
7599     // private
7600     onRemove : function(v, index, r){
7601         this.selections.remove(r);
7602     },
7603
7604     // private
7605     onRowUpdated : function(v, index, r){
7606         if(this.isSelected(r)){
7607             v.onRowSelect(index);
7608         }
7609     },
7610
7611     /**
7612      * Select records.
7613      * @param {Array} records The records to select
7614      * @param {Boolean} keepExisting (optional) True to keep existing selections
7615      */
7616     selectRecords : function(records, keepExisting){
7617         if(!keepExisting){
7618             this.clearSelections();
7619         }
7620         var ds = this.grid.ds;
7621         for(var i = 0, len = records.length; i < len; i++){
7622             this.selectRow(ds.indexOf(records[i]), true);
7623         }
7624     },
7625
7626     /**
7627      * Gets the number of selected rows.
7628      * @return {Number}
7629      */
7630     getCount : function(){
7631         return this.selections.length;
7632     },
7633
7634     /**
7635      * Selects the first row in the grid.
7636      */
7637     selectFirstRow : function(){
7638         this.selectRow(0);
7639     },
7640
7641     /**
7642      * Select the last row.
7643      * @param {Boolean} keepExisting (optional) True to keep existing selections
7644      */
7645     selectLastRow : function(keepExisting){
7646         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7647     },
7648
7649     /**
7650      * Selects the row immediately following the last selected row.
7651      * @param {Boolean} keepExisting (optional) True to keep existing selections
7652      */
7653     selectNext : function(keepExisting){
7654         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7655             this.selectRow(this.last+1, keepExisting);
7656             var view = this.grid.view ? this.grid.view : this.grid;
7657             view.focusRow(this.last);
7658         }
7659     },
7660
7661     /**
7662      * Selects the row that precedes the last selected row.
7663      * @param {Boolean} keepExisting (optional) True to keep existing selections
7664      */
7665     selectPrevious : function(keepExisting){
7666         if(this.last){
7667             this.selectRow(this.last-1, keepExisting);
7668             var view = this.grid.view ? this.grid.view : this.grid;
7669             view.focusRow(this.last);
7670         }
7671     },
7672
7673     /**
7674      * Returns the selected records
7675      * @return {Array} Array of selected records
7676      */
7677     getSelections : function(){
7678         return [].concat(this.selections.items);
7679     },
7680
7681     /**
7682      * Returns the first selected record.
7683      * @return {Record}
7684      */
7685     getSelected : function(){
7686         return this.selections.itemAt(0);
7687     },
7688
7689
7690     /**
7691      * Clears all selections.
7692      */
7693     clearSelections : function(fast){
7694         if(this.locked) {
7695             return;
7696         }
7697         if(fast !== true){
7698             var ds = this.grid.ds;
7699             var s = this.selections;
7700             s.each(function(r){
7701                 this.deselectRow(ds.indexOfId(r.id));
7702             }, this);
7703             s.clear();
7704         }else{
7705             this.selections.clear();
7706         }
7707         this.last = false;
7708     },
7709
7710
7711     /**
7712      * Selects all rows.
7713      */
7714     selectAll : function(){
7715         if(this.locked) {
7716             return;
7717         }
7718         this.selections.clear();
7719         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7720             this.selectRow(i, true);
7721         }
7722     },
7723
7724     /**
7725      * Returns True if there is a selection.
7726      * @return {Boolean}
7727      */
7728     hasSelection : function(){
7729         return this.selections.length > 0;
7730     },
7731
7732     /**
7733      * Returns True if the specified row is selected.
7734      * @param {Number/Record} record The record or index of the record to check
7735      * @return {Boolean}
7736      */
7737     isSelected : function(index){
7738         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7739         return (r && this.selections.key(r.id) ? true : false);
7740     },
7741
7742     /**
7743      * Returns True if the specified record id is selected.
7744      * @param {String} id The id of record to check
7745      * @return {Boolean}
7746      */
7747     isIdSelected : function(id){
7748         return (this.selections.key(id) ? true : false);
7749     },
7750
7751     // private
7752     handleMouseDown : function(e, t)
7753     {
7754         var view = this.grid.view ? this.grid.view : this.grid;
7755         var rowIndex;
7756         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7757             return;
7758         };
7759         if(e.shiftKey && this.last !== false){
7760             var last = this.last;
7761             this.selectRange(last, rowIndex, e.ctrlKey);
7762             this.last = last; // reset the last
7763             view.focusRow(rowIndex);
7764         }else{
7765             var isSelected = this.isSelected(rowIndex);
7766             if(e.button !== 0 && isSelected){
7767                 view.focusRow(rowIndex);
7768             }else if(e.ctrlKey && isSelected){
7769                 this.deselectRow(rowIndex);
7770             }else if(!isSelected){
7771                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7772                 view.focusRow(rowIndex);
7773             }
7774         }
7775         this.fireEvent("afterselectionchange", this);
7776     },
7777     // private
7778     handleDragableRowClick :  function(grid, rowIndex, e) 
7779     {
7780         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7781             this.selectRow(rowIndex, false);
7782             var view = this.grid.view ? this.grid.view : this.grid;
7783             view.focusRow(rowIndex);
7784              this.fireEvent("afterselectionchange", this);
7785         }
7786     },
7787     
7788     /**
7789      * Selects multiple rows.
7790      * @param {Array} rows Array of the indexes of the row to select
7791      * @param {Boolean} keepExisting (optional) True to keep existing selections
7792      */
7793     selectRows : function(rows, keepExisting){
7794         if(!keepExisting){
7795             this.clearSelections();
7796         }
7797         for(var i = 0, len = rows.length; i < len; i++){
7798             this.selectRow(rows[i], true);
7799         }
7800     },
7801
7802     /**
7803      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7804      * @param {Number} startRow The index of the first row in the range
7805      * @param {Number} endRow The index of the last row in the range
7806      * @param {Boolean} keepExisting (optional) True to retain existing selections
7807      */
7808     selectRange : function(startRow, endRow, keepExisting){
7809         if(this.locked) {
7810             return;
7811         }
7812         if(!keepExisting){
7813             this.clearSelections();
7814         }
7815         if(startRow <= endRow){
7816             for(var i = startRow; i <= endRow; i++){
7817                 this.selectRow(i, true);
7818             }
7819         }else{
7820             for(var i = startRow; i >= endRow; i--){
7821                 this.selectRow(i, true);
7822             }
7823         }
7824     },
7825
7826     /**
7827      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7828      * @param {Number} startRow The index of the first row in the range
7829      * @param {Number} endRow The index of the last row in the range
7830      */
7831     deselectRange : function(startRow, endRow, preventViewNotify){
7832         if(this.locked) {
7833             return;
7834         }
7835         for(var i = startRow; i <= endRow; i++){
7836             this.deselectRow(i, preventViewNotify);
7837         }
7838     },
7839
7840     /**
7841      * Selects a row.
7842      * @param {Number} row The index of the row to select
7843      * @param {Boolean} keepExisting (optional) True to keep existing selections
7844      */
7845     selectRow : function(index, keepExisting, preventViewNotify){
7846         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7847             return;
7848         }
7849         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7850             if(!keepExisting || this.singleSelect){
7851                 this.clearSelections();
7852             }
7853             var r = this.grid.ds.getAt(index);
7854             this.selections.add(r);
7855             this.last = this.lastActive = index;
7856             if(!preventViewNotify){
7857                 var view = this.grid.view ? this.grid.view : this.grid;
7858                 view.onRowSelect(index);
7859             }
7860             this.fireEvent("rowselect", this, index, r);
7861             this.fireEvent("selectionchange", this);
7862         }
7863     },
7864
7865     /**
7866      * Deselects a row.
7867      * @param {Number} row The index of the row to deselect
7868      */
7869     deselectRow : function(index, preventViewNotify){
7870         if(this.locked) {
7871             return;
7872         }
7873         if(this.last == index){
7874             this.last = false;
7875         }
7876         if(this.lastActive == index){
7877             this.lastActive = false;
7878         }
7879         var r = this.grid.ds.getAt(index);
7880         this.selections.remove(r);
7881         if(!preventViewNotify){
7882             var view = this.grid.view ? this.grid.view : this.grid;
7883             view.onRowDeselect(index);
7884         }
7885         this.fireEvent("rowdeselect", this, index);
7886         this.fireEvent("selectionchange", this);
7887     },
7888
7889     // private
7890     restoreLast : function(){
7891         if(this._last){
7892             this.last = this._last;
7893         }
7894     },
7895
7896     // private
7897     acceptsNav : function(row, col, cm){
7898         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7899     },
7900
7901     // private
7902     onEditorKey : function(field, e){
7903         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7904         if(k == e.TAB){
7905             e.stopEvent();
7906             ed.completeEdit();
7907             if(e.shiftKey){
7908                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7909             }else{
7910                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7911             }
7912         }else if(k == e.ENTER && !e.ctrlKey){
7913             e.stopEvent();
7914             ed.completeEdit();
7915             if(e.shiftKey){
7916                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7917             }else{
7918                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7919             }
7920         }else if(k == e.ESC){
7921             ed.cancelEdit();
7922         }
7923         if(newCell){
7924             g.startEditing(newCell[0], newCell[1]);
7925         }
7926     }
7927 });/*
7928  * Based on:
7929  * Ext JS Library 1.1.1
7930  * Copyright(c) 2006-2007, Ext JS, LLC.
7931  *
7932  * Originally Released Under LGPL - original licence link has changed is not relivant.
7933  *
7934  * Fork - LGPL
7935  * <script type="text/javascript">
7936  */
7937  
7938
7939 /**
7940  * @class Roo.grid.ColumnModel
7941  * @extends Roo.util.Observable
7942  * This is the default implementation of a ColumnModel used by the Grid. It defines
7943  * the columns in the grid.
7944  * <br>Usage:<br>
7945  <pre><code>
7946  var colModel = new Roo.grid.ColumnModel([
7947         {header: "Ticker", width: 60, sortable: true, locked: true},
7948         {header: "Company Name", width: 150, sortable: true},
7949         {header: "Market Cap.", width: 100, sortable: true},
7950         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7951         {header: "Employees", width: 100, sortable: true, resizable: false}
7952  ]);
7953  </code></pre>
7954  * <p>
7955  
7956  * The config options listed for this class are options which may appear in each
7957  * individual column definition.
7958  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7959  * @constructor
7960  * @param {Object} config An Array of column config objects. See this class's
7961  * config objects for details.
7962 */
7963 Roo.grid.ColumnModel = function(config){
7964         /**
7965      * The config passed into the constructor
7966      */
7967     this.config = []; //config;
7968     this.lookup = {};
7969
7970     // if no id, create one
7971     // if the column does not have a dataIndex mapping,
7972     // map it to the order it is in the config
7973     for(var i = 0, len = config.length; i < len; i++){
7974         this.addColumn(config[i]);
7975         
7976     }
7977
7978     /**
7979      * The width of columns which have no width specified (defaults to 100)
7980      * @type Number
7981      */
7982     this.defaultWidth = 100;
7983
7984     /**
7985      * Default sortable of columns which have no sortable specified (defaults to false)
7986      * @type Boolean
7987      */
7988     this.defaultSortable = false;
7989
7990     this.addEvents({
7991         /**
7992              * @event widthchange
7993              * Fires when the width of a column changes.
7994              * @param {ColumnModel} this
7995              * @param {Number} columnIndex The column index
7996              * @param {Number} newWidth The new width
7997              */
7998             "widthchange": true,
7999         /**
8000              * @event headerchange
8001              * Fires when the text of a header changes.
8002              * @param {ColumnModel} this
8003              * @param {Number} columnIndex The column index
8004              * @param {Number} newText The new header text
8005              */
8006             "headerchange": true,
8007         /**
8008              * @event hiddenchange
8009              * Fires when a column is hidden or "unhidden".
8010              * @param {ColumnModel} this
8011              * @param {Number} columnIndex The column index
8012              * @param {Boolean} hidden true if hidden, false otherwise
8013              */
8014             "hiddenchange": true,
8015             /**
8016          * @event columnmoved
8017          * Fires when a column is moved.
8018          * @param {ColumnModel} this
8019          * @param {Number} oldIndex
8020          * @param {Number} newIndex
8021          */
8022         "columnmoved" : true,
8023         /**
8024          * @event columlockchange
8025          * Fires when a column's locked state is changed
8026          * @param {ColumnModel} this
8027          * @param {Number} colIndex
8028          * @param {Boolean} locked true if locked
8029          */
8030         "columnlockchange" : true
8031     });
8032     Roo.grid.ColumnModel.superclass.constructor.call(this);
8033 };
8034 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8035     /**
8036      * @cfg {String} header The header text to display in the Grid view.
8037      */
8038         /**
8039      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8040      */
8041         /**
8042      * @cfg {String} smHeader Header at Bootsrap Small width
8043      */
8044         /**
8045      * @cfg {String} mdHeader Header at Bootsrap Medium width
8046      */
8047         /**
8048      * @cfg {String} lgHeader Header at Bootsrap Large width
8049      */
8050         /**
8051      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8052      */
8053     /**
8054      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8055      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8056      * specified, the column's index is used as an index into the Record's data Array.
8057      */
8058     /**
8059      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8060      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8061      */
8062     /**
8063      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8064      * Defaults to the value of the {@link #defaultSortable} property.
8065      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8066      */
8067     /**
8068      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8069      */
8070     /**
8071      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8072      */
8073     /**
8074      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8075      */
8076     /**
8077      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8078      */
8079     /**
8080      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8081      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8082      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8083      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8084      */
8085        /**
8086      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8087      */
8088     /**
8089      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8090      */
8091     /**
8092      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8093      */
8094     /**
8095      * @cfg {String} cursor (Optional)
8096      */
8097     /**
8098      * @cfg {String} tooltip (Optional)
8099      */
8100     /**
8101      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8102      */
8103     /**
8104      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8105      */
8106     /**
8107      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8108      */
8109     /**
8110      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8111      */
8112         /**
8113      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8114      */
8115     /**
8116      * Returns the id of the column at the specified index.
8117      * @param {Number} index The column index
8118      * @return {String} the id
8119      */
8120     getColumnId : function(index){
8121         return this.config[index].id;
8122     },
8123
8124     /**
8125      * Returns the column for a specified id.
8126      * @param {String} id The column id
8127      * @return {Object} the column
8128      */
8129     getColumnById : function(id){
8130         return this.lookup[id];
8131     },
8132
8133     
8134     /**
8135      * Returns the column Object for a specified dataIndex.
8136      * @param {String} dataIndex The column dataIndex
8137      * @return {Object|Boolean} the column or false if not found
8138      */
8139     getColumnByDataIndex: function(dataIndex){
8140         var index = this.findColumnIndex(dataIndex);
8141         return index > -1 ? this.config[index] : false;
8142     },
8143     
8144     /**
8145      * Returns the index for a specified column id.
8146      * @param {String} id The column id
8147      * @return {Number} the index, or -1 if not found
8148      */
8149     getIndexById : function(id){
8150         for(var i = 0, len = this.config.length; i < len; i++){
8151             if(this.config[i].id == id){
8152                 return i;
8153             }
8154         }
8155         return -1;
8156     },
8157     
8158     /**
8159      * Returns the index for a specified column dataIndex.
8160      * @param {String} dataIndex The column dataIndex
8161      * @return {Number} the index, or -1 if not found
8162      */
8163     
8164     findColumnIndex : function(dataIndex){
8165         for(var i = 0, len = this.config.length; i < len; i++){
8166             if(this.config[i].dataIndex == dataIndex){
8167                 return i;
8168             }
8169         }
8170         return -1;
8171     },
8172     
8173     
8174     moveColumn : function(oldIndex, newIndex){
8175         var c = this.config[oldIndex];
8176         this.config.splice(oldIndex, 1);
8177         this.config.splice(newIndex, 0, c);
8178         this.dataMap = null;
8179         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8180     },
8181
8182     isLocked : function(colIndex){
8183         return this.config[colIndex].locked === true;
8184     },
8185
8186     setLocked : function(colIndex, value, suppressEvent){
8187         if(this.isLocked(colIndex) == value){
8188             return;
8189         }
8190         this.config[colIndex].locked = value;
8191         if(!suppressEvent){
8192             this.fireEvent("columnlockchange", this, colIndex, value);
8193         }
8194     },
8195
8196     getTotalLockedWidth : function(){
8197         var totalWidth = 0;
8198         for(var i = 0; i < this.config.length; i++){
8199             if(this.isLocked(i) && !this.isHidden(i)){
8200                 this.totalWidth += this.getColumnWidth(i);
8201             }
8202         }
8203         return totalWidth;
8204     },
8205
8206     getLockedCount : function(){
8207         for(var i = 0, len = this.config.length; i < len; i++){
8208             if(!this.isLocked(i)){
8209                 return i;
8210             }
8211         }
8212         
8213         return this.config.length;
8214     },
8215
8216     /**
8217      * Returns the number of columns.
8218      * @return {Number}
8219      */
8220     getColumnCount : function(visibleOnly){
8221         if(visibleOnly === true){
8222             var c = 0;
8223             for(var i = 0, len = this.config.length; i < len; i++){
8224                 if(!this.isHidden(i)){
8225                     c++;
8226                 }
8227             }
8228             return c;
8229         }
8230         return this.config.length;
8231     },
8232
8233     /**
8234      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8235      * @param {Function} fn
8236      * @param {Object} scope (optional)
8237      * @return {Array} result
8238      */
8239     getColumnsBy : function(fn, scope){
8240         var r = [];
8241         for(var i = 0, len = this.config.length; i < len; i++){
8242             var c = this.config[i];
8243             if(fn.call(scope||this, c, i) === true){
8244                 r[r.length] = c;
8245             }
8246         }
8247         return r;
8248     },
8249
8250     /**
8251      * Returns true if the specified column is sortable.
8252      * @param {Number} col The column index
8253      * @return {Boolean}
8254      */
8255     isSortable : function(col){
8256         if(typeof this.config[col].sortable == "undefined"){
8257             return this.defaultSortable;
8258         }
8259         return this.config[col].sortable;
8260     },
8261
8262     /**
8263      * Returns the rendering (formatting) function defined for the column.
8264      * @param {Number} col The column index.
8265      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8266      */
8267     getRenderer : function(col){
8268         if(!this.config[col].renderer){
8269             return Roo.grid.ColumnModel.defaultRenderer;
8270         }
8271         return this.config[col].renderer;
8272     },
8273
8274     /**
8275      * Sets the rendering (formatting) function for a column.
8276      * @param {Number} col The column index
8277      * @param {Function} fn The function to use to process the cell's raw data
8278      * to return HTML markup for the grid view. The render function is called with
8279      * the following parameters:<ul>
8280      * <li>Data value.</li>
8281      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8282      * <li>css A CSS style string to apply to the table cell.</li>
8283      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8284      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8285      * <li>Row index</li>
8286      * <li>Column index</li>
8287      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8288      */
8289     setRenderer : function(col, fn){
8290         this.config[col].renderer = fn;
8291     },
8292
8293     /**
8294      * Returns the width for the specified column.
8295      * @param {Number} col The column index
8296      * @param (optional) {String} gridSize bootstrap width size.
8297      * @return {Number}
8298      */
8299     getColumnWidth : function(col, gridSize)
8300         {
8301                 var cfg = this.config[col];
8302                 
8303                 if (typeof(gridSize) == 'undefined') {
8304                         return cfg.width * 1 || this.defaultWidth;
8305                 }
8306                 if (gridSize === false) { // if we set it..
8307                         return cfg.width || false;
8308                 }
8309                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8310                 
8311                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8312                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8313                                 continue;
8314                         }
8315                         return cfg[ sizes[i] ];
8316                 }
8317                 return 1;
8318                 
8319     },
8320
8321     /**
8322      * Sets the width for a column.
8323      * @param {Number} col The column index
8324      * @param {Number} width The new width
8325      */
8326     setColumnWidth : function(col, width, suppressEvent){
8327         this.config[col].width = width;
8328         this.totalWidth = null;
8329         if(!suppressEvent){
8330              this.fireEvent("widthchange", this, col, width);
8331         }
8332     },
8333
8334     /**
8335      * Returns the total width of all columns.
8336      * @param {Boolean} includeHidden True to include hidden column widths
8337      * @return {Number}
8338      */
8339     getTotalWidth : function(includeHidden){
8340         if(!this.totalWidth){
8341             this.totalWidth = 0;
8342             for(var i = 0, len = this.config.length; i < len; i++){
8343                 if(includeHidden || !this.isHidden(i)){
8344                     this.totalWidth += this.getColumnWidth(i);
8345                 }
8346             }
8347         }
8348         return this.totalWidth;
8349     },
8350
8351     /**
8352      * Returns the header for the specified column.
8353      * @param {Number} col The column index
8354      * @return {String}
8355      */
8356     getColumnHeader : function(col){
8357         return this.config[col].header;
8358     },
8359
8360     /**
8361      * Sets the header for a column.
8362      * @param {Number} col The column index
8363      * @param {String} header The new header
8364      */
8365     setColumnHeader : function(col, header){
8366         this.config[col].header = header;
8367         this.fireEvent("headerchange", this, col, header);
8368     },
8369
8370     /**
8371      * Returns the tooltip for the specified column.
8372      * @param {Number} col The column index
8373      * @return {String}
8374      */
8375     getColumnTooltip : function(col){
8376             return this.config[col].tooltip;
8377     },
8378     /**
8379      * Sets the tooltip for a column.
8380      * @param {Number} col The column index
8381      * @param {String} tooltip The new tooltip
8382      */
8383     setColumnTooltip : function(col, tooltip){
8384             this.config[col].tooltip = tooltip;
8385     },
8386
8387     /**
8388      * Returns the dataIndex for the specified column.
8389      * @param {Number} col The column index
8390      * @return {Number}
8391      */
8392     getDataIndex : function(col){
8393         return this.config[col].dataIndex;
8394     },
8395
8396     /**
8397      * Sets the dataIndex for a column.
8398      * @param {Number} col The column index
8399      * @param {Number} dataIndex The new dataIndex
8400      */
8401     setDataIndex : function(col, dataIndex){
8402         this.config[col].dataIndex = dataIndex;
8403     },
8404
8405     
8406     
8407     /**
8408      * Returns true if the cell is editable.
8409      * @param {Number} colIndex The column index
8410      * @param {Number} rowIndex The row index - this is nto actually used..?
8411      * @return {Boolean}
8412      */
8413     isCellEditable : function(colIndex, rowIndex){
8414         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8415     },
8416
8417     /**
8418      * Returns the editor defined for the cell/column.
8419      * return false or null to disable editing.
8420      * @param {Number} colIndex The column index
8421      * @param {Number} rowIndex The row index
8422      * @return {Object}
8423      */
8424     getCellEditor : function(colIndex, rowIndex){
8425         return this.config[colIndex].editor;
8426     },
8427
8428     /**
8429      * Sets if a column is editable.
8430      * @param {Number} col The column index
8431      * @param {Boolean} editable True if the column is editable
8432      */
8433     setEditable : function(col, editable){
8434         this.config[col].editable = editable;
8435     },
8436
8437
8438     /**
8439      * Returns true if the column is hidden.
8440      * @param {Number} colIndex The column index
8441      * @return {Boolean}
8442      */
8443     isHidden : function(colIndex){
8444         return this.config[colIndex].hidden;
8445     },
8446
8447
8448     /**
8449      * Returns true if the column width cannot be changed
8450      */
8451     isFixed : function(colIndex){
8452         return this.config[colIndex].fixed;
8453     },
8454
8455     /**
8456      * Returns true if the column can be resized
8457      * @return {Boolean}
8458      */
8459     isResizable : function(colIndex){
8460         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8461     },
8462     /**
8463      * Sets if a column is hidden.
8464      * @param {Number} colIndex The column index
8465      * @param {Boolean} hidden True if the column is hidden
8466      */
8467     setHidden : function(colIndex, hidden){
8468         this.config[colIndex].hidden = hidden;
8469         this.totalWidth = null;
8470         this.fireEvent("hiddenchange", this, colIndex, hidden);
8471     },
8472
8473     /**
8474      * Sets the editor for a column.
8475      * @param {Number} col The column index
8476      * @param {Object} editor The editor object
8477      */
8478     setEditor : function(col, editor){
8479         this.config[col].editor = editor;
8480     },
8481     /**
8482      * Add a column (experimental...) - defaults to adding to the end..
8483      * @param {Object} config 
8484     */
8485     addColumn : function(c)
8486     {
8487     
8488         var i = this.config.length;
8489         this.config[i] = c;
8490         
8491         if(typeof c.dataIndex == "undefined"){
8492             c.dataIndex = i;
8493         }
8494         if(typeof c.renderer == "string"){
8495             c.renderer = Roo.util.Format[c.renderer];
8496         }
8497         if(typeof c.id == "undefined"){
8498             c.id = Roo.id();
8499         }
8500         if(c.editor && c.editor.xtype){
8501             c.editor  = Roo.factory(c.editor, Roo.grid);
8502         }
8503         if(c.editor && c.editor.isFormField){
8504             c.editor = new Roo.grid.GridEditor(c.editor);
8505         }
8506         this.lookup[c.id] = c;
8507     }
8508     
8509 });
8510
8511 Roo.grid.ColumnModel.defaultRenderer = function(value)
8512 {
8513     if(typeof value == "object") {
8514         return value;
8515     }
8516         if(typeof value == "string" && value.length < 1){
8517             return "&#160;";
8518         }
8519     
8520         return String.format("{0}", value);
8521 };
8522
8523 // Alias for backwards compatibility
8524 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8525 /*
8526  * Based on:
8527  * Ext JS Library 1.1.1
8528  * Copyright(c) 2006-2007, Ext JS, LLC.
8529  *
8530  * Originally Released Under LGPL - original licence link has changed is not relivant.
8531  *
8532  * Fork - LGPL
8533  * <script type="text/javascript">
8534  */
8535  
8536 /**
8537  * @class Roo.LoadMask
8538  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8539  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8540  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8541  * element's UpdateManager load indicator and will be destroyed after the initial load.
8542  * @constructor
8543  * Create a new LoadMask
8544  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8545  * @param {Object} config The config object
8546  */
8547 Roo.LoadMask = function(el, config){
8548     this.el = Roo.get(el);
8549     Roo.apply(this, config);
8550     if(this.store){
8551         this.store.on('beforeload', this.onBeforeLoad, this);
8552         this.store.on('load', this.onLoad, this);
8553         this.store.on('loadexception', this.onLoadException, this);
8554         this.removeMask = false;
8555     }else{
8556         var um = this.el.getUpdateManager();
8557         um.showLoadIndicator = false; // disable the default indicator
8558         um.on('beforeupdate', this.onBeforeLoad, this);
8559         um.on('update', this.onLoad, this);
8560         um.on('failure', this.onLoad, this);
8561         this.removeMask = true;
8562     }
8563 };
8564
8565 Roo.LoadMask.prototype = {
8566     /**
8567      * @cfg {Boolean} removeMask
8568      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8569      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8570      */
8571     removeMask : false,
8572     /**
8573      * @cfg {String} msg
8574      * The text to display in a centered loading message box (defaults to 'Loading...')
8575      */
8576     msg : 'Loading...',
8577     /**
8578      * @cfg {String} msgCls
8579      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8580      */
8581     msgCls : 'x-mask-loading',
8582
8583     /**
8584      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8585      * @type Boolean
8586      */
8587     disabled: false,
8588
8589     /**
8590      * Disables the mask to prevent it from being displayed
8591      */
8592     disable : function(){
8593        this.disabled = true;
8594     },
8595
8596     /**
8597      * Enables the mask so that it can be displayed
8598      */
8599     enable : function(){
8600         this.disabled = false;
8601     },
8602     
8603     onLoadException : function()
8604     {
8605         Roo.log(arguments);
8606         
8607         if (typeof(arguments[3]) != 'undefined') {
8608             Roo.MessageBox.alert("Error loading",arguments[3]);
8609         } 
8610         /*
8611         try {
8612             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8613                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8614             }   
8615         } catch(e) {
8616             
8617         }
8618         */
8619     
8620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8621     },
8622     // private
8623     onLoad : function()
8624     {
8625         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8626     },
8627
8628     // private
8629     onBeforeLoad : function(){
8630         if(!this.disabled){
8631             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8632         }
8633     },
8634
8635     // private
8636     destroy : function(){
8637         if(this.store){
8638             this.store.un('beforeload', this.onBeforeLoad, this);
8639             this.store.un('load', this.onLoad, this);
8640             this.store.un('loadexception', this.onLoadException, this);
8641         }else{
8642             var um = this.el.getUpdateManager();
8643             um.un('beforeupdate', this.onBeforeLoad, this);
8644             um.un('update', this.onLoad, this);
8645             um.un('failure', this.onLoad, this);
8646         }
8647     }
8648 };/**
8649  * @class Roo.bootstrap.Table
8650  * @licence LGBL
8651  * @extends Roo.bootstrap.Component
8652  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8653  * Similar to Roo.grid.Grid
8654  * <pre><code>
8655  var table = Roo.factory({
8656     xtype : 'Table',
8657     xns : Roo.bootstrap,
8658     autoSizeColumns: true,
8659     
8660     
8661     store : {
8662         xtype : 'Store',
8663         xns : Roo.data,
8664         remoteSort : true,
8665         sortInfo : { direction : 'ASC', field: 'name' },
8666         proxy : {
8667            xtype : 'HttpProxy',
8668            xns : Roo.data,
8669            method : 'GET',
8670            url : 'https://example.com/some.data.url.json'
8671         },
8672         reader : {
8673            xtype : 'JsonReader',
8674            xns : Roo.data,
8675            fields : [ 'id', 'name', whatever' ],
8676            id : 'id',
8677            root : 'data'
8678         }
8679     },
8680     cm : [
8681         {
8682             xtype : 'ColumnModel',
8683             xns : Roo.grid,
8684             align : 'center',
8685             cursor : 'pointer',
8686             dataIndex : 'is_in_group',
8687             header : "Name",
8688             sortable : true,
8689             renderer : function(v, x , r) {  
8690             
8691                 return String.format("{0}", v)
8692             }
8693             width : 3
8694         } // more columns..
8695     ],
8696     selModel : {
8697         xtype : 'RowSelectionModel',
8698         xns : Roo.bootstrap.Table
8699         // you can add listeners to catch selection change here....
8700     }
8701      
8702
8703  });
8704  // set any options
8705  grid.render(Roo.get("some-div"));
8706 </code></pre>
8707
8708 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8709
8710
8711
8712  *
8713  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8714  * @cfg {Roo.data.Store} store The data store to use
8715  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8716  * 
8717  * @cfg {String} cls table class
8718  *
8719  * 
8720  * @cfg {boolean} striped Should the rows be alternative striped
8721  * @cfg {boolean} bordered Add borders to the table
8722  * @cfg {boolean} hover Add hover highlighting
8723  * @cfg {boolean} condensed Format condensed
8724  * @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,
8725  *                also adds table-responsive (see bootstrap docs for details)
8726  * @cfg {Boolean} loadMask (true|false) default false
8727  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8728  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8729  * @cfg {Boolean} rowSelection (true|false) default false
8730  * @cfg {Boolean} cellSelection (true|false) default false
8731  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8732  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8733  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8734  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8735  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8736  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8737  * 
8738  * @constructor
8739  * Create a new Table
8740  * @param {Object} config The config object
8741  */
8742
8743 Roo.bootstrap.Table = function(config)
8744 {
8745     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8746      
8747     // BC...
8748     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8749     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8750     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8751     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8752     
8753     this.view = this; // compat with grid.
8754     
8755     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8756     if (this.sm) {
8757         this.sm.grid = this;
8758         this.selModel = Roo.factory(this.sm, Roo.grid);
8759         this.sm = this.selModel;
8760         this.sm.xmodule = this.xmodule || false;
8761     }
8762     
8763     if (this.cm && typeof(this.cm.config) == 'undefined') {
8764         this.colModel = new Roo.grid.ColumnModel(this.cm);
8765         this.cm = this.colModel;
8766         this.cm.xmodule = this.xmodule || false;
8767     }
8768     if (this.store) {
8769         this.store= Roo.factory(this.store, Roo.data);
8770         this.ds = this.store;
8771         this.ds.xmodule = this.xmodule || false;
8772          
8773     }
8774     if (this.footer && this.store) {
8775         this.footer.dataSource = this.ds;
8776         this.footer = Roo.factory(this.footer);
8777     }
8778     
8779     /** @private */
8780     this.addEvents({
8781         /**
8782          * @event cellclick
8783          * Fires when a cell is clicked
8784          * @param {Roo.bootstrap.Table} this
8785          * @param {Roo.Element} el
8786          * @param {Number} rowIndex
8787          * @param {Number} columnIndex
8788          * @param {Roo.EventObject} e
8789          */
8790         "cellclick" : true,
8791         /**
8792          * @event celldblclick
8793          * Fires when a cell is double clicked
8794          * @param {Roo.bootstrap.Table} this
8795          * @param {Roo.Element} el
8796          * @param {Number} rowIndex
8797          * @param {Number} columnIndex
8798          * @param {Roo.EventObject} e
8799          */
8800         "celldblclick" : true,
8801         /**
8802          * @event rowclick
8803          * Fires when a row is clicked
8804          * @param {Roo.bootstrap.Table} this
8805          * @param {Roo.Element} el
8806          * @param {Number} rowIndex
8807          * @param {Roo.EventObject} e
8808          */
8809         "rowclick" : true,
8810         /**
8811          * @event rowdblclick
8812          * Fires when a row is double clicked
8813          * @param {Roo.bootstrap.Table} this
8814          * @param {Roo.Element} el
8815          * @param {Number} rowIndex
8816          * @param {Roo.EventObject} e
8817          */
8818         "rowdblclick" : true,
8819         /**
8820          * @event mouseover
8821          * Fires when a mouseover occur
8822          * @param {Roo.bootstrap.Table} this
8823          * @param {Roo.Element} el
8824          * @param {Number} rowIndex
8825          * @param {Number} columnIndex
8826          * @param {Roo.EventObject} e
8827          */
8828         "mouseover" : true,
8829         /**
8830          * @event mouseout
8831          * Fires when a mouseout occur
8832          * @param {Roo.bootstrap.Table} this
8833          * @param {Roo.Element} el
8834          * @param {Number} rowIndex
8835          * @param {Number} columnIndex
8836          * @param {Roo.EventObject} e
8837          */
8838         "mouseout" : true,
8839         /**
8840          * @event rowclass
8841          * Fires when a row is rendered, so you can change add a style to it.
8842          * @param {Roo.bootstrap.Table} this
8843          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8844          */
8845         'rowclass' : true,
8846           /**
8847          * @event rowsrendered
8848          * Fires when all the  rows have been rendered
8849          * @param {Roo.bootstrap.Table} this
8850          */
8851         'rowsrendered' : true,
8852         /**
8853          * @event contextmenu
8854          * The raw contextmenu event for the entire grid.
8855          * @param {Roo.EventObject} e
8856          */
8857         "contextmenu" : true,
8858         /**
8859          * @event rowcontextmenu
8860          * Fires when a row is right clicked
8861          * @param {Roo.bootstrap.Table} this
8862          * @param {Number} rowIndex
8863          * @param {Roo.EventObject} e
8864          */
8865         "rowcontextmenu" : true,
8866         /**
8867          * @event cellcontextmenu
8868          * Fires when a cell is right clicked
8869          * @param {Roo.bootstrap.Table} this
8870          * @param {Number} rowIndex
8871          * @param {Number} cellIndex
8872          * @param {Roo.EventObject} e
8873          */
8874          "cellcontextmenu" : true,
8875          /**
8876          * @event headercontextmenu
8877          * Fires when a header is right clicked
8878          * @param {Roo.bootstrap.Table} this
8879          * @param {Number} columnIndex
8880          * @param {Roo.EventObject} e
8881          */
8882         "headercontextmenu" : true,
8883         /**
8884          * @event mousedown
8885          * The raw mousedown event for the entire grid.
8886          * @param {Roo.EventObject} e
8887          */
8888         "mousedown" : true
8889         
8890     });
8891 };
8892
8893 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8894     
8895     cls: false,
8896     
8897     striped : false,
8898     scrollBody : false,
8899     bordered: false,
8900     hover:  false,
8901     condensed : false,
8902     responsive : false,
8903     sm : false,
8904     cm : false,
8905     store : false,
8906     loadMask : false,
8907     footerShow : true,
8908     headerShow : true,
8909     enableColumnResize: true,
8910   
8911     rowSelection : false,
8912     cellSelection : false,
8913     layout : false,
8914
8915     minColumnWidth : 50,
8916     
8917     // Roo.Element - the tbody
8918     bodyEl: false,  // <tbody> Roo.Element - thead element    
8919     headEl: false,  // <thead> Roo.Element - thead element
8920     resizeProxy : false, // proxy element for dragging?
8921
8922
8923     
8924     container: false, // used by gridpanel...
8925     
8926     lazyLoad : false,
8927     
8928     CSS : Roo.util.CSS,
8929     
8930     auto_hide_footer : false,
8931     
8932     view: false, // actually points to this..
8933     
8934     getAutoCreate : function()
8935     {
8936         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8937         
8938         cfg = {
8939             tag: 'table',
8940             cls : 'table', 
8941             cn : []
8942         };
8943         // this get's auto added by panel.Grid
8944         if (this.scrollBody) {
8945             cfg.cls += ' table-body-fixed';
8946         }    
8947         if (this.striped) {
8948             cfg.cls += ' table-striped';
8949         }
8950         
8951         if (this.hover) {
8952             cfg.cls += ' table-hover';
8953         }
8954         if (this.bordered) {
8955             cfg.cls += ' table-bordered';
8956         }
8957         if (this.condensed) {
8958             cfg.cls += ' table-condensed';
8959         }
8960         
8961         if (this.responsive) {
8962             cfg.cls += ' table-responsive';
8963         }
8964         
8965         if (this.cls) {
8966             cfg.cls+=  ' ' +this.cls;
8967         }
8968         
8969         
8970         
8971         if (this.layout) {
8972             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8973         }
8974         
8975         if(this.store || this.cm){
8976             if(this.headerShow){
8977                 cfg.cn.push(this.renderHeader());
8978             }
8979             
8980             cfg.cn.push(this.renderBody());
8981             
8982             if(this.footerShow){
8983                 cfg.cn.push(this.renderFooter());
8984             }
8985             // where does this come from?
8986             //cfg.cls+=  ' TableGrid';
8987         }
8988         
8989         return { cn : [ cfg ] };
8990     },
8991     
8992     initEvents : function()
8993     {   
8994         if(!this.store || !this.cm){
8995             return;
8996         }
8997         if (this.selModel) {
8998             this.selModel.initEvents();
8999         }
9000         
9001         
9002         //Roo.log('initEvents with ds!!!!');
9003         
9004         this.bodyEl = this.el.select('tbody', true).first();
9005         this.headEl = this.el.select('thead', true).first();
9006         this.mainFoot = this.el.select('tfoot', true).first();
9007         
9008         
9009         
9010         
9011         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9012             e.on('click', this.sort, this);
9013         }, this);
9014         
9015         
9016         // why is this done????? = it breaks dialogs??
9017         //this.parent().el.setStyle('position', 'relative');
9018         
9019         
9020         if (this.footer) {
9021             this.footer.parentId = this.id;
9022             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9023             
9024             if(this.lazyLoad){
9025                 this.el.select('tfoot tr td').first().addClass('hide');
9026             }
9027         } 
9028         
9029         if(this.loadMask) {
9030             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9031         }
9032         
9033         this.store.on('load', this.onLoad, this);
9034         this.store.on('beforeload', this.onBeforeLoad, this);
9035         this.store.on('update', this.onUpdate, this);
9036         this.store.on('add', this.onAdd, this);
9037         this.store.on("clear", this.clear, this);
9038         
9039         this.el.on("contextmenu", this.onContextMenu, this);
9040         
9041         
9042         this.cm.on("headerchange", this.onHeaderChange, this);
9043         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9044
9045  //?? does bodyEl get replaced on render?
9046         this.bodyEl.on("click", this.onClick, this);
9047         this.bodyEl.on("dblclick", this.onDblClick, this);        
9048         this.bodyEl.on('scroll', this.onBodyScroll, this);
9049
9050         // guessing mainbody will work - this relays usually caught by selmodel at present.
9051         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9052   
9053   
9054         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9055         
9056   
9057         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9058             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9059         }
9060         
9061         this.initCSS();
9062     },
9063     // Compatibility with grid - we implement all the view features at present.
9064     getView : function()
9065     {
9066         return this;
9067     },
9068     
9069     initCSS : function()
9070     {
9071         
9072         
9073         var cm = this.cm, styles = [];
9074         this.CSS.removeStyleSheet(this.id + '-cssrules');
9075         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9076         // we can honour xs/sm/md/xl  as widths...
9077         // we first have to decide what widht we are currently at...
9078         var sz = Roo.getGridSize();
9079         
9080         var total = 0;
9081         var last = -1;
9082         var cols = []; // visable cols.
9083         var total_abs = 0;
9084         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9085             var w = cm.getColumnWidth(i, false);
9086             if(cm.isHidden(i)){
9087                 cols.push( { rel : false, abs : 0 });
9088                 continue;
9089             }
9090             if (w !== false) {
9091                 cols.push( { rel : false, abs : w });
9092                 total_abs += w;
9093                 last = i; // not really..
9094                 continue;
9095             }
9096             var w = cm.getColumnWidth(i, sz);
9097             if (w > 0) {
9098                 last = i
9099             }
9100             total += w;
9101             cols.push( { rel : w, abs : false });
9102         }
9103         
9104         var avail = this.bodyEl.dom.clientWidth - total_abs;
9105         
9106         var unitWidth = Math.floor(avail / total);
9107         var rem = avail - (unitWidth * total);
9108         
9109         var hidden, width, pos = 0 , splithide , left;
9110         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9111             
9112             hidden = 'display:none;';
9113             left = '';
9114             width  = 'width:0px;';
9115             splithide = '';
9116             if(!cm.isHidden(i)){
9117                 hidden = '';
9118                 
9119                 
9120                 // we can honour xs/sm/md/xl ?
9121                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9122                 if (w===0) {
9123                     hidden = 'display:none;';
9124                 }
9125                 // width should return a small number...
9126                 if (i == last) {
9127                     w+=rem; // add the remaining with..
9128                 }
9129                 pos += w;
9130                 left = "left:" + (pos -4) + "px;";
9131                 width = "width:" + w+ "px;";
9132                 
9133             }
9134             if (this.responsive) {
9135                 width = '';
9136                 left = '';
9137                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9138                 splithide = 'display: none;';
9139             }
9140             
9141             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9142             if (this.headEl) {
9143                 if (i == last) {
9144                     splithide = 'display:none;';
9145                 }
9146                 
9147                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9148                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9149                 );
9150             }
9151             
9152         }
9153         //Roo.log(styles.join(''));
9154         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9155         
9156     },
9157     
9158     
9159     
9160     onContextMenu : function(e, t)
9161     {
9162         this.processEvent("contextmenu", e);
9163     },
9164     
9165     processEvent : function(name, e)
9166     {
9167         if (name != 'touchstart' ) {
9168             this.fireEvent(name, e);    
9169         }
9170         
9171         var t = e.getTarget();
9172         
9173         var cell = Roo.get(t);
9174         
9175         if(!cell){
9176             return;
9177         }
9178         
9179         if(cell.findParent('tfoot', false, true)){
9180             return;
9181         }
9182         
9183         if(cell.findParent('thead', false, true)){
9184             
9185             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9186                 cell = Roo.get(t).findParent('th', false, true);
9187                 if (!cell) {
9188                     Roo.log("failed to find th in thead?");
9189                     Roo.log(e.getTarget());
9190                     return;
9191                 }
9192             }
9193             
9194             var cellIndex = cell.dom.cellIndex;
9195             
9196             var ename = name == 'touchstart' ? 'click' : name;
9197             this.fireEvent("header" + ename, this, cellIndex, e);
9198             
9199             return;
9200         }
9201         
9202         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9203             cell = Roo.get(t).findParent('td', false, true);
9204             if (!cell) {
9205                 Roo.log("failed to find th in tbody?");
9206                 Roo.log(e.getTarget());
9207                 return;
9208             }
9209         }
9210         
9211         var row = cell.findParent('tr', false, true);
9212         var cellIndex = cell.dom.cellIndex;
9213         var rowIndex = row.dom.rowIndex - 1;
9214         
9215         if(row !== false){
9216             
9217             this.fireEvent("row" + name, this, rowIndex, e);
9218             
9219             if(cell !== false){
9220             
9221                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9222             }
9223         }
9224         
9225     },
9226     
9227     onMouseover : function(e, el)
9228     {
9229         var cell = Roo.get(el);
9230         
9231         if(!cell){
9232             return;
9233         }
9234         
9235         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9236             cell = cell.findParent('td', false, true);
9237         }
9238         
9239         var row = cell.findParent('tr', false, true);
9240         var cellIndex = cell.dom.cellIndex;
9241         var rowIndex = row.dom.rowIndex - 1; // start from 0
9242         
9243         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9244         
9245     },
9246     
9247     onMouseout : function(e, el)
9248     {
9249         var cell = Roo.get(el);
9250         
9251         if(!cell){
9252             return;
9253         }
9254         
9255         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9256             cell = cell.findParent('td', false, true);
9257         }
9258         
9259         var row = cell.findParent('tr', false, true);
9260         var cellIndex = cell.dom.cellIndex;
9261         var rowIndex = row.dom.rowIndex - 1; // start from 0
9262         
9263         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9264         
9265     },
9266     
9267     onClick : function(e, el)
9268     {
9269         var cell = Roo.get(el);
9270         
9271         if(!cell || (!this.cellSelection && !this.rowSelection)){
9272             return;
9273         }
9274         
9275         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9276             cell = cell.findParent('td', false, true);
9277         }
9278         
9279         if(!cell || typeof(cell) == 'undefined'){
9280             return;
9281         }
9282         
9283         var row = cell.findParent('tr', false, true);
9284         
9285         if(!row || typeof(row) == 'undefined'){
9286             return;
9287         }
9288         
9289         var cellIndex = cell.dom.cellIndex;
9290         var rowIndex = this.getRowIndex(row);
9291         
9292         // why??? - should these not be based on SelectionModel?
9293         //if(this.cellSelection){
9294             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9295         //}
9296         
9297         //if(this.rowSelection){
9298             this.fireEvent('rowclick', this, row, rowIndex, e);
9299         //}
9300          
9301     },
9302         
9303     onDblClick : function(e,el)
9304     {
9305         var cell = Roo.get(el);
9306         
9307         if(!cell || (!this.cellSelection && !this.rowSelection)){
9308             return;
9309         }
9310         
9311         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9312             cell = cell.findParent('td', false, true);
9313         }
9314         
9315         if(!cell || typeof(cell) == 'undefined'){
9316             return;
9317         }
9318         
9319         var row = cell.findParent('tr', false, true);
9320         
9321         if(!row || typeof(row) == 'undefined'){
9322             return;
9323         }
9324         
9325         var cellIndex = cell.dom.cellIndex;
9326         var rowIndex = this.getRowIndex(row);
9327         
9328         if(this.cellSelection){
9329             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9330         }
9331         
9332         if(this.rowSelection){
9333             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9334         }
9335     },
9336     findRowIndex : function(el)
9337     {
9338         var cell = Roo.get(el);
9339         if(!cell) {
9340             return false;
9341         }
9342         var row = cell.findParent('tr', false, true);
9343         
9344         if(!row || typeof(row) == 'undefined'){
9345             return false;
9346         }
9347         return this.getRowIndex(row);
9348     },
9349     sort : function(e,el)
9350     {
9351         var col = Roo.get(el);
9352         
9353         if(!col.hasClass('sortable')){
9354             return;
9355         }
9356         
9357         var sort = col.attr('sort');
9358         var dir = 'ASC';
9359         
9360         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9361             dir = 'DESC';
9362         }
9363         
9364         this.store.sortInfo = {field : sort, direction : dir};
9365         
9366         if (this.footer) {
9367             Roo.log("calling footer first");
9368             this.footer.onClick('first');
9369         } else {
9370         
9371             this.store.load({ params : { start : 0 } });
9372         }
9373     },
9374     
9375     renderHeader : function()
9376     {
9377         var header = {
9378             tag: 'thead',
9379             cn : []
9380         };
9381         
9382         var cm = this.cm;
9383         this.totalWidth = 0;
9384         
9385         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9386             
9387             var config = cm.config[i];
9388             
9389             var c = {
9390                 tag: 'th',
9391                 cls : 'x-hcol-' + i,
9392                 style : '',
9393                 
9394                 html: cm.getColumnHeader(i)
9395             };
9396             
9397             var tooltip = cm.getColumnTooltip(i);
9398             if (tooltip) {
9399                 c.tooltip = tooltip;
9400             }
9401             
9402             
9403             var hh = '';
9404             
9405             if(typeof(config.sortable) != 'undefined' && config.sortable){
9406                 c.cls += ' sortable';
9407                 c.html = '<i class="fa"></i>' + c.html;
9408             }
9409             
9410             // could use BS4 hidden-..-down 
9411             
9412             if(typeof(config.lgHeader) != 'undefined'){
9413                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9414             }
9415             
9416             if(typeof(config.mdHeader) != 'undefined'){
9417                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9418             }
9419             
9420             if(typeof(config.smHeader) != 'undefined'){
9421                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9422             }
9423             
9424             if(typeof(config.xsHeader) != 'undefined'){
9425                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9426             }
9427             
9428             if(hh.length){
9429                 c.html = hh;
9430             }
9431             
9432             if(typeof(config.tooltip) != 'undefined'){
9433                 c.tooltip = config.tooltip;
9434             }
9435             
9436             if(typeof(config.colspan) != 'undefined'){
9437                 c.colspan = config.colspan;
9438             }
9439             
9440             // hidden is handled by CSS now
9441             
9442             if(typeof(config.dataIndex) != 'undefined'){
9443                 c.sort = config.dataIndex;
9444             }
9445             
9446            
9447             
9448             if(typeof(config.align) != 'undefined' && config.align.length){
9449                 c.style += ' text-align:' + config.align + ';';
9450             }
9451             
9452             /* width is done in CSS
9453              *if(typeof(config.width) != 'undefined'){
9454                 c.style += ' width:' + config.width + 'px;';
9455                 this.totalWidth += config.width;
9456             } else {
9457                 this.totalWidth += 100; // assume minimum of 100 per column?
9458             }
9459             */
9460             
9461             if(typeof(config.cls) != 'undefined'){
9462                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9463             }
9464             // this is the bit that doesnt reall work at all...
9465             
9466             if (this.responsive) {
9467                  
9468             
9469                 ['xs','sm','md','lg'].map(function(size){
9470                     
9471                     if(typeof(config[size]) == 'undefined'){
9472                         return;
9473                     }
9474                      
9475                     if (!config[size]) { // 0 = hidden
9476                         // BS 4 '0' is treated as hide that column and below.
9477                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9478                         return;
9479                     }
9480                     
9481                     c.cls += ' col-' + size + '-' + config[size] + (
9482                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9483                     );
9484                     
9485                     
9486                 });
9487             }
9488             // at the end?
9489             
9490             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9491             
9492             
9493             
9494             
9495             header.cn.push(c)
9496         }
9497         
9498         return header;
9499     },
9500     
9501     renderBody : function()
9502     {
9503         var body = {
9504             tag: 'tbody',
9505             cn : [
9506                 {
9507                     tag: 'tr',
9508                     cn : [
9509                         {
9510                             tag : 'td',
9511                             colspan :  this.cm.getColumnCount()
9512                         }
9513                     ]
9514                 }
9515             ]
9516         };
9517         
9518         return body;
9519     },
9520     
9521     renderFooter : function()
9522     {
9523         var footer = {
9524             tag: 'tfoot',
9525             cn : [
9526                 {
9527                     tag: 'tr',
9528                     cn : [
9529                         {
9530                             tag : 'td',
9531                             colspan :  this.cm.getColumnCount()
9532                         }
9533                     ]
9534                 }
9535             ]
9536         };
9537         
9538         return footer;
9539     },
9540     
9541     
9542     
9543     onLoad : function()
9544     {
9545 //        Roo.log('ds onload');
9546         this.clear();
9547         
9548         var _this = this;
9549         var cm = this.cm;
9550         var ds = this.store;
9551         
9552         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9553             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9554             if (_this.store.sortInfo) {
9555                     
9556                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9557                     e.select('i', true).addClass(['fa-arrow-up']);
9558                 }
9559                 
9560                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9561                     e.select('i', true).addClass(['fa-arrow-down']);
9562                 }
9563             }
9564         });
9565         
9566         var tbody =  this.bodyEl;
9567               
9568         if(ds.getCount() > 0){
9569             ds.data.each(function(d,rowIndex){
9570                 var row =  this.renderRow(cm, ds, rowIndex);
9571                 
9572                 tbody.createChild(row);
9573                 
9574                 var _this = this;
9575                 
9576                 if(row.cellObjects.length){
9577                     Roo.each(row.cellObjects, function(r){
9578                         _this.renderCellObject(r);
9579                     })
9580                 }
9581                 
9582             }, this);
9583         }
9584         
9585         var tfoot = this.el.select('tfoot', true).first();
9586         
9587         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9588             
9589             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9590             
9591             var total = this.ds.getTotalCount();
9592             
9593             if(this.footer.pageSize < total){
9594                 this.mainFoot.show();
9595             }
9596         }
9597         
9598         Roo.each(this.el.select('tbody td', true).elements, function(e){
9599             e.on('mouseover', _this.onMouseover, _this);
9600         });
9601         
9602         Roo.each(this.el.select('tbody td', true).elements, function(e){
9603             e.on('mouseout', _this.onMouseout, _this);
9604         });
9605         this.fireEvent('rowsrendered', this);
9606         
9607         this.autoSize();
9608         
9609         this.initCSS(); /// resize cols
9610
9611         
9612     },
9613     
9614     
9615     onUpdate : function(ds,record)
9616     {
9617         this.refreshRow(record);
9618         this.autoSize();
9619     },
9620     
9621     onRemove : function(ds, record, index, isUpdate){
9622         if(isUpdate !== true){
9623             this.fireEvent("beforerowremoved", this, index, record);
9624         }
9625         var bt = this.bodyEl.dom;
9626         
9627         var rows = this.el.select('tbody > tr', true).elements;
9628         
9629         if(typeof(rows[index]) != 'undefined'){
9630             bt.removeChild(rows[index].dom);
9631         }
9632         
9633 //        if(bt.rows[index]){
9634 //            bt.removeChild(bt.rows[index]);
9635 //        }
9636         
9637         if(isUpdate !== true){
9638             //this.stripeRows(index);
9639             //this.syncRowHeights(index, index);
9640             //this.layout();
9641             this.fireEvent("rowremoved", this, index, record);
9642         }
9643     },
9644     
9645     onAdd : function(ds, records, rowIndex)
9646     {
9647         //Roo.log('on Add called');
9648         // - note this does not handle multiple adding very well..
9649         var bt = this.bodyEl.dom;
9650         for (var i =0 ; i < records.length;i++) {
9651             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9652             //Roo.log(records[i]);
9653             //Roo.log(this.store.getAt(rowIndex+i));
9654             this.insertRow(this.store, rowIndex + i, false);
9655             return;
9656         }
9657         
9658     },
9659     
9660     
9661     refreshRow : function(record){
9662         var ds = this.store, index;
9663         if(typeof record == 'number'){
9664             index = record;
9665             record = ds.getAt(index);
9666         }else{
9667             index = ds.indexOf(record);
9668             if (index < 0) {
9669                 return; // should not happen - but seems to 
9670             }
9671         }
9672         this.insertRow(ds, index, true);
9673         this.autoSize();
9674         this.onRemove(ds, record, index+1, true);
9675         this.autoSize();
9676         //this.syncRowHeights(index, index);
9677         //this.layout();
9678         this.fireEvent("rowupdated", this, index, record);
9679     },
9680     // private - called by RowSelection
9681     onRowSelect : function(rowIndex){
9682         var row = this.getRowDom(rowIndex);
9683         row.addClass(['bg-info','info']);
9684     },
9685     // private - called by RowSelection
9686     onRowDeselect : function(rowIndex)
9687     {
9688         if (rowIndex < 0) {
9689             return;
9690         }
9691         var row = this.getRowDom(rowIndex);
9692         row.removeClass(['bg-info','info']);
9693     },
9694       /**
9695      * Focuses the specified row.
9696      * @param {Number} row The row index
9697      */
9698     focusRow : function(row)
9699     {
9700         //Roo.log('GridView.focusRow');
9701         var x = this.bodyEl.dom.scrollLeft;
9702         this.focusCell(row, 0, false);
9703         this.bodyEl.dom.scrollLeft = x;
9704
9705     },
9706      /**
9707      * Focuses the specified cell.
9708      * @param {Number} row The row index
9709      * @param {Number} col The column index
9710      * @param {Boolean} hscroll false to disable horizontal scrolling
9711      */
9712     focusCell : function(row, col, hscroll)
9713     {
9714         //Roo.log('GridView.focusCell');
9715         var el = this.ensureVisible(row, col, hscroll);
9716         // not sure what focusEL achives = it's a <a> pos relative 
9717         //this.focusEl.alignTo(el, "tl-tl");
9718         //if(Roo.isGecko){
9719         //    this.focusEl.focus();
9720         //}else{
9721         //    this.focusEl.focus.defer(1, this.focusEl);
9722         //}
9723     },
9724     
9725      /**
9726      * Scrolls the specified cell into view
9727      * @param {Number} row The row index
9728      * @param {Number} col The column index
9729      * @param {Boolean} hscroll false to disable horizontal scrolling
9730      */
9731     ensureVisible : function(row, col, hscroll)
9732     {
9733         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9734         //return null; //disable for testing.
9735         if(typeof row != "number"){
9736             row = row.rowIndex;
9737         }
9738         if(row < 0 && row >= this.ds.getCount()){
9739             return  null;
9740         }
9741         col = (col !== undefined ? col : 0);
9742         var cm = this.cm;
9743         while(cm.isHidden(col)){
9744             col++;
9745         }
9746
9747         var el = this.getCellDom(row, col);
9748         if(!el){
9749             return null;
9750         }
9751         var c = this.bodyEl.dom;
9752
9753         var ctop = parseInt(el.offsetTop, 10);
9754         var cleft = parseInt(el.offsetLeft, 10);
9755         var cbot = ctop + el.offsetHeight;
9756         var cright = cleft + el.offsetWidth;
9757
9758         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9759         var ch = 0; //?? header is not withing the area?
9760         var stop = parseInt(c.scrollTop, 10);
9761         var sleft = parseInt(c.scrollLeft, 10);
9762         var sbot = stop + ch;
9763         var sright = sleft + c.clientWidth;
9764         /*
9765         Roo.log('GridView.ensureVisible:' +
9766                 ' ctop:' + ctop +
9767                 ' c.clientHeight:' + c.clientHeight +
9768                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9769                 ' stop:' + stop +
9770                 ' cbot:' + cbot +
9771                 ' sbot:' + sbot +
9772                 ' ch:' + ch  
9773                 );
9774         */
9775         if(ctop < stop){
9776             c.scrollTop = ctop;
9777             //Roo.log("set scrolltop to ctop DISABLE?");
9778         }else if(cbot > sbot){
9779             //Roo.log("set scrolltop to cbot-ch");
9780             c.scrollTop = cbot-ch;
9781         }
9782
9783         if(hscroll !== false){
9784             if(cleft < sleft){
9785                 c.scrollLeft = cleft;
9786             }else if(cright > sright){
9787                 c.scrollLeft = cright-c.clientWidth;
9788             }
9789         }
9790
9791         return el;
9792     },
9793     
9794     
9795     insertRow : function(dm, rowIndex, isUpdate){
9796         
9797         if(!isUpdate){
9798             this.fireEvent("beforerowsinserted", this, rowIndex);
9799         }
9800             //var s = this.getScrollState();
9801         var row = this.renderRow(this.cm, this.store, rowIndex);
9802         // insert before rowIndex..
9803         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9804         
9805         var _this = this;
9806                 
9807         if(row.cellObjects.length){
9808             Roo.each(row.cellObjects, function(r){
9809                 _this.renderCellObject(r);
9810             })
9811         }
9812             
9813         if(!isUpdate){
9814             this.fireEvent("rowsinserted", this, rowIndex);
9815             //this.syncRowHeights(firstRow, lastRow);
9816             //this.stripeRows(firstRow);
9817             //this.layout();
9818         }
9819         
9820     },
9821     
9822     
9823     getRowDom : function(rowIndex)
9824     {
9825         var rows = this.el.select('tbody > tr', true).elements;
9826         
9827         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9828         
9829     },
9830     getCellDom : function(rowIndex, colIndex)
9831     {
9832         var row = this.getRowDom(rowIndex);
9833         if (row === false) {
9834             return false;
9835         }
9836         var cols = row.select('td', true).elements;
9837         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9838         
9839     },
9840     
9841     // returns the object tree for a tr..
9842   
9843     
9844     renderRow : function(cm, ds, rowIndex) 
9845     {
9846         var d = ds.getAt(rowIndex);
9847         
9848         var row = {
9849             tag : 'tr',
9850             cls : 'x-row-' + rowIndex,
9851             cn : []
9852         };
9853             
9854         var cellObjects = [];
9855         
9856         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9857             var config = cm.config[i];
9858             
9859             var renderer = cm.getRenderer(i);
9860             var value = '';
9861             var id = false;
9862             
9863             if(typeof(renderer) !== 'undefined'){
9864                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9865             }
9866             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9867             // and are rendered into the cells after the row is rendered - using the id for the element.
9868             
9869             if(typeof(value) === 'object'){
9870                 id = Roo.id();
9871                 cellObjects.push({
9872                     container : id,
9873                     cfg : value 
9874                 })
9875             }
9876             
9877             var rowcfg = {
9878                 record: d,
9879                 rowIndex : rowIndex,
9880                 colIndex : i,
9881                 rowClass : ''
9882             };
9883
9884             this.fireEvent('rowclass', this, rowcfg);
9885             
9886             var td = {
9887                 tag: 'td',
9888                 // this might end up displaying HTML?
9889                 // this is too messy... - better to only do it on columsn you know are going to be too long
9890                 //tooltip : (typeof(value) === 'object') ? '' : value,
9891                 cls : rowcfg.rowClass + ' x-col-' + i,
9892                 style: '',
9893                 html: (typeof(value) === 'object') ? '' : value
9894             };
9895             
9896             if (id) {
9897                 td.id = id;
9898             }
9899             
9900             if(typeof(config.colspan) != 'undefined'){
9901                 td.colspan = config.colspan;
9902             }
9903             
9904             
9905             
9906             if(typeof(config.align) != 'undefined' && config.align.length){
9907                 td.style += ' text-align:' + config.align + ';';
9908             }
9909             if(typeof(config.valign) != 'undefined' && config.valign.length){
9910                 td.style += ' vertical-align:' + config.valign + ';';
9911             }
9912             /*
9913             if(typeof(config.width) != 'undefined'){
9914                 td.style += ' width:' +  config.width + 'px;';
9915             }
9916             */
9917             
9918             if(typeof(config.cursor) != 'undefined'){
9919                 td.style += ' cursor:' +  config.cursor + ';';
9920             }
9921             
9922             if(typeof(config.cls) != 'undefined'){
9923                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9924             }
9925             if (this.responsive) {
9926                 ['xs','sm','md','lg'].map(function(size){
9927                     
9928                     if(typeof(config[size]) == 'undefined'){
9929                         return;
9930                     }
9931                     
9932                     
9933                       
9934                     if (!config[size]) { // 0 = hidden
9935                         // BS 4 '0' is treated as hide that column and below.
9936                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9937                         return;
9938                     }
9939                     
9940                     td.cls += ' col-' + size + '-' + config[size] + (
9941                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9942                     );
9943                      
9944     
9945                 });
9946             }
9947             row.cn.push(td);
9948            
9949         }
9950         
9951         row.cellObjects = cellObjects;
9952         
9953         return row;
9954           
9955     },
9956     
9957     
9958     
9959     onBeforeLoad : function()
9960     {
9961         
9962     },
9963      /**
9964      * Remove all rows
9965      */
9966     clear : function()
9967     {
9968         this.el.select('tbody', true).first().dom.innerHTML = '';
9969     },
9970     /**
9971      * Show or hide a row.
9972      * @param {Number} rowIndex to show or hide
9973      * @param {Boolean} state hide
9974      */
9975     setRowVisibility : function(rowIndex, state)
9976     {
9977         var bt = this.bodyEl.dom;
9978         
9979         var rows = this.el.select('tbody > tr', true).elements;
9980         
9981         if(typeof(rows[rowIndex]) == 'undefined'){
9982             return;
9983         }
9984         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9985         
9986     },
9987     
9988     
9989     getSelectionModel : function(){
9990         if(!this.selModel){
9991             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9992         }
9993         return this.selModel;
9994     },
9995     /*
9996      * Render the Roo.bootstrap object from renderder
9997      */
9998     renderCellObject : function(r)
9999     {
10000         var _this = this;
10001         
10002         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10003         
10004         var t = r.cfg.render(r.container);
10005         
10006         if(r.cfg.cn){
10007             Roo.each(r.cfg.cn, function(c){
10008                 var child = {
10009                     container: t.getChildContainer(),
10010                     cfg: c
10011                 };
10012                 _this.renderCellObject(child);
10013             })
10014         }
10015     },
10016     /**
10017      * get the Row Index from a dom element.
10018      * @param {Roo.Element} row The row to look for
10019      * @returns {Number} the row
10020      */
10021     getRowIndex : function(row)
10022     {
10023         var rowIndex = -1;
10024         
10025         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10026             if(el != row){
10027                 return;
10028             }
10029             
10030             rowIndex = index;
10031         });
10032         
10033         return rowIndex;
10034     },
10035     /**
10036      * get the header TH element for columnIndex
10037      * @param {Number} columnIndex
10038      * @returns {Roo.Element}
10039      */
10040     getHeaderIndex: function(colIndex)
10041     {
10042         var cols = this.headEl.select('th', true).elements;
10043         return cols[colIndex]; 
10044     },
10045     /**
10046      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10047      * @param {domElement} cell to look for
10048      * @returns {Number} the column
10049      */
10050     getCellIndex : function(cell)
10051     {
10052         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10053         if(id){
10054             return parseInt(id[1], 10);
10055         }
10056         return 0;
10057     },
10058      /**
10059      * Returns the grid's underlying element = used by panel.Grid
10060      * @return {Element} The element
10061      */
10062     getGridEl : function(){
10063         return this.el;
10064     },
10065      /**
10066      * Forces a resize - used by panel.Grid
10067      * @return {Element} The element
10068      */
10069     autoSize : function()
10070     {
10071         //var ctr = Roo.get(this.container.dom.parentElement);
10072         var ctr = Roo.get(this.el.dom);
10073         
10074         var thd = this.getGridEl().select('thead',true).first();
10075         var tbd = this.getGridEl().select('tbody', true).first();
10076         var tfd = this.getGridEl().select('tfoot', true).first();
10077         
10078         var cw = ctr.getWidth();
10079         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10080         
10081         if (tbd) {
10082             
10083             tbd.setWidth(ctr.getWidth());
10084             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10085             // this needs fixing for various usage - currently only hydra job advers I think..
10086             //tdb.setHeight(
10087             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10088             //); 
10089             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10090             cw -= barsize;
10091         }
10092         cw = Math.max(cw, this.totalWidth);
10093         this.getGridEl().select('tbody tr',true).setWidth(cw);
10094         this.initCSS();
10095         
10096         // resize 'expandable coloumn?
10097         
10098         return; // we doe not have a view in this design..
10099         
10100     },
10101     onBodyScroll: function()
10102     {
10103         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10104         if(this.headEl){
10105             this.headEl.setStyle({
10106                 'position' : 'relative',
10107                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10108             });
10109         }
10110         
10111         if(this.lazyLoad){
10112             
10113             var scrollHeight = this.bodyEl.dom.scrollHeight;
10114             
10115             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10116             
10117             var height = this.bodyEl.getHeight();
10118             
10119             if(scrollHeight - height == scrollTop) {
10120                 
10121                 var total = this.ds.getTotalCount();
10122                 
10123                 if(this.footer.cursor + this.footer.pageSize < total){
10124                     
10125                     this.footer.ds.load({
10126                         params : {
10127                             start : this.footer.cursor + this.footer.pageSize,
10128                             limit : this.footer.pageSize
10129                         },
10130                         add : true
10131                     });
10132                 }
10133             }
10134             
10135         }
10136     },
10137     onColumnSplitterMoved : function(i, diff)
10138     {
10139         this.userResized = true;
10140         
10141         var cm = this.colModel;
10142         
10143         var w = this.getHeaderIndex(i).getWidth() + diff;
10144         
10145         
10146         cm.setColumnWidth(i, w, true);
10147         this.initCSS();
10148         //var cid = cm.getColumnId(i); << not used in this version?
10149        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10150         
10151         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10152         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10153         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10154 */
10155         //this.updateSplitters();
10156         //this.layout(); << ??
10157         this.fireEvent("columnresize", i, w);
10158     },
10159     onHeaderChange : function()
10160     {
10161         var header = this.renderHeader();
10162         var table = this.el.select('table', true).first();
10163         
10164         this.headEl.remove();
10165         this.headEl = table.createChild(header, this.bodyEl, false);
10166         
10167         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10168             e.on('click', this.sort, this);
10169         }, this);
10170         
10171         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10172             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10173         }
10174         
10175     },
10176     
10177     onHiddenChange : function(colModel, colIndex, hidden)
10178     {
10179         /*
10180         this.cm.setHidden()
10181         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10182         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10183         
10184         this.CSS.updateRule(thSelector, "display", "");
10185         this.CSS.updateRule(tdSelector, "display", "");
10186         
10187         if(hidden){
10188             this.CSS.updateRule(thSelector, "display", "none");
10189             this.CSS.updateRule(tdSelector, "display", "none");
10190         }
10191         */
10192         // onload calls initCSS()
10193         this.onHeaderChange();
10194         this.onLoad();
10195     },
10196     
10197     setColumnWidth: function(col_index, width)
10198     {
10199         // width = "md-2 xs-2..."
10200         if(!this.colModel.config[col_index]) {
10201             return;
10202         }
10203         
10204         var w = width.split(" ");
10205         
10206         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10207         
10208         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10209         
10210         
10211         for(var j = 0; j < w.length; j++) {
10212             
10213             if(!w[j]) {
10214                 continue;
10215             }
10216             
10217             var size_cls = w[j].split("-");
10218             
10219             if(!Number.isInteger(size_cls[1] * 1)) {
10220                 continue;
10221             }
10222             
10223             if(!this.colModel.config[col_index][size_cls[0]]) {
10224                 continue;
10225             }
10226             
10227             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10228                 continue;
10229             }
10230             
10231             h_row[0].classList.replace(
10232                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10233                 "col-"+size_cls[0]+"-"+size_cls[1]
10234             );
10235             
10236             for(var i = 0; i < rows.length; i++) {
10237                 
10238                 var size_cls = w[j].split("-");
10239                 
10240                 if(!Number.isInteger(size_cls[1] * 1)) {
10241                     continue;
10242                 }
10243                 
10244                 if(!this.colModel.config[col_index][size_cls[0]]) {
10245                     continue;
10246                 }
10247                 
10248                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10249                     continue;
10250                 }
10251                 
10252                 rows[i].classList.replace(
10253                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10254                     "col-"+size_cls[0]+"-"+size_cls[1]
10255                 );
10256             }
10257             
10258             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10259         }
10260     }
10261 });
10262
10263 // currently only used to find the split on drag.. 
10264 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10265
10266 /**
10267  * @depricated
10268 */
10269 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10270 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10271 /*
10272  * - LGPL
10273  *
10274  * table cell
10275  * 
10276  */
10277
10278 /**
10279  * @class Roo.bootstrap.TableCell
10280  * @extends Roo.bootstrap.Component
10281  * Bootstrap TableCell class
10282  * @cfg {String} html cell contain text
10283  * @cfg {String} cls cell class
10284  * @cfg {String} tag cell tag (td|th) default td
10285  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10286  * @cfg {String} align Aligns the content in a cell
10287  * @cfg {String} axis Categorizes cells
10288  * @cfg {String} bgcolor Specifies the background color of a cell
10289  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10290  * @cfg {Number} colspan Specifies the number of columns a cell should span
10291  * @cfg {String} headers Specifies one or more header cells a cell is related to
10292  * @cfg {Number} height Sets the height of a cell
10293  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10294  * @cfg {Number} rowspan Sets the number of rows a cell should span
10295  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10296  * @cfg {String} valign Vertical aligns the content in a cell
10297  * @cfg {Number} width Specifies the width of a cell
10298  * 
10299  * @constructor
10300  * Create a new TableCell
10301  * @param {Object} config The config object
10302  */
10303
10304 Roo.bootstrap.TableCell = function(config){
10305     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10306 };
10307
10308 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10309     
10310     html: false,
10311     cls: false,
10312     tag: false,
10313     abbr: false,
10314     align: false,
10315     axis: false,
10316     bgcolor: false,
10317     charoff: false,
10318     colspan: false,
10319     headers: false,
10320     height: false,
10321     nowrap: false,
10322     rowspan: false,
10323     scope: false,
10324     valign: false,
10325     width: false,
10326     
10327     
10328     getAutoCreate : function(){
10329         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10330         
10331         cfg = {
10332             tag: 'td'
10333         };
10334         
10335         if(this.tag){
10336             cfg.tag = this.tag;
10337         }
10338         
10339         if (this.html) {
10340             cfg.html=this.html
10341         }
10342         if (this.cls) {
10343             cfg.cls=this.cls
10344         }
10345         if (this.abbr) {
10346             cfg.abbr=this.abbr
10347         }
10348         if (this.align) {
10349             cfg.align=this.align
10350         }
10351         if (this.axis) {
10352             cfg.axis=this.axis
10353         }
10354         if (this.bgcolor) {
10355             cfg.bgcolor=this.bgcolor
10356         }
10357         if (this.charoff) {
10358             cfg.charoff=this.charoff
10359         }
10360         if (this.colspan) {
10361             cfg.colspan=this.colspan
10362         }
10363         if (this.headers) {
10364             cfg.headers=this.headers
10365         }
10366         if (this.height) {
10367             cfg.height=this.height
10368         }
10369         if (this.nowrap) {
10370             cfg.nowrap=this.nowrap
10371         }
10372         if (this.rowspan) {
10373             cfg.rowspan=this.rowspan
10374         }
10375         if (this.scope) {
10376             cfg.scope=this.scope
10377         }
10378         if (this.valign) {
10379             cfg.valign=this.valign
10380         }
10381         if (this.width) {
10382             cfg.width=this.width
10383         }
10384         
10385         
10386         return cfg;
10387     }
10388    
10389 });
10390
10391  
10392
10393  /*
10394  * - LGPL
10395  *
10396  * table row
10397  * 
10398  */
10399
10400 /**
10401  * @class Roo.bootstrap.TableRow
10402  * @extends Roo.bootstrap.Component
10403  * Bootstrap TableRow class
10404  * @cfg {String} cls row class
10405  * @cfg {String} align Aligns the content in a table row
10406  * @cfg {String} bgcolor Specifies a background color for a table row
10407  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10408  * @cfg {String} valign Vertical aligns the content in a table row
10409  * 
10410  * @constructor
10411  * Create a new TableRow
10412  * @param {Object} config The config object
10413  */
10414
10415 Roo.bootstrap.TableRow = function(config){
10416     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10417 };
10418
10419 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10420     
10421     cls: false,
10422     align: false,
10423     bgcolor: false,
10424     charoff: false,
10425     valign: false,
10426     
10427     getAutoCreate : function(){
10428         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10429         
10430         cfg = {
10431             tag: 'tr'
10432         };
10433             
10434         if(this.cls){
10435             cfg.cls = this.cls;
10436         }
10437         if(this.align){
10438             cfg.align = this.align;
10439         }
10440         if(this.bgcolor){
10441             cfg.bgcolor = this.bgcolor;
10442         }
10443         if(this.charoff){
10444             cfg.charoff = this.charoff;
10445         }
10446         if(this.valign){
10447             cfg.valign = this.valign;
10448         }
10449         
10450         return cfg;
10451     }
10452    
10453 });
10454
10455  
10456
10457  /*
10458  * - LGPL
10459  *
10460  * table body
10461  * 
10462  */
10463
10464 /**
10465  * @class Roo.bootstrap.TableBody
10466  * @extends Roo.bootstrap.Component
10467  * Bootstrap TableBody class
10468  * @cfg {String} cls element class
10469  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10470  * @cfg {String} align Aligns the content inside the element
10471  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10472  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10473  * 
10474  * @constructor
10475  * Create a new TableBody
10476  * @param {Object} config The config object
10477  */
10478
10479 Roo.bootstrap.TableBody = function(config){
10480     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10481 };
10482
10483 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10484     
10485     cls: false,
10486     tag: false,
10487     align: false,
10488     charoff: false,
10489     valign: false,
10490     
10491     getAutoCreate : function(){
10492         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10493         
10494         cfg = {
10495             tag: 'tbody'
10496         };
10497             
10498         if (this.cls) {
10499             cfg.cls=this.cls
10500         }
10501         if(this.tag){
10502             cfg.tag = this.tag;
10503         }
10504         
10505         if(this.align){
10506             cfg.align = this.align;
10507         }
10508         if(this.charoff){
10509             cfg.charoff = this.charoff;
10510         }
10511         if(this.valign){
10512             cfg.valign = this.valign;
10513         }
10514         
10515         return cfg;
10516     }
10517     
10518     
10519 //    initEvents : function()
10520 //    {
10521 //        
10522 //        if(!this.store){
10523 //            return;
10524 //        }
10525 //        
10526 //        this.store = Roo.factory(this.store, Roo.data);
10527 //        this.store.on('load', this.onLoad, this);
10528 //        
10529 //        this.store.load();
10530 //        
10531 //    },
10532 //    
10533 //    onLoad: function () 
10534 //    {   
10535 //        this.fireEvent('load', this);
10536 //    }
10537 //    
10538 //   
10539 });
10540
10541  
10542
10543  /*
10544  * Based on:
10545  * Ext JS Library 1.1.1
10546  * Copyright(c) 2006-2007, Ext JS, LLC.
10547  *
10548  * Originally Released Under LGPL - original licence link has changed is not relivant.
10549  *
10550  * Fork - LGPL
10551  * <script type="text/javascript">
10552  */
10553
10554 // as we use this in bootstrap.
10555 Roo.namespace('Roo.form');
10556  /**
10557  * @class Roo.form.Action
10558  * Internal Class used to handle form actions
10559  * @constructor
10560  * @param {Roo.form.BasicForm} el The form element or its id
10561  * @param {Object} config Configuration options
10562  */
10563
10564  
10565  
10566 // define the action interface
10567 Roo.form.Action = function(form, options){
10568     this.form = form;
10569     this.options = options || {};
10570 };
10571 /**
10572  * Client Validation Failed
10573  * @const 
10574  */
10575 Roo.form.Action.CLIENT_INVALID = 'client';
10576 /**
10577  * Server Validation Failed
10578  * @const 
10579  */
10580 Roo.form.Action.SERVER_INVALID = 'server';
10581  /**
10582  * Connect to Server Failed
10583  * @const 
10584  */
10585 Roo.form.Action.CONNECT_FAILURE = 'connect';
10586 /**
10587  * Reading Data from Server Failed
10588  * @const 
10589  */
10590 Roo.form.Action.LOAD_FAILURE = 'load';
10591
10592 Roo.form.Action.prototype = {
10593     type : 'default',
10594     failureType : undefined,
10595     response : undefined,
10596     result : undefined,
10597
10598     // interface method
10599     run : function(options){
10600
10601     },
10602
10603     // interface method
10604     success : function(response){
10605
10606     },
10607
10608     // interface method
10609     handleResponse : function(response){
10610
10611     },
10612
10613     // default connection failure
10614     failure : function(response){
10615         
10616         this.response = response;
10617         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10618         this.form.afterAction(this, false);
10619     },
10620
10621     processResponse : function(response){
10622         this.response = response;
10623         if(!response.responseText){
10624             return true;
10625         }
10626         this.result = this.handleResponse(response);
10627         return this.result;
10628     },
10629
10630     // utility functions used internally
10631     getUrl : function(appendParams){
10632         var url = this.options.url || this.form.url || this.form.el.dom.action;
10633         if(appendParams){
10634             var p = this.getParams();
10635             if(p){
10636                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10637             }
10638         }
10639         return url;
10640     },
10641
10642     getMethod : function(){
10643         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10644     },
10645
10646     getParams : function(){
10647         var bp = this.form.baseParams;
10648         var p = this.options.params;
10649         if(p){
10650             if(typeof p == "object"){
10651                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10652             }else if(typeof p == 'string' && bp){
10653                 p += '&' + Roo.urlEncode(bp);
10654             }
10655         }else if(bp){
10656             p = Roo.urlEncode(bp);
10657         }
10658         return p;
10659     },
10660
10661     createCallback : function(){
10662         return {
10663             success: this.success,
10664             failure: this.failure,
10665             scope: this,
10666             timeout: (this.form.timeout*1000),
10667             upload: this.form.fileUpload ? this.success : undefined
10668         };
10669     }
10670 };
10671
10672 Roo.form.Action.Submit = function(form, options){
10673     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10674 };
10675
10676 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10677     type : 'submit',
10678
10679     haveProgress : false,
10680     uploadComplete : false,
10681     
10682     // uploadProgress indicator.
10683     uploadProgress : function()
10684     {
10685         if (!this.form.progressUrl) {
10686             return;
10687         }
10688         
10689         if (!this.haveProgress) {
10690             Roo.MessageBox.progress("Uploading", "Uploading");
10691         }
10692         if (this.uploadComplete) {
10693            Roo.MessageBox.hide();
10694            return;
10695         }
10696         
10697         this.haveProgress = true;
10698    
10699         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10700         
10701         var c = new Roo.data.Connection();
10702         c.request({
10703             url : this.form.progressUrl,
10704             params: {
10705                 id : uid
10706             },
10707             method: 'GET',
10708             success : function(req){
10709                //console.log(data);
10710                 var rdata = false;
10711                 var edata;
10712                 try  {
10713                    rdata = Roo.decode(req.responseText)
10714                 } catch (e) {
10715                     Roo.log("Invalid data from server..");
10716                     Roo.log(edata);
10717                     return;
10718                 }
10719                 if (!rdata || !rdata.success) {
10720                     Roo.log(rdata);
10721                     Roo.MessageBox.alert(Roo.encode(rdata));
10722                     return;
10723                 }
10724                 var data = rdata.data;
10725                 
10726                 if (this.uploadComplete) {
10727                    Roo.MessageBox.hide();
10728                    return;
10729                 }
10730                    
10731                 if (data){
10732                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10733                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10734                     );
10735                 }
10736                 this.uploadProgress.defer(2000,this);
10737             },
10738        
10739             failure: function(data) {
10740                 Roo.log('progress url failed ');
10741                 Roo.log(data);
10742             },
10743             scope : this
10744         });
10745            
10746     },
10747     
10748     
10749     run : function()
10750     {
10751         // run get Values on the form, so it syncs any secondary forms.
10752         this.form.getValues();
10753         
10754         var o = this.options;
10755         var method = this.getMethod();
10756         var isPost = method == 'POST';
10757         if(o.clientValidation === false || this.form.isValid()){
10758             
10759             if (this.form.progressUrl) {
10760                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10761                     (new Date() * 1) + '' + Math.random());
10762                     
10763             } 
10764             
10765             
10766             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10767                 form:this.form.el.dom,
10768                 url:this.getUrl(!isPost),
10769                 method: method,
10770                 params:isPost ? this.getParams() : null,
10771                 isUpload: this.form.fileUpload,
10772                 formData : this.form.formData
10773             }));
10774             
10775             this.uploadProgress();
10776
10777         }else if (o.clientValidation !== false){ // client validation failed
10778             this.failureType = Roo.form.Action.CLIENT_INVALID;
10779             this.form.afterAction(this, false);
10780         }
10781     },
10782
10783     success : function(response)
10784     {
10785         this.uploadComplete= true;
10786         if (this.haveProgress) {
10787             Roo.MessageBox.hide();
10788         }
10789         
10790         
10791         var result = this.processResponse(response);
10792         if(result === true || result.success){
10793             this.form.afterAction(this, true);
10794             return;
10795         }
10796         if(result.errors){
10797             this.form.markInvalid(result.errors);
10798             this.failureType = Roo.form.Action.SERVER_INVALID;
10799         }
10800         this.form.afterAction(this, false);
10801     },
10802     failure : function(response)
10803     {
10804         this.uploadComplete= true;
10805         if (this.haveProgress) {
10806             Roo.MessageBox.hide();
10807         }
10808         
10809         this.response = response;
10810         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10811         this.form.afterAction(this, false);
10812     },
10813     
10814     handleResponse : function(response){
10815         if(this.form.errorReader){
10816             var rs = this.form.errorReader.read(response);
10817             var errors = [];
10818             if(rs.records){
10819                 for(var i = 0, len = rs.records.length; i < len; i++) {
10820                     var r = rs.records[i];
10821                     errors[i] = r.data;
10822                 }
10823             }
10824             if(errors.length < 1){
10825                 errors = null;
10826             }
10827             return {
10828                 success : rs.success,
10829                 errors : errors
10830             };
10831         }
10832         var ret = false;
10833         try {
10834             ret = Roo.decode(response.responseText);
10835         } catch (e) {
10836             ret = {
10837                 success: false,
10838                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10839                 errors : []
10840             };
10841         }
10842         return ret;
10843         
10844     }
10845 });
10846
10847
10848 Roo.form.Action.Load = function(form, options){
10849     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10850     this.reader = this.form.reader;
10851 };
10852
10853 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10854     type : 'load',
10855
10856     run : function(){
10857         
10858         Roo.Ajax.request(Roo.apply(
10859                 this.createCallback(), {
10860                     method:this.getMethod(),
10861                     url:this.getUrl(false),
10862                     params:this.getParams()
10863         }));
10864     },
10865
10866     success : function(response){
10867         
10868         var result = this.processResponse(response);
10869         if(result === true || !result.success || !result.data){
10870             this.failureType = Roo.form.Action.LOAD_FAILURE;
10871             this.form.afterAction(this, false);
10872             return;
10873         }
10874         this.form.clearInvalid();
10875         this.form.setValues(result.data);
10876         this.form.afterAction(this, true);
10877     },
10878
10879     handleResponse : function(response){
10880         if(this.form.reader){
10881             var rs = this.form.reader.read(response);
10882             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10883             return {
10884                 success : rs.success,
10885                 data : data
10886             };
10887         }
10888         return Roo.decode(response.responseText);
10889     }
10890 });
10891
10892 Roo.form.Action.ACTION_TYPES = {
10893     'load' : Roo.form.Action.Load,
10894     'submit' : Roo.form.Action.Submit
10895 };/*
10896  * - LGPL
10897  *
10898  * form
10899  *
10900  */
10901
10902 /**
10903  * @class Roo.bootstrap.Form
10904  * @extends Roo.bootstrap.Component
10905  * Bootstrap Form class
10906  * @cfg {String} method  GET | POST (default POST)
10907  * @cfg {String} labelAlign top | left (default top)
10908  * @cfg {String} align left  | right - for navbars
10909  * @cfg {Boolean} loadMask load mask when submit (default true)
10910
10911  *
10912  * @constructor
10913  * Create a new Form
10914  * @param {Object} config The config object
10915  */
10916
10917
10918 Roo.bootstrap.Form = function(config){
10919     
10920     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10921     
10922     Roo.bootstrap.Form.popover.apply();
10923     
10924     this.addEvents({
10925         /**
10926          * @event clientvalidation
10927          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10928          * @param {Form} this
10929          * @param {Boolean} valid true if the form has passed client-side validation
10930          */
10931         clientvalidation: true,
10932         /**
10933          * @event beforeaction
10934          * Fires before any action is performed. Return false to cancel the action.
10935          * @param {Form} this
10936          * @param {Action} action The action to be performed
10937          */
10938         beforeaction: true,
10939         /**
10940          * @event actionfailed
10941          * Fires when an action fails.
10942          * @param {Form} this
10943          * @param {Action} action The action that failed
10944          */
10945         actionfailed : true,
10946         /**
10947          * @event actioncomplete
10948          * Fires when an action is completed.
10949          * @param {Form} this
10950          * @param {Action} action The action that completed
10951          */
10952         actioncomplete : true
10953     });
10954 };
10955
10956 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10957
10958      /**
10959      * @cfg {String} method
10960      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10961      */
10962     method : 'POST',
10963     /**
10964      * @cfg {String} url
10965      * The URL to use for form actions if one isn't supplied in the action options.
10966      */
10967     /**
10968      * @cfg {Boolean} fileUpload
10969      * Set to true if this form is a file upload.
10970      */
10971
10972     /**
10973      * @cfg {Object} baseParams
10974      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10975      */
10976
10977     /**
10978      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10979      */
10980     timeout: 30,
10981     /**
10982      * @cfg {Sting} align (left|right) for navbar forms
10983      */
10984     align : 'left',
10985
10986     // private
10987     activeAction : null,
10988
10989     /**
10990      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10991      * element by passing it or its id or mask the form itself by passing in true.
10992      * @type Mixed
10993      */
10994     waitMsgTarget : false,
10995
10996     loadMask : true,
10997     
10998     /**
10999      * @cfg {Boolean} errorMask (true|false) default false
11000      */
11001     errorMask : false,
11002     
11003     /**
11004      * @cfg {Number} maskOffset Default 100
11005      */
11006     maskOffset : 100,
11007     
11008     /**
11009      * @cfg {Boolean} maskBody
11010      */
11011     maskBody : false,
11012
11013     getAutoCreate : function(){
11014
11015         var cfg = {
11016             tag: 'form',
11017             method : this.method || 'POST',
11018             id : this.id || Roo.id(),
11019             cls : ''
11020         };
11021         if (this.parent().xtype.match(/^Nav/)) {
11022             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11023
11024         }
11025
11026         if (this.labelAlign == 'left' ) {
11027             cfg.cls += ' form-horizontal';
11028         }
11029
11030
11031         return cfg;
11032     },
11033     initEvents : function()
11034     {
11035         this.el.on('submit', this.onSubmit, this);
11036         // this was added as random key presses on the form where triggering form submit.
11037         this.el.on('keypress', function(e) {
11038             if (e.getCharCode() != 13) {
11039                 return true;
11040             }
11041             // we might need to allow it for textareas.. and some other items.
11042             // check e.getTarget().
11043
11044             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11045                 return true;
11046             }
11047
11048             Roo.log("keypress blocked");
11049
11050             e.preventDefault();
11051             return false;
11052         });
11053         
11054     },
11055     // private
11056     onSubmit : function(e){
11057         e.stopEvent();
11058     },
11059
11060      /**
11061      * Returns true if client-side validation on the form is successful.
11062      * @return Boolean
11063      */
11064     isValid : function(){
11065         var items = this.getItems();
11066         var valid = true;
11067         var target = false;
11068         
11069         items.each(function(f){
11070             
11071             if(f.validate()){
11072                 return;
11073             }
11074             
11075             Roo.log('invalid field: ' + f.name);
11076             
11077             valid = false;
11078
11079             if(!target && f.el.isVisible(true)){
11080                 target = f;
11081             }
11082            
11083         });
11084         
11085         if(this.errorMask && !valid){
11086             Roo.bootstrap.Form.popover.mask(this, target);
11087         }
11088         
11089         return valid;
11090     },
11091     
11092     /**
11093      * Returns true if any fields in this form have changed since their original load.
11094      * @return Boolean
11095      */
11096     isDirty : function(){
11097         var dirty = false;
11098         var items = this.getItems();
11099         items.each(function(f){
11100            if(f.isDirty()){
11101                dirty = true;
11102                return false;
11103            }
11104            return true;
11105         });
11106         return dirty;
11107     },
11108      /**
11109      * Performs a predefined action (submit or load) or custom actions you define on this form.
11110      * @param {String} actionName The name of the action type
11111      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11112      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11113      * accept other config options):
11114      * <pre>
11115 Property          Type             Description
11116 ----------------  ---------------  ----------------------------------------------------------------------------------
11117 url               String           The url for the action (defaults to the form's url)
11118 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11119 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11120 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11121                                    validate the form on the client (defaults to false)
11122      * </pre>
11123      * @return {BasicForm} this
11124      */
11125     doAction : function(action, options){
11126         if(typeof action == 'string'){
11127             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11128         }
11129         if(this.fireEvent('beforeaction', this, action) !== false){
11130             this.beforeAction(action);
11131             action.run.defer(100, action);
11132         }
11133         return this;
11134     },
11135
11136     // private
11137     beforeAction : function(action){
11138         var o = action.options;
11139         
11140         if(this.loadMask){
11141             
11142             if(this.maskBody){
11143                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11144             } else {
11145                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11146             }
11147         }
11148         // not really supported yet.. ??
11149
11150         //if(this.waitMsgTarget === true){
11151         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11152         //}else if(this.waitMsgTarget){
11153         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11154         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11155         //}else {
11156         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11157        // }
11158
11159     },
11160
11161     // private
11162     afterAction : function(action, success){
11163         this.activeAction = null;
11164         var o = action.options;
11165
11166         if(this.loadMask){
11167             
11168             if(this.maskBody){
11169                 Roo.get(document.body).unmask();
11170             } else {
11171                 this.el.unmask();
11172             }
11173         }
11174         
11175         //if(this.waitMsgTarget === true){
11176 //            this.el.unmask();
11177         //}else if(this.waitMsgTarget){
11178         //    this.waitMsgTarget.unmask();
11179         //}else{
11180         //    Roo.MessageBox.updateProgress(1);
11181         //    Roo.MessageBox.hide();
11182        // }
11183         //
11184         if(success){
11185             if(o.reset){
11186                 this.reset();
11187             }
11188             Roo.callback(o.success, o.scope, [this, action]);
11189             this.fireEvent('actioncomplete', this, action);
11190
11191         }else{
11192
11193             // failure condition..
11194             // we have a scenario where updates need confirming.
11195             // eg. if a locking scenario exists..
11196             // we look for { errors : { needs_confirm : true }} in the response.
11197             if (
11198                 (typeof(action.result) != 'undefined')  &&
11199                 (typeof(action.result.errors) != 'undefined')  &&
11200                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11201            ){
11202                 var _t = this;
11203                 Roo.log("not supported yet");
11204                  /*
11205
11206                 Roo.MessageBox.confirm(
11207                     "Change requires confirmation",
11208                     action.result.errorMsg,
11209                     function(r) {
11210                         if (r != 'yes') {
11211                             return;
11212                         }
11213                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11214                     }
11215
11216                 );
11217                 */
11218
11219
11220                 return;
11221             }
11222
11223             Roo.callback(o.failure, o.scope, [this, action]);
11224             // show an error message if no failed handler is set..
11225             if (!this.hasListener('actionfailed')) {
11226                 Roo.log("need to add dialog support");
11227                 /*
11228                 Roo.MessageBox.alert("Error",
11229                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11230                         action.result.errorMsg :
11231                         "Saving Failed, please check your entries or try again"
11232                 );
11233                 */
11234             }
11235
11236             this.fireEvent('actionfailed', this, action);
11237         }
11238
11239     },
11240     /**
11241      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11242      * @param {String} id The value to search for
11243      * @return Field
11244      */
11245     findField : function(id){
11246         var items = this.getItems();
11247         var field = items.get(id);
11248         if(!field){
11249              items.each(function(f){
11250                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11251                     field = f;
11252                     return false;
11253                 }
11254                 return true;
11255             });
11256         }
11257         return field || null;
11258     },
11259      /**
11260      * Mark fields in this form invalid in bulk.
11261      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11262      * @return {BasicForm} this
11263      */
11264     markInvalid : function(errors){
11265         if(errors instanceof Array){
11266             for(var i = 0, len = errors.length; i < len; i++){
11267                 var fieldError = errors[i];
11268                 var f = this.findField(fieldError.id);
11269                 if(f){
11270                     f.markInvalid(fieldError.msg);
11271                 }
11272             }
11273         }else{
11274             var field, id;
11275             for(id in errors){
11276                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11277                     field.markInvalid(errors[id]);
11278                 }
11279             }
11280         }
11281         //Roo.each(this.childForms || [], function (f) {
11282         //    f.markInvalid(errors);
11283         //});
11284
11285         return this;
11286     },
11287
11288     /**
11289      * Set values for fields in this form in bulk.
11290      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11291      * @return {BasicForm} this
11292      */
11293     setValues : function(values){
11294         if(values instanceof Array){ // array of objects
11295             for(var i = 0, len = values.length; i < len; i++){
11296                 var v = values[i];
11297                 var f = this.findField(v.id);
11298                 if(f){
11299                     f.setValue(v.value);
11300                     if(this.trackResetOnLoad){
11301                         f.originalValue = f.getValue();
11302                     }
11303                 }
11304             }
11305         }else{ // object hash
11306             var field, id;
11307             for(id in values){
11308                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11309
11310                     if (field.setFromData &&
11311                         field.valueField &&
11312                         field.displayField &&
11313                         // combos' with local stores can
11314                         // be queried via setValue()
11315                         // to set their value..
11316                         (field.store && !field.store.isLocal)
11317                         ) {
11318                         // it's a combo
11319                         var sd = { };
11320                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11321                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11322                         field.setFromData(sd);
11323
11324                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11325                         
11326                         field.setFromData(values);
11327                         
11328                     } else {
11329                         field.setValue(values[id]);
11330                     }
11331
11332
11333                     if(this.trackResetOnLoad){
11334                         field.originalValue = field.getValue();
11335                     }
11336                 }
11337             }
11338         }
11339
11340         //Roo.each(this.childForms || [], function (f) {
11341         //    f.setValues(values);
11342         //});
11343
11344         return this;
11345     },
11346
11347     /**
11348      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11349      * they are returned as an array.
11350      * @param {Boolean} asString
11351      * @return {Object}
11352      */
11353     getValues : function(asString){
11354         //if (this.childForms) {
11355             // copy values from the child forms
11356         //    Roo.each(this.childForms, function (f) {
11357         //        this.setValues(f.getValues());
11358         //    }, this);
11359         //}
11360
11361
11362
11363         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11364         if(asString === true){
11365             return fs;
11366         }
11367         return Roo.urlDecode(fs);
11368     },
11369
11370     /**
11371      * Returns the fields in this form as an object with key/value pairs.
11372      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11373      * @return {Object}
11374      */
11375     getFieldValues : function(with_hidden)
11376     {
11377         var items = this.getItems();
11378         var ret = {};
11379         items.each(function(f){
11380             
11381             if (!f.getName()) {
11382                 return;
11383             }
11384             
11385             var v = f.getValue();
11386             
11387             if (f.inputType =='radio') {
11388                 if (typeof(ret[f.getName()]) == 'undefined') {
11389                     ret[f.getName()] = ''; // empty..
11390                 }
11391
11392                 if (!f.el.dom.checked) {
11393                     return;
11394
11395                 }
11396                 v = f.el.dom.value;
11397
11398             }
11399             
11400             if(f.xtype == 'MoneyField'){
11401                 ret[f.currencyName] = f.getCurrency();
11402             }
11403
11404             // not sure if this supported any more..
11405             if ((typeof(v) == 'object') && f.getRawValue) {
11406                 v = f.getRawValue() ; // dates..
11407             }
11408             // combo boxes where name != hiddenName...
11409             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11410                 ret[f.name] = f.getRawValue();
11411             }
11412             ret[f.getName()] = v;
11413         });
11414
11415         return ret;
11416     },
11417
11418     /**
11419      * Clears all invalid messages in this form.
11420      * @return {BasicForm} this
11421      */
11422     clearInvalid : function(){
11423         var items = this.getItems();
11424
11425         items.each(function(f){
11426            f.clearInvalid();
11427         });
11428
11429         return this;
11430     },
11431
11432     /**
11433      * Resets this form.
11434      * @return {BasicForm} this
11435      */
11436     reset : function(){
11437         var items = this.getItems();
11438         items.each(function(f){
11439             f.reset();
11440         });
11441
11442         Roo.each(this.childForms || [], function (f) {
11443             f.reset();
11444         });
11445
11446
11447         return this;
11448     },
11449     
11450     getItems : function()
11451     {
11452         var r=new Roo.util.MixedCollection(false, function(o){
11453             return o.id || (o.id = Roo.id());
11454         });
11455         var iter = function(el) {
11456             if (el.inputEl) {
11457                 r.add(el);
11458             }
11459             if (!el.items) {
11460                 return;
11461             }
11462             Roo.each(el.items,function(e) {
11463                 iter(e);
11464             });
11465         };
11466
11467         iter(this);
11468         return r;
11469     },
11470     
11471     hideFields : function(items)
11472     {
11473         Roo.each(items, function(i){
11474             
11475             var f = this.findField(i);
11476             
11477             if(!f){
11478                 return;
11479             }
11480             
11481             f.hide();
11482             
11483         }, this);
11484     },
11485     
11486     showFields : function(items)
11487     {
11488         Roo.each(items, function(i){
11489             
11490             var f = this.findField(i);
11491             
11492             if(!f){
11493                 return;
11494             }
11495             
11496             f.show();
11497             
11498         }, this);
11499     }
11500
11501 });
11502
11503 Roo.apply(Roo.bootstrap.Form, {
11504     
11505     popover : {
11506         
11507         padding : 5,
11508         
11509         isApplied : false,
11510         
11511         isMasked : false,
11512         
11513         form : false,
11514         
11515         target : false,
11516         
11517         toolTip : false,
11518         
11519         intervalID : false,
11520         
11521         maskEl : false,
11522         
11523         apply : function()
11524         {
11525             if(this.isApplied){
11526                 return;
11527             }
11528             
11529             this.maskEl = {
11530                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11531                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11532                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11533                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11534             };
11535             
11536             this.maskEl.top.enableDisplayMode("block");
11537             this.maskEl.left.enableDisplayMode("block");
11538             this.maskEl.bottom.enableDisplayMode("block");
11539             this.maskEl.right.enableDisplayMode("block");
11540             
11541             this.toolTip = new Roo.bootstrap.Tooltip({
11542                 cls : 'roo-form-error-popover',
11543                 alignment : {
11544                     'left' : ['r-l', [-2,0], 'right'],
11545                     'right' : ['l-r', [2,0], 'left'],
11546                     'bottom' : ['tl-bl', [0,2], 'top'],
11547                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11548                 }
11549             });
11550             
11551             this.toolTip.render(Roo.get(document.body));
11552
11553             this.toolTip.el.enableDisplayMode("block");
11554             
11555             Roo.get(document.body).on('click', function(){
11556                 this.unmask();
11557             }, this);
11558             
11559             Roo.get(document.body).on('touchstart', function(){
11560                 this.unmask();
11561             }, this);
11562             
11563             this.isApplied = true
11564         },
11565         
11566         mask : function(form, target)
11567         {
11568             this.form = form;
11569             
11570             this.target = target;
11571             
11572             if(!this.form.errorMask || !target.el){
11573                 return;
11574             }
11575             
11576             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11577             
11578             Roo.log(scrollable);
11579             
11580             var ot = this.target.el.calcOffsetsTo(scrollable);
11581             
11582             var scrollTo = ot[1] - this.form.maskOffset;
11583             
11584             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11585             
11586             scrollable.scrollTo('top', scrollTo);
11587             
11588             var box = this.target.el.getBox();
11589             Roo.log(box);
11590             var zIndex = Roo.bootstrap.Modal.zIndex++;
11591
11592             
11593             this.maskEl.top.setStyle('position', 'absolute');
11594             this.maskEl.top.setStyle('z-index', zIndex);
11595             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11596             this.maskEl.top.setLeft(0);
11597             this.maskEl.top.setTop(0);
11598             this.maskEl.top.show();
11599             
11600             this.maskEl.left.setStyle('position', 'absolute');
11601             this.maskEl.left.setStyle('z-index', zIndex);
11602             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11603             this.maskEl.left.setLeft(0);
11604             this.maskEl.left.setTop(box.y - this.padding);
11605             this.maskEl.left.show();
11606
11607             this.maskEl.bottom.setStyle('position', 'absolute');
11608             this.maskEl.bottom.setStyle('z-index', zIndex);
11609             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11610             this.maskEl.bottom.setLeft(0);
11611             this.maskEl.bottom.setTop(box.bottom + this.padding);
11612             this.maskEl.bottom.show();
11613
11614             this.maskEl.right.setStyle('position', 'absolute');
11615             this.maskEl.right.setStyle('z-index', zIndex);
11616             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11617             this.maskEl.right.setLeft(box.right + this.padding);
11618             this.maskEl.right.setTop(box.y - this.padding);
11619             this.maskEl.right.show();
11620
11621             this.toolTip.bindEl = this.target.el;
11622
11623             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11624
11625             var tip = this.target.blankText;
11626
11627             if(this.target.getValue() !== '' ) {
11628                 
11629                 if (this.target.invalidText.length) {
11630                     tip = this.target.invalidText;
11631                 } else if (this.target.regexText.length){
11632                     tip = this.target.regexText;
11633                 }
11634             }
11635
11636             this.toolTip.show(tip);
11637
11638             this.intervalID = window.setInterval(function() {
11639                 Roo.bootstrap.Form.popover.unmask();
11640             }, 10000);
11641
11642             window.onwheel = function(){ return false;};
11643             
11644             (function(){ this.isMasked = true; }).defer(500, this);
11645             
11646         },
11647         
11648         unmask : function()
11649         {
11650             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11651                 return;
11652             }
11653             
11654             this.maskEl.top.setStyle('position', 'absolute');
11655             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11656             this.maskEl.top.hide();
11657
11658             this.maskEl.left.setStyle('position', 'absolute');
11659             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11660             this.maskEl.left.hide();
11661
11662             this.maskEl.bottom.setStyle('position', 'absolute');
11663             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11664             this.maskEl.bottom.hide();
11665
11666             this.maskEl.right.setStyle('position', 'absolute');
11667             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11668             this.maskEl.right.hide();
11669             
11670             this.toolTip.hide();
11671             
11672             this.toolTip.el.hide();
11673             
11674             window.onwheel = function(){ return true;};
11675             
11676             if(this.intervalID){
11677                 window.clearInterval(this.intervalID);
11678                 this.intervalID = false;
11679             }
11680             
11681             this.isMasked = false;
11682             
11683         }
11684         
11685     }
11686     
11687 });
11688
11689 /*
11690  * Based on:
11691  * Ext JS Library 1.1.1
11692  * Copyright(c) 2006-2007, Ext JS, LLC.
11693  *
11694  * Originally Released Under LGPL - original licence link has changed is not relivant.
11695  *
11696  * Fork - LGPL
11697  * <script type="text/javascript">
11698  */
11699 /**
11700  * @class Roo.form.VTypes
11701  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11702  * @singleton
11703  */
11704 Roo.form.VTypes = function(){
11705     // closure these in so they are only created once.
11706     var alpha = /^[a-zA-Z_]+$/;
11707     var alphanum = /^[a-zA-Z0-9_]+$/;
11708     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11709     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11710
11711     // All these messages and functions are configurable
11712     return {
11713         /**
11714          * The function used to validate email addresses
11715          * @param {String} value The email address
11716          */
11717         'email' : function(v){
11718             return email.test(v);
11719         },
11720         /**
11721          * The error text to display when the email validation function returns false
11722          * @type String
11723          */
11724         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11725         /**
11726          * The keystroke filter mask to be applied on email input
11727          * @type RegExp
11728          */
11729         'emailMask' : /[a-z0-9_\.\-@]/i,
11730
11731         /**
11732          * The function used to validate URLs
11733          * @param {String} value The URL
11734          */
11735         'url' : function(v){
11736             return url.test(v);
11737         },
11738         /**
11739          * The error text to display when the url validation function returns false
11740          * @type String
11741          */
11742         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11743         
11744         /**
11745          * The function used to validate alpha values
11746          * @param {String} value The value
11747          */
11748         'alpha' : function(v){
11749             return alpha.test(v);
11750         },
11751         /**
11752          * The error text to display when the alpha validation function returns false
11753          * @type String
11754          */
11755         'alphaText' : 'This field should only contain letters and _',
11756         /**
11757          * The keystroke filter mask to be applied on alpha input
11758          * @type RegExp
11759          */
11760         'alphaMask' : /[a-z_]/i,
11761
11762         /**
11763          * The function used to validate alphanumeric values
11764          * @param {String} value The value
11765          */
11766         'alphanum' : function(v){
11767             return alphanum.test(v);
11768         },
11769         /**
11770          * The error text to display when the alphanumeric validation function returns false
11771          * @type String
11772          */
11773         'alphanumText' : 'This field should only contain letters, numbers and _',
11774         /**
11775          * The keystroke filter mask to be applied on alphanumeric input
11776          * @type RegExp
11777          */
11778         'alphanumMask' : /[a-z0-9_]/i
11779     };
11780 }();/*
11781  * - LGPL
11782  *
11783  * Input
11784  * 
11785  */
11786
11787 /**
11788  * @class Roo.bootstrap.Input
11789  * @extends Roo.bootstrap.Component
11790  * Bootstrap Input class
11791  * @cfg {Boolean} disabled is it disabled
11792  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11793  * @cfg {String} name name of the input
11794  * @cfg {string} fieldLabel - the label associated
11795  * @cfg {string} placeholder - placeholder to put in text.
11796  * @cfg {string}  before - input group add on before
11797  * @cfg {string} after - input group add on after
11798  * @cfg {string} size - (lg|sm) or leave empty..
11799  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11800  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11801  * @cfg {Number} md colspan out of 12 for computer-sized screens
11802  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11803  * @cfg {string} value default value of the input
11804  * @cfg {Number} labelWidth set the width of label 
11805  * @cfg {Number} labellg set the width of label (1-12)
11806  * @cfg {Number} labelmd set the width of label (1-12)
11807  * @cfg {Number} labelsm set the width of label (1-12)
11808  * @cfg {Number} labelxs set the width of label (1-12)
11809  * @cfg {String} labelAlign (top|left)
11810  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11811  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11812  * @cfg {String} indicatorpos (left|right) default left
11813  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11814  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11815  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11816
11817  * @cfg {String} align (left|center|right) Default left
11818  * @cfg {Boolean} forceFeedback (true|false) Default false
11819  * 
11820  * @constructor
11821  * Create a new Input
11822  * @param {Object} config The config object
11823  */
11824
11825 Roo.bootstrap.Input = function(config){
11826     
11827     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11828     
11829     this.addEvents({
11830         /**
11831          * @event focus
11832          * Fires when this field receives input focus.
11833          * @param {Roo.form.Field} this
11834          */
11835         focus : true,
11836         /**
11837          * @event blur
11838          * Fires when this field loses input focus.
11839          * @param {Roo.form.Field} this
11840          */
11841         blur : true,
11842         /**
11843          * @event specialkey
11844          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11845          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11846          * @param {Roo.form.Field} this
11847          * @param {Roo.EventObject} e The event object
11848          */
11849         specialkey : true,
11850         /**
11851          * @event change
11852          * Fires just before the field blurs if the field value has changed.
11853          * @param {Roo.form.Field} this
11854          * @param {Mixed} newValue The new value
11855          * @param {Mixed} oldValue The original value
11856          */
11857         change : true,
11858         /**
11859          * @event invalid
11860          * Fires after the field has been marked as invalid.
11861          * @param {Roo.form.Field} this
11862          * @param {String} msg The validation message
11863          */
11864         invalid : true,
11865         /**
11866          * @event valid
11867          * Fires after the field has been validated with no errors.
11868          * @param {Roo.form.Field} this
11869          */
11870         valid : true,
11871          /**
11872          * @event keyup
11873          * Fires after the key up
11874          * @param {Roo.form.Field} this
11875          * @param {Roo.EventObject}  e The event Object
11876          */
11877         keyup : true,
11878         /**
11879          * @event paste
11880          * Fires after the user pastes into input
11881          * @param {Roo.form.Field} this
11882          * @param {Roo.EventObject}  e The event Object
11883          */
11884         paste : true
11885     });
11886 };
11887
11888 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11889      /**
11890      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11891       automatic validation (defaults to "keyup").
11892      */
11893     validationEvent : "keyup",
11894      /**
11895      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11896      */
11897     validateOnBlur : true,
11898     /**
11899      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11900      */
11901     validationDelay : 250,
11902      /**
11903      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11904      */
11905     focusClass : "x-form-focus",  // not needed???
11906     
11907        
11908     /**
11909      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11910      */
11911     invalidClass : "has-warning",
11912     
11913     /**
11914      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11915      */
11916     validClass : "has-success",
11917     
11918     /**
11919      * @cfg {Boolean} hasFeedback (true|false) default true
11920      */
11921     hasFeedback : true,
11922     
11923     /**
11924      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11925      */
11926     invalidFeedbackClass : "glyphicon-warning-sign",
11927     
11928     /**
11929      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11930      */
11931     validFeedbackClass : "glyphicon-ok",
11932     
11933     /**
11934      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11935      */
11936     selectOnFocus : false,
11937     
11938      /**
11939      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11940      */
11941     maskRe : null,
11942        /**
11943      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11944      */
11945     vtype : null,
11946     
11947       /**
11948      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11949      */
11950     disableKeyFilter : false,
11951     
11952        /**
11953      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11954      */
11955     disabled : false,
11956      /**
11957      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11958      */
11959     allowBlank : true,
11960     /**
11961      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11962      */
11963     blankText : "Please complete this mandatory field",
11964     
11965      /**
11966      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11967      */
11968     minLength : 0,
11969     /**
11970      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11971      */
11972     maxLength : Number.MAX_VALUE,
11973     /**
11974      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11975      */
11976     minLengthText : "The minimum length for this field is {0}",
11977     /**
11978      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11979      */
11980     maxLengthText : "The maximum length for this field is {0}",
11981   
11982     
11983     /**
11984      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11985      * If available, this function will be called only after the basic validators all return true, and will be passed the
11986      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11987      */
11988     validator : null,
11989     /**
11990      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11991      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11992      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11993      */
11994     regex : null,
11995     /**
11996      * @cfg {String} regexText -- Depricated - use Invalid Text
11997      */
11998     regexText : "",
11999     
12000     /**
12001      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12002      */
12003     invalidText : "",
12004     
12005     
12006     
12007     autocomplete: false,
12008     
12009     
12010     fieldLabel : '',
12011     inputType : 'text',
12012     
12013     name : false,
12014     placeholder: false,
12015     before : false,
12016     after : false,
12017     size : false,
12018     hasFocus : false,
12019     preventMark: false,
12020     isFormField : true,
12021     value : '',
12022     labelWidth : 2,
12023     labelAlign : false,
12024     readOnly : false,
12025     align : false,
12026     formatedValue : false,
12027     forceFeedback : false,
12028     
12029     indicatorpos : 'left',
12030     
12031     labellg : 0,
12032     labelmd : 0,
12033     labelsm : 0,
12034     labelxs : 0,
12035     
12036     capture : '',
12037     accept : '',
12038     
12039     parentLabelAlign : function()
12040     {
12041         var parent = this;
12042         while (parent.parent()) {
12043             parent = parent.parent();
12044             if (typeof(parent.labelAlign) !='undefined') {
12045                 return parent.labelAlign;
12046             }
12047         }
12048         return 'left';
12049         
12050     },
12051     
12052     getAutoCreate : function()
12053     {
12054         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12055         
12056         var id = Roo.id();
12057         
12058         var cfg = {};
12059         
12060         if(this.inputType != 'hidden'){
12061             cfg.cls = 'form-group' //input-group
12062         }
12063         
12064         var input =  {
12065             tag: 'input',
12066             id : id,
12067             type : this.inputType,
12068             value : this.value,
12069             cls : 'form-control',
12070             placeholder : this.placeholder || '',
12071             autocomplete : this.autocomplete || 'new-password'
12072         };
12073         if (this.inputType == 'file') {
12074             input.style = 'overflow:hidden'; // why not in CSS?
12075         }
12076         
12077         if(this.capture.length){
12078             input.capture = this.capture;
12079         }
12080         
12081         if(this.accept.length){
12082             input.accept = this.accept + "/*";
12083         }
12084         
12085         if(this.align){
12086             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12087         }
12088         
12089         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12090             input.maxLength = this.maxLength;
12091         }
12092         
12093         if (this.disabled) {
12094             input.disabled=true;
12095         }
12096         
12097         if (this.readOnly) {
12098             input.readonly=true;
12099         }
12100         
12101         if (this.name) {
12102             input.name = this.name;
12103         }
12104         
12105         if (this.size) {
12106             input.cls += ' input-' + this.size;
12107         }
12108         
12109         var settings=this;
12110         ['xs','sm','md','lg'].map(function(size){
12111             if (settings[size]) {
12112                 cfg.cls += ' col-' + size + '-' + settings[size];
12113             }
12114         });
12115         
12116         var inputblock = input;
12117         
12118         var feedback = {
12119             tag: 'span',
12120             cls: 'glyphicon form-control-feedback'
12121         };
12122             
12123         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12124             
12125             inputblock = {
12126                 cls : 'has-feedback',
12127                 cn :  [
12128                     input,
12129                     feedback
12130                 ] 
12131             };  
12132         }
12133         
12134         if (this.before || this.after) {
12135             
12136             inputblock = {
12137                 cls : 'input-group',
12138                 cn :  [] 
12139             };
12140             
12141             if (this.before && typeof(this.before) == 'string') {
12142                 
12143                 inputblock.cn.push({
12144                     tag :'span',
12145                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12146                     html : this.before
12147                 });
12148             }
12149             if (this.before && typeof(this.before) == 'object') {
12150                 this.before = Roo.factory(this.before);
12151                 
12152                 inputblock.cn.push({
12153                     tag :'span',
12154                     cls : 'roo-input-before input-group-prepend   input-group-' +
12155                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12156                 });
12157             }
12158             
12159             inputblock.cn.push(input);
12160             
12161             if (this.after && typeof(this.after) == 'string') {
12162                 inputblock.cn.push({
12163                     tag :'span',
12164                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12165                     html : this.after
12166                 });
12167             }
12168             if (this.after && typeof(this.after) == 'object') {
12169                 this.after = Roo.factory(this.after);
12170                 
12171                 inputblock.cn.push({
12172                     tag :'span',
12173                     cls : 'roo-input-after input-group-append  input-group-' +
12174                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12175                 });
12176             }
12177             
12178             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12179                 inputblock.cls += ' has-feedback';
12180                 inputblock.cn.push(feedback);
12181             }
12182         };
12183         var indicator = {
12184             tag : 'i',
12185             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12186             tooltip : 'This field is required'
12187         };
12188         if (this.allowBlank ) {
12189             indicator.style = this.allowBlank ? ' display:none' : '';
12190         }
12191         if (align ==='left' && this.fieldLabel.length) {
12192             
12193             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12194             
12195             cfg.cn = [
12196                 indicator,
12197                 {
12198                     tag: 'label',
12199                     'for' :  id,
12200                     cls : 'control-label col-form-label',
12201                     html : this.fieldLabel
12202
12203                 },
12204                 {
12205                     cls : "", 
12206                     cn: [
12207                         inputblock
12208                     ]
12209                 }
12210             ];
12211             
12212             var labelCfg = cfg.cn[1];
12213             var contentCfg = cfg.cn[2];
12214             
12215             if(this.indicatorpos == 'right'){
12216                 cfg.cn = [
12217                     {
12218                         tag: 'label',
12219                         'for' :  id,
12220                         cls : 'control-label col-form-label',
12221                         cn : [
12222                             {
12223                                 tag : 'span',
12224                                 html : this.fieldLabel
12225                             },
12226                             indicator
12227                         ]
12228                     },
12229                     {
12230                         cls : "",
12231                         cn: [
12232                             inputblock
12233                         ]
12234                     }
12235
12236                 ];
12237                 
12238                 labelCfg = cfg.cn[0];
12239                 contentCfg = cfg.cn[1];
12240             
12241             }
12242             
12243             if(this.labelWidth > 12){
12244                 labelCfg.style = "width: " + this.labelWidth + 'px';
12245             }
12246             
12247             if(this.labelWidth < 13 && this.labelmd == 0){
12248                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12249             }
12250             
12251             if(this.labellg > 0){
12252                 labelCfg.cls += ' col-lg-' + this.labellg;
12253                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12254             }
12255             
12256             if(this.labelmd > 0){
12257                 labelCfg.cls += ' col-md-' + this.labelmd;
12258                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12259             }
12260             
12261             if(this.labelsm > 0){
12262                 labelCfg.cls += ' col-sm-' + this.labelsm;
12263                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12264             }
12265             
12266             if(this.labelxs > 0){
12267                 labelCfg.cls += ' col-xs-' + this.labelxs;
12268                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12269             }
12270             
12271             
12272         } else if ( this.fieldLabel.length) {
12273                 
12274             
12275             
12276             cfg.cn = [
12277                 {
12278                     tag : 'i',
12279                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12280                     tooltip : 'This field is required',
12281                     style : this.allowBlank ? ' display:none' : '' 
12282                 },
12283                 {
12284                     tag: 'label',
12285                    //cls : 'input-group-addon',
12286                     html : this.fieldLabel
12287
12288                 },
12289
12290                inputblock
12291
12292            ];
12293            
12294            if(this.indicatorpos == 'right'){
12295        
12296                 cfg.cn = [
12297                     {
12298                         tag: 'label',
12299                        //cls : 'input-group-addon',
12300                         html : this.fieldLabel
12301
12302                     },
12303                     {
12304                         tag : 'i',
12305                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12306                         tooltip : 'This field is required',
12307                         style : this.allowBlank ? ' display:none' : '' 
12308                     },
12309
12310                    inputblock
12311
12312                ];
12313
12314             }
12315
12316         } else {
12317             
12318             cfg.cn = [
12319
12320                     inputblock
12321
12322             ];
12323                 
12324                 
12325         };
12326         
12327         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12328            cfg.cls += ' navbar-form';
12329         }
12330         
12331         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12332             // on BS4 we do this only if not form 
12333             cfg.cls += ' navbar-form';
12334             cfg.tag = 'li';
12335         }
12336         
12337         return cfg;
12338         
12339     },
12340     /**
12341      * return the real input element.
12342      */
12343     inputEl: function ()
12344     {
12345         return this.el.select('input.form-control',true).first();
12346     },
12347     
12348     tooltipEl : function()
12349     {
12350         return this.inputEl();
12351     },
12352     
12353     indicatorEl : function()
12354     {
12355         if (Roo.bootstrap.version == 4) {
12356             return false; // not enabled in v4 yet.
12357         }
12358         
12359         var indicator = this.el.select('i.roo-required-indicator',true).first();
12360         
12361         if(!indicator){
12362             return false;
12363         }
12364         
12365         return indicator;
12366         
12367     },
12368     
12369     setDisabled : function(v)
12370     {
12371         var i  = this.inputEl().dom;
12372         if (!v) {
12373             i.removeAttribute('disabled');
12374             return;
12375             
12376         }
12377         i.setAttribute('disabled','true');
12378     },
12379     initEvents : function()
12380     {
12381           
12382         this.inputEl().on("keydown" , this.fireKey,  this);
12383         this.inputEl().on("focus", this.onFocus,  this);
12384         this.inputEl().on("blur", this.onBlur,  this);
12385         
12386         this.inputEl().relayEvent('keyup', this);
12387         this.inputEl().relayEvent('paste', this);
12388         
12389         this.indicator = this.indicatorEl();
12390         
12391         if(this.indicator){
12392             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12393         }
12394  
12395         // reference to original value for reset
12396         this.originalValue = this.getValue();
12397         //Roo.form.TextField.superclass.initEvents.call(this);
12398         if(this.validationEvent == 'keyup'){
12399             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12400             this.inputEl().on('keyup', this.filterValidation, this);
12401         }
12402         else if(this.validationEvent !== false){
12403             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12404         }
12405         
12406         if(this.selectOnFocus){
12407             this.on("focus", this.preFocus, this);
12408             
12409         }
12410         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12411             this.inputEl().on("keypress", this.filterKeys, this);
12412         } else {
12413             this.inputEl().relayEvent('keypress', this);
12414         }
12415        /* if(this.grow){
12416             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12417             this.el.on("click", this.autoSize,  this);
12418         }
12419         */
12420         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12421             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12422         }
12423         
12424         if (typeof(this.before) == 'object') {
12425             this.before.render(this.el.select('.roo-input-before',true).first());
12426         }
12427         if (typeof(this.after) == 'object') {
12428             this.after.render(this.el.select('.roo-input-after',true).first());
12429         }
12430         
12431         this.inputEl().on('change', this.onChange, this);
12432         
12433     },
12434     filterValidation : function(e){
12435         if(!e.isNavKeyPress()){
12436             this.validationTask.delay(this.validationDelay);
12437         }
12438     },
12439      /**
12440      * Validates the field value
12441      * @return {Boolean} True if the value is valid, else false
12442      */
12443     validate : function(){
12444         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12445         if(this.disabled || this.validateValue(this.getRawValue())){
12446             this.markValid();
12447             return true;
12448         }
12449         
12450         this.markInvalid();
12451         return false;
12452     },
12453     
12454     
12455     /**
12456      * Validates a value according to the field's validation rules and marks the field as invalid
12457      * if the validation fails
12458      * @param {Mixed} value The value to validate
12459      * @return {Boolean} True if the value is valid, else false
12460      */
12461     validateValue : function(value)
12462     {
12463         if(this.getVisibilityEl().hasClass('hidden')){
12464             return true;
12465         }
12466         
12467         if(value.length < 1)  { // if it's blank
12468             if(this.allowBlank){
12469                 return true;
12470             }
12471             return false;
12472         }
12473         
12474         if(value.length < this.minLength){
12475             return false;
12476         }
12477         if(value.length > this.maxLength){
12478             return false;
12479         }
12480         if(this.vtype){
12481             var vt = Roo.form.VTypes;
12482             if(!vt[this.vtype](value, this)){
12483                 return false;
12484             }
12485         }
12486         if(typeof this.validator == "function"){
12487             var msg = this.validator(value);
12488             if(msg !== true){
12489                 return false;
12490             }
12491             if (typeof(msg) == 'string') {
12492                 this.invalidText = msg;
12493             }
12494         }
12495         
12496         if(this.regex && !this.regex.test(value)){
12497             return false;
12498         }
12499         
12500         return true;
12501     },
12502     
12503      // private
12504     fireKey : function(e){
12505         //Roo.log('field ' + e.getKey());
12506         if(e.isNavKeyPress()){
12507             this.fireEvent("specialkey", this, e);
12508         }
12509     },
12510     focus : function (selectText){
12511         if(this.rendered){
12512             this.inputEl().focus();
12513             if(selectText === true){
12514                 this.inputEl().dom.select();
12515             }
12516         }
12517         return this;
12518     } ,
12519     
12520     onFocus : function(){
12521         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12522            // this.el.addClass(this.focusClass);
12523         }
12524         if(!this.hasFocus){
12525             this.hasFocus = true;
12526             this.startValue = this.getValue();
12527             this.fireEvent("focus", this);
12528         }
12529     },
12530     
12531     beforeBlur : Roo.emptyFn,
12532
12533     
12534     // private
12535     onBlur : function(){
12536         this.beforeBlur();
12537         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12538             //this.el.removeClass(this.focusClass);
12539         }
12540         this.hasFocus = false;
12541         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12542             this.validate();
12543         }
12544         var v = this.getValue();
12545         if(String(v) !== String(this.startValue)){
12546             this.fireEvent('change', this, v, this.startValue);
12547         }
12548         this.fireEvent("blur", this);
12549     },
12550     
12551     onChange : function(e)
12552     {
12553         var v = this.getValue();
12554         if(String(v) !== String(this.startValue)){
12555             this.fireEvent('change', this, v, this.startValue);
12556         }
12557         
12558     },
12559     
12560     /**
12561      * Resets the current field value to the originally loaded value and clears any validation messages
12562      */
12563     reset : function(){
12564         this.setValue(this.originalValue);
12565         this.validate();
12566     },
12567      /**
12568      * Returns the name of the field
12569      * @return {Mixed} name The name field
12570      */
12571     getName: function(){
12572         return this.name;
12573     },
12574      /**
12575      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12576      * @return {Mixed} value The field value
12577      */
12578     getValue : function(){
12579         
12580         var v = this.inputEl().getValue();
12581         
12582         return v;
12583     },
12584     /**
12585      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12586      * @return {Mixed} value The field value
12587      */
12588     getRawValue : function(){
12589         var v = this.inputEl().getValue();
12590         
12591         return v;
12592     },
12593     
12594     /**
12595      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12596      * @param {Mixed} value The value to set
12597      */
12598     setRawValue : function(v){
12599         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12600     },
12601     
12602     selectText : function(start, end){
12603         var v = this.getRawValue();
12604         if(v.length > 0){
12605             start = start === undefined ? 0 : start;
12606             end = end === undefined ? v.length : end;
12607             var d = this.inputEl().dom;
12608             if(d.setSelectionRange){
12609                 d.setSelectionRange(start, end);
12610             }else if(d.createTextRange){
12611                 var range = d.createTextRange();
12612                 range.moveStart("character", start);
12613                 range.moveEnd("character", v.length-end);
12614                 range.select();
12615             }
12616         }
12617     },
12618     
12619     /**
12620      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12621      * @param {Mixed} value The value to set
12622      */
12623     setValue : function(v){
12624         this.value = v;
12625         if(this.rendered){
12626             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12627             this.validate();
12628         }
12629     },
12630     
12631     /*
12632     processValue : function(value){
12633         if(this.stripCharsRe){
12634             var newValue = value.replace(this.stripCharsRe, '');
12635             if(newValue !== value){
12636                 this.setRawValue(newValue);
12637                 return newValue;
12638             }
12639         }
12640         return value;
12641     },
12642   */
12643     preFocus : function(){
12644         
12645         if(this.selectOnFocus){
12646             this.inputEl().dom.select();
12647         }
12648     },
12649     filterKeys : function(e){
12650         var k = e.getKey();
12651         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12652             return;
12653         }
12654         var c = e.getCharCode(), cc = String.fromCharCode(c);
12655         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12656             return;
12657         }
12658         if(!this.maskRe.test(cc)){
12659             e.stopEvent();
12660         }
12661     },
12662      /**
12663      * Clear any invalid styles/messages for this field
12664      */
12665     clearInvalid : function(){
12666         
12667         if(!this.el || this.preventMark){ // not rendered
12668             return;
12669         }
12670         
12671         
12672         this.el.removeClass([this.invalidClass, 'is-invalid']);
12673         
12674         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12675             
12676             var feedback = this.el.select('.form-control-feedback', true).first();
12677             
12678             if(feedback){
12679                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12680             }
12681             
12682         }
12683         
12684         if(this.indicator){
12685             this.indicator.removeClass('visible');
12686             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12687         }
12688         
12689         this.fireEvent('valid', this);
12690     },
12691     
12692      /**
12693      * Mark this field as valid
12694      */
12695     markValid : function()
12696     {
12697         if(!this.el  || this.preventMark){ // not rendered...
12698             return;
12699         }
12700         
12701         this.el.removeClass([this.invalidClass, this.validClass]);
12702         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12703
12704         var feedback = this.el.select('.form-control-feedback', true).first();
12705             
12706         if(feedback){
12707             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12708         }
12709         
12710         if(this.indicator){
12711             this.indicator.removeClass('visible');
12712             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12713         }
12714         
12715         if(this.disabled){
12716             return;
12717         }
12718         
12719            
12720         if(this.allowBlank && !this.getRawValue().length){
12721             return;
12722         }
12723         if (Roo.bootstrap.version == 3) {
12724             this.el.addClass(this.validClass);
12725         } else {
12726             this.inputEl().addClass('is-valid');
12727         }
12728
12729         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12730             
12731             var feedback = this.el.select('.form-control-feedback', true).first();
12732             
12733             if(feedback){
12734                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12735                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12736             }
12737             
12738         }
12739         
12740         this.fireEvent('valid', this);
12741     },
12742     
12743      /**
12744      * Mark this field as invalid
12745      * @param {String} msg The validation message
12746      */
12747     markInvalid : function(msg)
12748     {
12749         if(!this.el  || this.preventMark){ // not rendered
12750             return;
12751         }
12752         
12753         this.el.removeClass([this.invalidClass, this.validClass]);
12754         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12755         
12756         var feedback = this.el.select('.form-control-feedback', true).first();
12757             
12758         if(feedback){
12759             this.el.select('.form-control-feedback', true).first().removeClass(
12760                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12761         }
12762
12763         if(this.disabled){
12764             return;
12765         }
12766         
12767         if(this.allowBlank && !this.getRawValue().length){
12768             return;
12769         }
12770         
12771         if(this.indicator){
12772             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12773             this.indicator.addClass('visible');
12774         }
12775         if (Roo.bootstrap.version == 3) {
12776             this.el.addClass(this.invalidClass);
12777         } else {
12778             this.inputEl().addClass('is-invalid');
12779         }
12780         
12781         
12782         
12783         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12784             
12785             var feedback = this.el.select('.form-control-feedback', true).first();
12786             
12787             if(feedback){
12788                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12789                 
12790                 if(this.getValue().length || this.forceFeedback){
12791                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12792                 }
12793                 
12794             }
12795             
12796         }
12797         
12798         this.fireEvent('invalid', this, msg);
12799     },
12800     // private
12801     SafariOnKeyDown : function(event)
12802     {
12803         // this is a workaround for a password hang bug on chrome/ webkit.
12804         if (this.inputEl().dom.type != 'password') {
12805             return;
12806         }
12807         
12808         var isSelectAll = false;
12809         
12810         if(this.inputEl().dom.selectionEnd > 0){
12811             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12812         }
12813         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12814             event.preventDefault();
12815             this.setValue('');
12816             return;
12817         }
12818         
12819         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12820             
12821             event.preventDefault();
12822             // this is very hacky as keydown always get's upper case.
12823             //
12824             var cc = String.fromCharCode(event.getCharCode());
12825             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12826             
12827         }
12828     },
12829     adjustWidth : function(tag, w){
12830         tag = tag.toLowerCase();
12831         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12832             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12833                 if(tag == 'input'){
12834                     return w + 2;
12835                 }
12836                 if(tag == 'textarea'){
12837                     return w-2;
12838                 }
12839             }else if(Roo.isOpera){
12840                 if(tag == 'input'){
12841                     return w + 2;
12842                 }
12843                 if(tag == 'textarea'){
12844                     return w-2;
12845                 }
12846             }
12847         }
12848         return w;
12849     },
12850     
12851     setFieldLabel : function(v)
12852     {
12853         if(!this.rendered){
12854             return;
12855         }
12856         
12857         if(this.indicatorEl()){
12858             var ar = this.el.select('label > span',true);
12859             
12860             if (ar.elements.length) {
12861                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12862                 this.fieldLabel = v;
12863                 return;
12864             }
12865             
12866             var br = this.el.select('label',true);
12867             
12868             if(br.elements.length) {
12869                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12870                 this.fieldLabel = v;
12871                 return;
12872             }
12873             
12874             Roo.log('Cannot Found any of label > span || label in input');
12875             return;
12876         }
12877         
12878         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12879         this.fieldLabel = v;
12880         
12881         
12882     }
12883 });
12884
12885  
12886 /*
12887  * - LGPL
12888  *
12889  * Input
12890  * 
12891  */
12892
12893 /**
12894  * @class Roo.bootstrap.TextArea
12895  * @extends Roo.bootstrap.Input
12896  * Bootstrap TextArea class
12897  * @cfg {Number} cols Specifies the visible width of a text area
12898  * @cfg {Number} rows Specifies the visible number of lines in a text area
12899  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12900  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12901  * @cfg {string} html text
12902  * 
12903  * @constructor
12904  * Create a new TextArea
12905  * @param {Object} config The config object
12906  */
12907
12908 Roo.bootstrap.TextArea = function(config){
12909     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12910    
12911 };
12912
12913 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12914      
12915     cols : false,
12916     rows : 5,
12917     readOnly : false,
12918     warp : 'soft',
12919     resize : false,
12920     value: false,
12921     html: false,
12922     
12923     getAutoCreate : function(){
12924         
12925         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12926         
12927         var id = Roo.id();
12928         
12929         var cfg = {};
12930         
12931         if(this.inputType != 'hidden'){
12932             cfg.cls = 'form-group' //input-group
12933         }
12934         
12935         var input =  {
12936             tag: 'textarea',
12937             id : id,
12938             warp : this.warp,
12939             rows : this.rows,
12940             value : this.value || '',
12941             html: this.html || '',
12942             cls : 'form-control',
12943             placeholder : this.placeholder || '' 
12944             
12945         };
12946         
12947         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12948             input.maxLength = this.maxLength;
12949         }
12950         
12951         if(this.resize){
12952             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12953         }
12954         
12955         if(this.cols){
12956             input.cols = this.cols;
12957         }
12958         
12959         if (this.readOnly) {
12960             input.readonly = true;
12961         }
12962         
12963         if (this.name) {
12964             input.name = this.name;
12965         }
12966         
12967         if (this.size) {
12968             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12969         }
12970         
12971         var settings=this;
12972         ['xs','sm','md','lg'].map(function(size){
12973             if (settings[size]) {
12974                 cfg.cls += ' col-' + size + '-' + settings[size];
12975             }
12976         });
12977         
12978         var inputblock = input;
12979         
12980         if(this.hasFeedback && !this.allowBlank){
12981             
12982             var feedback = {
12983                 tag: 'span',
12984                 cls: 'glyphicon form-control-feedback'
12985             };
12986
12987             inputblock = {
12988                 cls : 'has-feedback',
12989                 cn :  [
12990                     input,
12991                     feedback
12992                 ] 
12993             };  
12994         }
12995         
12996         
12997         if (this.before || this.after) {
12998             
12999             inputblock = {
13000                 cls : 'input-group',
13001                 cn :  [] 
13002             };
13003             if (this.before) {
13004                 inputblock.cn.push({
13005                     tag :'span',
13006                     cls : 'input-group-addon',
13007                     html : this.before
13008                 });
13009             }
13010             
13011             inputblock.cn.push(input);
13012             
13013             if(this.hasFeedback && !this.allowBlank){
13014                 inputblock.cls += ' has-feedback';
13015                 inputblock.cn.push(feedback);
13016             }
13017             
13018             if (this.after) {
13019                 inputblock.cn.push({
13020                     tag :'span',
13021                     cls : 'input-group-addon',
13022                     html : this.after
13023                 });
13024             }
13025             
13026         }
13027         
13028         if (align ==='left' && this.fieldLabel.length) {
13029             cfg.cn = [
13030                 {
13031                     tag: 'label',
13032                     'for' :  id,
13033                     cls : 'control-label',
13034                     html : this.fieldLabel
13035                 },
13036                 {
13037                     cls : "",
13038                     cn: [
13039                         inputblock
13040                     ]
13041                 }
13042
13043             ];
13044             
13045             if(this.labelWidth > 12){
13046                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13047             }
13048
13049             if(this.labelWidth < 13 && this.labelmd == 0){
13050                 this.labelmd = this.labelWidth;
13051             }
13052
13053             if(this.labellg > 0){
13054                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13055                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13056             }
13057
13058             if(this.labelmd > 0){
13059                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13060                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13061             }
13062
13063             if(this.labelsm > 0){
13064                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13065                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13066             }
13067
13068             if(this.labelxs > 0){
13069                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13070                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13071             }
13072             
13073         } else if ( this.fieldLabel.length) {
13074             cfg.cn = [
13075
13076                {
13077                    tag: 'label',
13078                    //cls : 'input-group-addon',
13079                    html : this.fieldLabel
13080
13081                },
13082
13083                inputblock
13084
13085            ];
13086
13087         } else {
13088
13089             cfg.cn = [
13090
13091                 inputblock
13092
13093             ];
13094                 
13095         }
13096         
13097         if (this.disabled) {
13098             input.disabled=true;
13099         }
13100         
13101         return cfg;
13102         
13103     },
13104     /**
13105      * return the real textarea element.
13106      */
13107     inputEl: function ()
13108     {
13109         return this.el.select('textarea.form-control',true).first();
13110     },
13111     
13112     /**
13113      * Clear any invalid styles/messages for this field
13114      */
13115     clearInvalid : function()
13116     {
13117         
13118         if(!this.el || this.preventMark){ // not rendered
13119             return;
13120         }
13121         
13122         var label = this.el.select('label', true).first();
13123         var icon = this.el.select('i.fa-star', true).first();
13124         
13125         if(label && icon){
13126             icon.remove();
13127         }
13128         this.el.removeClass( this.validClass);
13129         this.inputEl().removeClass('is-invalid');
13130          
13131         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13132             
13133             var feedback = this.el.select('.form-control-feedback', true).first();
13134             
13135             if(feedback){
13136                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13137             }
13138             
13139         }
13140         
13141         this.fireEvent('valid', this);
13142     },
13143     
13144      /**
13145      * Mark this field as valid
13146      */
13147     markValid : function()
13148     {
13149         if(!this.el  || this.preventMark){ // not rendered
13150             return;
13151         }
13152         
13153         this.el.removeClass([this.invalidClass, this.validClass]);
13154         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13155         
13156         var feedback = this.el.select('.form-control-feedback', true).first();
13157             
13158         if(feedback){
13159             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13160         }
13161
13162         if(this.disabled || this.allowBlank){
13163             return;
13164         }
13165         
13166         var label = this.el.select('label', true).first();
13167         var icon = this.el.select('i.fa-star', true).first();
13168         
13169         if(label && icon){
13170             icon.remove();
13171         }
13172         if (Roo.bootstrap.version == 3) {
13173             this.el.addClass(this.validClass);
13174         } else {
13175             this.inputEl().addClass('is-valid');
13176         }
13177         
13178         
13179         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13180             
13181             var feedback = this.el.select('.form-control-feedback', true).first();
13182             
13183             if(feedback){
13184                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13185                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13186             }
13187             
13188         }
13189         
13190         this.fireEvent('valid', this);
13191     },
13192     
13193      /**
13194      * Mark this field as invalid
13195      * @param {String} msg The validation message
13196      */
13197     markInvalid : function(msg)
13198     {
13199         if(!this.el  || this.preventMark){ // not rendered
13200             return;
13201         }
13202         
13203         this.el.removeClass([this.invalidClass, this.validClass]);
13204         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13205         
13206         var feedback = this.el.select('.form-control-feedback', true).first();
13207             
13208         if(feedback){
13209             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13210         }
13211
13212         if(this.disabled || this.allowBlank){
13213             return;
13214         }
13215         
13216         var label = this.el.select('label', true).first();
13217         var icon = this.el.select('i.fa-star', true).first();
13218         
13219         if(!this.getValue().length && label && !icon){
13220             this.el.createChild({
13221                 tag : 'i',
13222                 cls : 'text-danger fa fa-lg fa-star',
13223                 tooltip : 'This field is required',
13224                 style : 'margin-right:5px;'
13225             }, label, true);
13226         }
13227         
13228         if (Roo.bootstrap.version == 3) {
13229             this.el.addClass(this.invalidClass);
13230         } else {
13231             this.inputEl().addClass('is-invalid');
13232         }
13233         
13234         // fixme ... this may be depricated need to test..
13235         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13236             
13237             var feedback = this.el.select('.form-control-feedback', true).first();
13238             
13239             if(feedback){
13240                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13241                 
13242                 if(this.getValue().length || this.forceFeedback){
13243                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13244                 }
13245                 
13246             }
13247             
13248         }
13249         
13250         this.fireEvent('invalid', this, msg);
13251     }
13252 });
13253
13254  
13255 /*
13256  * - LGPL
13257  *
13258  * trigger field - base class for combo..
13259  * 
13260  */
13261  
13262 /**
13263  * @class Roo.bootstrap.TriggerField
13264  * @extends Roo.bootstrap.Input
13265  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13266  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13267  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13268  * for which you can provide a custom implementation.  For example:
13269  * <pre><code>
13270 var trigger = new Roo.bootstrap.TriggerField();
13271 trigger.onTriggerClick = myTriggerFn;
13272 trigger.applyTo('my-field');
13273 </code></pre>
13274  *
13275  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13276  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13277  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13278  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13279  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13280
13281  * @constructor
13282  * Create a new TriggerField.
13283  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13284  * to the base TextField)
13285  */
13286 Roo.bootstrap.TriggerField = function(config){
13287     this.mimicing = false;
13288     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13289 };
13290
13291 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13292     /**
13293      * @cfg {String} triggerClass A CSS class to apply to the trigger
13294      */
13295      /**
13296      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13297      */
13298     hideTrigger:false,
13299
13300     /**
13301      * @cfg {Boolean} removable (true|false) special filter default false
13302      */
13303     removable : false,
13304     
13305     /** @cfg {Boolean} grow @hide */
13306     /** @cfg {Number} growMin @hide */
13307     /** @cfg {Number} growMax @hide */
13308
13309     /**
13310      * @hide 
13311      * @method
13312      */
13313     autoSize: Roo.emptyFn,
13314     // private
13315     monitorTab : true,
13316     // private
13317     deferHeight : true,
13318
13319     
13320     actionMode : 'wrap',
13321     
13322     caret : false,
13323     
13324     
13325     getAutoCreate : function(){
13326        
13327         var align = this.labelAlign || this.parentLabelAlign();
13328         
13329         var id = Roo.id();
13330         
13331         var cfg = {
13332             cls: 'form-group' //input-group
13333         };
13334         
13335         
13336         var input =  {
13337             tag: 'input',
13338             id : id,
13339             type : this.inputType,
13340             cls : 'form-control',
13341             autocomplete: 'new-password',
13342             placeholder : this.placeholder || '' 
13343             
13344         };
13345         if (this.name) {
13346             input.name = this.name;
13347         }
13348         if (this.size) {
13349             input.cls += ' input-' + this.size;
13350         }
13351         
13352         if (this.disabled) {
13353             input.disabled=true;
13354         }
13355         
13356         var inputblock = input;
13357         
13358         if(this.hasFeedback && !this.allowBlank){
13359             
13360             var feedback = {
13361                 tag: 'span',
13362                 cls: 'glyphicon form-control-feedback'
13363             };
13364             
13365             if(this.removable && !this.editable  ){
13366                 inputblock = {
13367                     cls : 'has-feedback',
13368                     cn :  [
13369                         inputblock,
13370                         {
13371                             tag: 'button',
13372                             html : 'x',
13373                             cls : 'roo-combo-removable-btn close'
13374                         },
13375                         feedback
13376                     ] 
13377                 };
13378             } else {
13379                 inputblock = {
13380                     cls : 'has-feedback',
13381                     cn :  [
13382                         inputblock,
13383                         feedback
13384                     ] 
13385                 };
13386             }
13387
13388         } else {
13389             if(this.removable && !this.editable ){
13390                 inputblock = {
13391                     cls : 'roo-removable',
13392                     cn :  [
13393                         inputblock,
13394                         {
13395                             tag: 'button',
13396                             html : 'x',
13397                             cls : 'roo-combo-removable-btn close'
13398                         }
13399                     ] 
13400                 };
13401             }
13402         }
13403         
13404         if (this.before || this.after) {
13405             
13406             inputblock = {
13407                 cls : 'input-group',
13408                 cn :  [] 
13409             };
13410             if (this.before) {
13411                 inputblock.cn.push({
13412                     tag :'span',
13413                     cls : 'input-group-addon input-group-prepend input-group-text',
13414                     html : this.before
13415                 });
13416             }
13417             
13418             inputblock.cn.push(input);
13419             
13420             if(this.hasFeedback && !this.allowBlank){
13421                 inputblock.cls += ' has-feedback';
13422                 inputblock.cn.push(feedback);
13423             }
13424             
13425             if (this.after) {
13426                 inputblock.cn.push({
13427                     tag :'span',
13428                     cls : 'input-group-addon input-group-append input-group-text',
13429                     html : this.after
13430                 });
13431             }
13432             
13433         };
13434         
13435       
13436         
13437         var ibwrap = inputblock;
13438         
13439         if(this.multiple){
13440             ibwrap = {
13441                 tag: 'ul',
13442                 cls: 'roo-select2-choices',
13443                 cn:[
13444                     {
13445                         tag: 'li',
13446                         cls: 'roo-select2-search-field',
13447                         cn: [
13448
13449                             inputblock
13450                         ]
13451                     }
13452                 ]
13453             };
13454                 
13455         }
13456         
13457         var combobox = {
13458             cls: 'roo-select2-container input-group',
13459             cn: [
13460                  {
13461                     tag: 'input',
13462                     type : 'hidden',
13463                     cls: 'form-hidden-field'
13464                 },
13465                 ibwrap
13466             ]
13467         };
13468         
13469         if(!this.multiple && this.showToggleBtn){
13470             
13471             var caret = {
13472                         tag: 'span',
13473                         cls: 'caret'
13474              };
13475             if (this.caret != false) {
13476                 caret = {
13477                      tag: 'i',
13478                      cls: 'fa fa-' + this.caret
13479                 };
13480                 
13481             }
13482             
13483             combobox.cn.push({
13484                 tag :'span',
13485                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13486                 cn : [
13487                     Roo.bootstrap.version == 3 ? caret : '',
13488                     {
13489                         tag: 'span',
13490                         cls: 'combobox-clear',
13491                         cn  : [
13492                             {
13493                                 tag : 'i',
13494                                 cls: 'icon-remove'
13495                             }
13496                         ]
13497                     }
13498                 ]
13499
13500             })
13501         }
13502         
13503         if(this.multiple){
13504             combobox.cls += ' roo-select2-container-multi';
13505         }
13506          var indicator = {
13507             tag : 'i',
13508             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13509             tooltip : 'This field is required'
13510         };
13511         if (Roo.bootstrap.version == 4) {
13512             indicator = {
13513                 tag : 'i',
13514                 style : 'display:none'
13515             };
13516         }
13517         
13518         
13519         if (align ==='left' && this.fieldLabel.length) {
13520             
13521             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13522
13523             cfg.cn = [
13524                 indicator,
13525                 {
13526                     tag: 'label',
13527                     'for' :  id,
13528                     cls : 'control-label',
13529                     html : this.fieldLabel
13530
13531                 },
13532                 {
13533                     cls : "", 
13534                     cn: [
13535                         combobox
13536                     ]
13537                 }
13538
13539             ];
13540             
13541             var labelCfg = cfg.cn[1];
13542             var contentCfg = cfg.cn[2];
13543             
13544             if(this.indicatorpos == 'right'){
13545                 cfg.cn = [
13546                     {
13547                         tag: 'label',
13548                         'for' :  id,
13549                         cls : 'control-label',
13550                         cn : [
13551                             {
13552                                 tag : 'span',
13553                                 html : this.fieldLabel
13554                             },
13555                             indicator
13556                         ]
13557                     },
13558                     {
13559                         cls : "", 
13560                         cn: [
13561                             combobox
13562                         ]
13563                     }
13564
13565                 ];
13566                 
13567                 labelCfg = cfg.cn[0];
13568                 contentCfg = cfg.cn[1];
13569             }
13570             
13571             if(this.labelWidth > 12){
13572                 labelCfg.style = "width: " + this.labelWidth + 'px';
13573             }
13574             
13575             if(this.labelWidth < 13 && this.labelmd == 0){
13576                 this.labelmd = this.labelWidth;
13577             }
13578             
13579             if(this.labellg > 0){
13580                 labelCfg.cls += ' col-lg-' + this.labellg;
13581                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13582             }
13583             
13584             if(this.labelmd > 0){
13585                 labelCfg.cls += ' col-md-' + this.labelmd;
13586                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13587             }
13588             
13589             if(this.labelsm > 0){
13590                 labelCfg.cls += ' col-sm-' + this.labelsm;
13591                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13592             }
13593             
13594             if(this.labelxs > 0){
13595                 labelCfg.cls += ' col-xs-' + this.labelxs;
13596                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13597             }
13598             
13599         } else if ( this.fieldLabel.length) {
13600 //                Roo.log(" label");
13601             cfg.cn = [
13602                 indicator,
13603                {
13604                    tag: 'label',
13605                    //cls : 'input-group-addon',
13606                    html : this.fieldLabel
13607
13608                },
13609
13610                combobox
13611
13612             ];
13613             
13614             if(this.indicatorpos == 'right'){
13615                 
13616                 cfg.cn = [
13617                     {
13618                        tag: 'label',
13619                        cn : [
13620                            {
13621                                tag : 'span',
13622                                html : this.fieldLabel
13623                            },
13624                            indicator
13625                        ]
13626
13627                     },
13628                     combobox
13629
13630                 ];
13631
13632             }
13633
13634         } else {
13635             
13636 //                Roo.log(" no label && no align");
13637                 cfg = combobox
13638                      
13639                 
13640         }
13641         
13642         var settings=this;
13643         ['xs','sm','md','lg'].map(function(size){
13644             if (settings[size]) {
13645                 cfg.cls += ' col-' + size + '-' + settings[size];
13646             }
13647         });
13648         
13649         return cfg;
13650         
13651     },
13652     
13653     
13654     
13655     // private
13656     onResize : function(w, h){
13657 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13658 //        if(typeof w == 'number'){
13659 //            var x = w - this.trigger.getWidth();
13660 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13661 //            this.trigger.setStyle('left', x+'px');
13662 //        }
13663     },
13664
13665     // private
13666     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13667
13668     // private
13669     getResizeEl : function(){
13670         return this.inputEl();
13671     },
13672
13673     // private
13674     getPositionEl : function(){
13675         return this.inputEl();
13676     },
13677
13678     // private
13679     alignErrorIcon : function(){
13680         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13681     },
13682
13683     // private
13684     initEvents : function(){
13685         
13686         this.createList();
13687         
13688         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13689         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13690         if(!this.multiple && this.showToggleBtn){
13691             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13692             if(this.hideTrigger){
13693                 this.trigger.setDisplayed(false);
13694             }
13695             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13696         }
13697         
13698         if(this.multiple){
13699             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13700         }
13701         
13702         if(this.removable && !this.editable && !this.tickable){
13703             var close = this.closeTriggerEl();
13704             
13705             if(close){
13706                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13707                 close.on('click', this.removeBtnClick, this, close);
13708             }
13709         }
13710         
13711         //this.trigger.addClassOnOver('x-form-trigger-over');
13712         //this.trigger.addClassOnClick('x-form-trigger-click');
13713         
13714         //if(!this.width){
13715         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13716         //}
13717     },
13718     
13719     closeTriggerEl : function()
13720     {
13721         var close = this.el.select('.roo-combo-removable-btn', true).first();
13722         return close ? close : false;
13723     },
13724     
13725     removeBtnClick : function(e, h, el)
13726     {
13727         e.preventDefault();
13728         
13729         if(this.fireEvent("remove", this) !== false){
13730             this.reset();
13731             this.fireEvent("afterremove", this)
13732         }
13733     },
13734     
13735     createList : function()
13736     {
13737         this.list = Roo.get(document.body).createChild({
13738             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13739             cls: 'typeahead typeahead-long dropdown-menu shadow',
13740             style: 'display:none'
13741         });
13742         
13743         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13744         
13745     },
13746
13747     // private
13748     initTrigger : function(){
13749        
13750     },
13751
13752     // private
13753     onDestroy : function(){
13754         if(this.trigger){
13755             this.trigger.removeAllListeners();
13756           //  this.trigger.remove();
13757         }
13758         //if(this.wrap){
13759         //    this.wrap.remove();
13760         //}
13761         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13762     },
13763
13764     // private
13765     onFocus : function(){
13766         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13767         /*
13768         if(!this.mimicing){
13769             this.wrap.addClass('x-trigger-wrap-focus');
13770             this.mimicing = true;
13771             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13772             if(this.monitorTab){
13773                 this.el.on("keydown", this.checkTab, this);
13774             }
13775         }
13776         */
13777     },
13778
13779     // private
13780     checkTab : function(e){
13781         if(e.getKey() == e.TAB){
13782             this.triggerBlur();
13783         }
13784     },
13785
13786     // private
13787     onBlur : function(){
13788         // do nothing
13789     },
13790
13791     // private
13792     mimicBlur : function(e, t){
13793         /*
13794         if(!this.wrap.contains(t) && this.validateBlur()){
13795             this.triggerBlur();
13796         }
13797         */
13798     },
13799
13800     // private
13801     triggerBlur : function(){
13802         this.mimicing = false;
13803         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13804         if(this.monitorTab){
13805             this.el.un("keydown", this.checkTab, this);
13806         }
13807         //this.wrap.removeClass('x-trigger-wrap-focus');
13808         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13809     },
13810
13811     // private
13812     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13813     validateBlur : function(e, t){
13814         return true;
13815     },
13816
13817     // private
13818     onDisable : function(){
13819         this.inputEl().dom.disabled = true;
13820         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13821         //if(this.wrap){
13822         //    this.wrap.addClass('x-item-disabled');
13823         //}
13824     },
13825
13826     // private
13827     onEnable : function(){
13828         this.inputEl().dom.disabled = false;
13829         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13830         //if(this.wrap){
13831         //    this.el.removeClass('x-item-disabled');
13832         //}
13833     },
13834
13835     // private
13836     onShow : function(){
13837         var ae = this.getActionEl();
13838         
13839         if(ae){
13840             ae.dom.style.display = '';
13841             ae.dom.style.visibility = 'visible';
13842         }
13843     },
13844
13845     // private
13846     
13847     onHide : function(){
13848         var ae = this.getActionEl();
13849         ae.dom.style.display = 'none';
13850     },
13851
13852     /**
13853      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13854      * by an implementing function.
13855      * @method
13856      * @param {EventObject} e
13857      */
13858     onTriggerClick : Roo.emptyFn
13859 });
13860  
13861 /*
13862 * Licence: LGPL
13863 */
13864
13865 /**
13866  * @class Roo.bootstrap.CardUploader
13867  * @extends Roo.bootstrap.Button
13868  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13869  * @cfg {Number} errorTimeout default 3000
13870  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13871  * @cfg {Array}  html The button text.
13872
13873  *
13874  * @constructor
13875  * Create a new CardUploader
13876  * @param {Object} config The config object
13877  */
13878
13879 Roo.bootstrap.CardUploader = function(config){
13880     
13881  
13882     
13883     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13884     
13885     
13886     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13887         return r.data.id
13888      });
13889     
13890      this.addEvents({
13891          // raw events
13892         /**
13893          * @event preview
13894          * When a image is clicked on - and needs to display a slideshow or similar..
13895          * @param {Roo.bootstrap.Card} this
13896          * @param {Object} The image information data 
13897          *
13898          */
13899         'preview' : true,
13900          /**
13901          * @event download
13902          * When a the download link is clicked
13903          * @param {Roo.bootstrap.Card} this
13904          * @param {Object} The image information data  contains 
13905          */
13906         'download' : true
13907         
13908     });
13909 };
13910  
13911 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13912     
13913      
13914     errorTimeout : 3000,
13915      
13916     images : false,
13917    
13918     fileCollection : false,
13919     allowBlank : true,
13920     
13921     getAutoCreate : function()
13922     {
13923         
13924         var cfg =  {
13925             cls :'form-group' ,
13926             cn : [
13927                
13928                 {
13929                     tag: 'label',
13930                    //cls : 'input-group-addon',
13931                     html : this.fieldLabel
13932
13933                 },
13934
13935                 {
13936                     tag: 'input',
13937                     type : 'hidden',
13938                     name : this.name,
13939                     value : this.value,
13940                     cls : 'd-none  form-control'
13941                 },
13942                 
13943                 {
13944                     tag: 'input',
13945                     multiple : 'multiple',
13946                     type : 'file',
13947                     cls : 'd-none  roo-card-upload-selector'
13948                 },
13949                 
13950                 {
13951                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13952                 },
13953                 {
13954                     cls : 'card-columns roo-card-uploader-container'
13955                 }
13956
13957             ]
13958         };
13959            
13960          
13961         return cfg;
13962     },
13963     
13964     getChildContainer : function() /// what children are added to.
13965     {
13966         return this.containerEl;
13967     },
13968    
13969     getButtonContainer : function() /// what children are added to.
13970     {
13971         return this.el.select(".roo-card-uploader-button-container").first();
13972     },
13973    
13974     initEvents : function()
13975     {
13976         
13977         Roo.bootstrap.Input.prototype.initEvents.call(this);
13978         
13979         var t = this;
13980         this.addxtype({
13981             xns: Roo.bootstrap,
13982
13983             xtype : 'Button',
13984             container_method : 'getButtonContainer' ,            
13985             html :  this.html, // fix changable?
13986             cls : 'w-100 ',
13987             listeners : {
13988                 'click' : function(btn, e) {
13989                     t.onClick(e);
13990                 }
13991             }
13992         });
13993         
13994         
13995         
13996         
13997         this.urlAPI = (window.createObjectURL && window) || 
13998                                 (window.URL && URL.revokeObjectURL && URL) || 
13999                                 (window.webkitURL && webkitURL);
14000                         
14001          
14002          
14003          
14004         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14005         
14006         this.selectorEl.on('change', this.onFileSelected, this);
14007         if (this.images) {
14008             var t = this;
14009             this.images.forEach(function(img) {
14010                 t.addCard(img)
14011             });
14012             this.images = false;
14013         }
14014         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14015          
14016        
14017     },
14018     
14019    
14020     onClick : function(e)
14021     {
14022         e.preventDefault();
14023          
14024         this.selectorEl.dom.click();
14025          
14026     },
14027     
14028     onFileSelected : function(e)
14029     {
14030         e.preventDefault();
14031         
14032         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14033             return;
14034         }
14035         
14036         Roo.each(this.selectorEl.dom.files, function(file){    
14037             this.addFile(file);
14038         }, this);
14039          
14040     },
14041     
14042       
14043     
14044       
14045     
14046     addFile : function(file)
14047     {
14048            
14049         if(typeof(file) === 'string'){
14050             throw "Add file by name?"; // should not happen
14051             return;
14052         }
14053         
14054         if(!file || !this.urlAPI){
14055             return;
14056         }
14057         
14058         // file;
14059         // file.type;
14060         
14061         var _this = this;
14062         
14063         
14064         var url = _this.urlAPI.createObjectURL( file);
14065            
14066         this.addCard({
14067             id : Roo.bootstrap.CardUploader.ID--,
14068             is_uploaded : false,
14069             src : url,
14070             srcfile : file,
14071             title : file.name,
14072             mimetype : file.type,
14073             preview : false,
14074             is_deleted : 0
14075         });
14076         
14077     },
14078     
14079     /**
14080      * addCard - add an Attachment to the uploader
14081      * @param data - the data about the image to upload
14082      *
14083      * {
14084           id : 123
14085           title : "Title of file",
14086           is_uploaded : false,
14087           src : "http://.....",
14088           srcfile : { the File upload object },
14089           mimetype : file.type,
14090           preview : false,
14091           is_deleted : 0
14092           .. any other data...
14093         }
14094      *
14095      * 
14096     */
14097     
14098     addCard : function (data)
14099     {
14100         // hidden input element?
14101         // if the file is not an image...
14102         //then we need to use something other that and header_image
14103         var t = this;
14104         //   remove.....
14105         var footer = [
14106             {
14107                 xns : Roo.bootstrap,
14108                 xtype : 'CardFooter',
14109                  items: [
14110                     {
14111                         xns : Roo.bootstrap,
14112                         xtype : 'Element',
14113                         cls : 'd-flex',
14114                         items : [
14115                             
14116                             {
14117                                 xns : Roo.bootstrap,
14118                                 xtype : 'Button',
14119                                 html : String.format("<small>{0}</small>", data.title),
14120                                 cls : 'col-10 text-left',
14121                                 size: 'sm',
14122                                 weight: 'link',
14123                                 fa : 'download',
14124                                 listeners : {
14125                                     click : function() {
14126                                      
14127                                         t.fireEvent( "download", t, data );
14128                                     }
14129                                 }
14130                             },
14131                           
14132                             {
14133                                 xns : Roo.bootstrap,
14134                                 xtype : 'Button',
14135                                 style: 'max-height: 28px; ',
14136                                 size : 'sm',
14137                                 weight: 'danger',
14138                                 cls : 'col-2',
14139                                 fa : 'times',
14140                                 listeners : {
14141                                     click : function() {
14142                                         t.removeCard(data.id)
14143                                     }
14144                                 }
14145                             }
14146                         ]
14147                     }
14148                     
14149                 ] 
14150             }
14151             
14152         ];
14153         
14154         var cn = this.addxtype(
14155             {
14156                  
14157                 xns : Roo.bootstrap,
14158                 xtype : 'Card',
14159                 closeable : true,
14160                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14161                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14162                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14163                 data : data,
14164                 html : false,
14165                  
14166                 items : footer,
14167                 initEvents : function() {
14168                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14169                     var card = this;
14170                     this.imgEl = this.el.select('.card-img-top').first();
14171                     if (this.imgEl) {
14172                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14173                         this.imgEl.set({ 'pointer' : 'cursor' });
14174                                   
14175                     }
14176                     this.getCardFooter().addClass('p-1');
14177                     
14178                   
14179                 }
14180                 
14181             }
14182         );
14183         // dont' really need ot update items.
14184         // this.items.push(cn);
14185         this.fileCollection.add(cn);
14186         
14187         if (!data.srcfile) {
14188             this.updateInput();
14189             return;
14190         }
14191             
14192         var _t = this;
14193         var reader = new FileReader();
14194         reader.addEventListener("load", function() {  
14195             data.srcdata =  reader.result;
14196             _t.updateInput();
14197         });
14198         reader.readAsDataURL(data.srcfile);
14199         
14200         
14201         
14202     },
14203     removeCard : function(id)
14204     {
14205         
14206         var card  = this.fileCollection.get(id);
14207         card.data.is_deleted = 1;
14208         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14209         //this.fileCollection.remove(card);
14210         //this.items = this.items.filter(function(e) { return e != card });
14211         // dont' really need ot update items.
14212         card.el.dom.parentNode.removeChild(card.el.dom);
14213         this.updateInput();
14214
14215         
14216     },
14217     reset: function()
14218     {
14219         this.fileCollection.each(function(card) {
14220             if (card.el.dom && card.el.dom.parentNode) {
14221                 card.el.dom.parentNode.removeChild(card.el.dom);
14222             }
14223         });
14224         this.fileCollection.clear();
14225         this.updateInput();
14226     },
14227     
14228     updateInput : function()
14229     {
14230          var data = [];
14231         this.fileCollection.each(function(e) {
14232             data.push(e.data);
14233             
14234         });
14235         this.inputEl().dom.value = JSON.stringify(data);
14236         
14237         
14238         
14239     }
14240     
14241     
14242 });
14243
14244
14245 Roo.bootstrap.CardUploader.ID = -1;/*
14246  * Based on:
14247  * Ext JS Library 1.1.1
14248  * Copyright(c) 2006-2007, Ext JS, LLC.
14249  *
14250  * Originally Released Under LGPL - original licence link has changed is not relivant.
14251  *
14252  * Fork - LGPL
14253  * <script type="text/javascript">
14254  */
14255
14256
14257 /**
14258  * @class Roo.data.SortTypes
14259  * @singleton
14260  * Defines the default sorting (casting?) comparison functions used when sorting data.
14261  */
14262 Roo.data.SortTypes = {
14263     /**
14264      * Default sort that does nothing
14265      * @param {Mixed} s The value being converted
14266      * @return {Mixed} The comparison value
14267      */
14268     none : function(s){
14269         return s;
14270     },
14271     
14272     /**
14273      * The regular expression used to strip tags
14274      * @type {RegExp}
14275      * @property
14276      */
14277     stripTagsRE : /<\/?[^>]+>/gi,
14278     
14279     /**
14280      * Strips all HTML tags to sort on text only
14281      * @param {Mixed} s The value being converted
14282      * @return {String} The comparison value
14283      */
14284     asText : function(s){
14285         return String(s).replace(this.stripTagsRE, "");
14286     },
14287     
14288     /**
14289      * Strips all HTML tags to sort on text only - Case insensitive
14290      * @param {Mixed} s The value being converted
14291      * @return {String} The comparison value
14292      */
14293     asUCText : function(s){
14294         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14295     },
14296     
14297     /**
14298      * Case insensitive string
14299      * @param {Mixed} s The value being converted
14300      * @return {String} The comparison value
14301      */
14302     asUCString : function(s) {
14303         return String(s).toUpperCase();
14304     },
14305     
14306     /**
14307      * Date sorting
14308      * @param {Mixed} s The value being converted
14309      * @return {Number} The comparison value
14310      */
14311     asDate : function(s) {
14312         if(!s){
14313             return 0;
14314         }
14315         if(s instanceof Date){
14316             return s.getTime();
14317         }
14318         return Date.parse(String(s));
14319     },
14320     
14321     /**
14322      * Float sorting
14323      * @param {Mixed} s The value being converted
14324      * @return {Float} The comparison value
14325      */
14326     asFloat : function(s) {
14327         var val = parseFloat(String(s).replace(/,/g, ""));
14328         if(isNaN(val)) {
14329             val = 0;
14330         }
14331         return val;
14332     },
14333     
14334     /**
14335      * Integer sorting
14336      * @param {Mixed} s The value being converted
14337      * @return {Number} The comparison value
14338      */
14339     asInt : function(s) {
14340         var val = parseInt(String(s).replace(/,/g, ""));
14341         if(isNaN(val)) {
14342             val = 0;
14343         }
14344         return val;
14345     }
14346 };/*
14347  * Based on:
14348  * Ext JS Library 1.1.1
14349  * Copyright(c) 2006-2007, Ext JS, LLC.
14350  *
14351  * Originally Released Under LGPL - original licence link has changed is not relivant.
14352  *
14353  * Fork - LGPL
14354  * <script type="text/javascript">
14355  */
14356
14357 /**
14358 * @class Roo.data.Record
14359  * Instances of this class encapsulate both record <em>definition</em> information, and record
14360  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14361  * to access Records cached in an {@link Roo.data.Store} object.<br>
14362  * <p>
14363  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14364  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14365  * objects.<br>
14366  * <p>
14367  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14368  * @constructor
14369  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14370  * {@link #create}. The parameters are the same.
14371  * @param {Array} data An associative Array of data values keyed by the field name.
14372  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14373  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14374  * not specified an integer id is generated.
14375  */
14376 Roo.data.Record = function(data, id){
14377     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14378     this.data = data;
14379 };
14380
14381 /**
14382  * Generate a constructor for a specific record layout.
14383  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14384  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14385  * Each field definition object may contain the following properties: <ul>
14386  * <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,
14387  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14388  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14389  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14390  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14391  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14392  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14393  * this may be omitted.</p></li>
14394  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14395  * <ul><li>auto (Default, implies no conversion)</li>
14396  * <li>string</li>
14397  * <li>int</li>
14398  * <li>float</li>
14399  * <li>boolean</li>
14400  * <li>date</li></ul></p></li>
14401  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14402  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14403  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14404  * by the Reader into an object that will be stored in the Record. It is passed the
14405  * following parameters:<ul>
14406  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14407  * </ul></p></li>
14408  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14409  * </ul>
14410  * <br>usage:<br><pre><code>
14411 var TopicRecord = Roo.data.Record.create(
14412     {name: 'title', mapping: 'topic_title'},
14413     {name: 'author', mapping: 'username'},
14414     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14415     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14416     {name: 'lastPoster', mapping: 'user2'},
14417     {name: 'excerpt', mapping: 'post_text'}
14418 );
14419
14420 var myNewRecord = new TopicRecord({
14421     title: 'Do my job please',
14422     author: 'noobie',
14423     totalPosts: 1,
14424     lastPost: new Date(),
14425     lastPoster: 'Animal',
14426     excerpt: 'No way dude!'
14427 });
14428 myStore.add(myNewRecord);
14429 </code></pre>
14430  * @method create
14431  * @static
14432  */
14433 Roo.data.Record.create = function(o){
14434     var f = function(){
14435         f.superclass.constructor.apply(this, arguments);
14436     };
14437     Roo.extend(f, Roo.data.Record);
14438     var p = f.prototype;
14439     p.fields = new Roo.util.MixedCollection(false, function(field){
14440         return field.name;
14441     });
14442     for(var i = 0, len = o.length; i < len; i++){
14443         p.fields.add(new Roo.data.Field(o[i]));
14444     }
14445     f.getField = function(name){
14446         return p.fields.get(name);  
14447     };
14448     return f;
14449 };
14450
14451 Roo.data.Record.AUTO_ID = 1000;
14452 Roo.data.Record.EDIT = 'edit';
14453 Roo.data.Record.REJECT = 'reject';
14454 Roo.data.Record.COMMIT = 'commit';
14455
14456 Roo.data.Record.prototype = {
14457     /**
14458      * Readonly flag - true if this record has been modified.
14459      * @type Boolean
14460      */
14461     dirty : false,
14462     editing : false,
14463     error: null,
14464     modified: null,
14465
14466     // private
14467     join : function(store){
14468         this.store = store;
14469     },
14470
14471     /**
14472      * Set the named field to the specified value.
14473      * @param {String} name The name of the field to set.
14474      * @param {Object} value The value to set the field to.
14475      */
14476     set : function(name, value){
14477         if(this.data[name] == value){
14478             return;
14479         }
14480         this.dirty = true;
14481         if(!this.modified){
14482             this.modified = {};
14483         }
14484         if(typeof this.modified[name] == 'undefined'){
14485             this.modified[name] = this.data[name];
14486         }
14487         this.data[name] = value;
14488         if(!this.editing && this.store){
14489             this.store.afterEdit(this);
14490         }       
14491     },
14492
14493     /**
14494      * Get the value of the named field.
14495      * @param {String} name The name of the field to get the value of.
14496      * @return {Object} The value of the field.
14497      */
14498     get : function(name){
14499         return this.data[name]; 
14500     },
14501
14502     // private
14503     beginEdit : function(){
14504         this.editing = true;
14505         this.modified = {}; 
14506     },
14507
14508     // private
14509     cancelEdit : function(){
14510         this.editing = false;
14511         delete this.modified;
14512     },
14513
14514     // private
14515     endEdit : function(){
14516         this.editing = false;
14517         if(this.dirty && this.store){
14518             this.store.afterEdit(this);
14519         }
14520     },
14521
14522     /**
14523      * Usually called by the {@link Roo.data.Store} which owns the Record.
14524      * Rejects all changes made to the Record since either creation, or the last commit operation.
14525      * Modified fields are reverted to their original values.
14526      * <p>
14527      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14528      * of reject operations.
14529      */
14530     reject : function(){
14531         var m = this.modified;
14532         for(var n in m){
14533             if(typeof m[n] != "function"){
14534                 this.data[n] = m[n];
14535             }
14536         }
14537         this.dirty = false;
14538         delete this.modified;
14539         this.editing = false;
14540         if(this.store){
14541             this.store.afterReject(this);
14542         }
14543     },
14544
14545     /**
14546      * Usually called by the {@link Roo.data.Store} which owns the Record.
14547      * Commits all changes made to the Record since either creation, or the last commit operation.
14548      * <p>
14549      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14550      * of commit operations.
14551      */
14552     commit : function(){
14553         this.dirty = false;
14554         delete this.modified;
14555         this.editing = false;
14556         if(this.store){
14557             this.store.afterCommit(this);
14558         }
14559     },
14560
14561     // private
14562     hasError : function(){
14563         return this.error != null;
14564     },
14565
14566     // private
14567     clearError : function(){
14568         this.error = null;
14569     },
14570
14571     /**
14572      * Creates a copy of this record.
14573      * @param {String} id (optional) A new record id if you don't want to use this record's id
14574      * @return {Record}
14575      */
14576     copy : function(newId) {
14577         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14578     }
14579 };/*
14580  * Based on:
14581  * Ext JS Library 1.1.1
14582  * Copyright(c) 2006-2007, Ext JS, LLC.
14583  *
14584  * Originally Released Under LGPL - original licence link has changed is not relivant.
14585  *
14586  * Fork - LGPL
14587  * <script type="text/javascript">
14588  */
14589
14590
14591
14592 /**
14593  * @class Roo.data.Store
14594  * @extends Roo.util.Observable
14595  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14596  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14597  * <p>
14598  * 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
14599  * has no knowledge of the format of the data returned by the Proxy.<br>
14600  * <p>
14601  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14602  * instances from the data object. These records are cached and made available through accessor functions.
14603  * @constructor
14604  * Creates a new Store.
14605  * @param {Object} config A config object containing the objects needed for the Store to access data,
14606  * and read the data into Records.
14607  */
14608 Roo.data.Store = function(config){
14609     this.data = new Roo.util.MixedCollection(false);
14610     this.data.getKey = function(o){
14611         return o.id;
14612     };
14613     this.baseParams = {};
14614     // private
14615     this.paramNames = {
14616         "start" : "start",
14617         "limit" : "limit",
14618         "sort" : "sort",
14619         "dir" : "dir",
14620         "multisort" : "_multisort"
14621     };
14622
14623     if(config && config.data){
14624         this.inlineData = config.data;
14625         delete config.data;
14626     }
14627
14628     Roo.apply(this, config);
14629     
14630     if(this.reader){ // reader passed
14631         this.reader = Roo.factory(this.reader, Roo.data);
14632         this.reader.xmodule = this.xmodule || false;
14633         if(!this.recordType){
14634             this.recordType = this.reader.recordType;
14635         }
14636         if(this.reader.onMetaChange){
14637             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14638         }
14639     }
14640
14641     if(this.recordType){
14642         this.fields = this.recordType.prototype.fields;
14643     }
14644     this.modified = [];
14645
14646     this.addEvents({
14647         /**
14648          * @event datachanged
14649          * Fires when the data cache has changed, and a widget which is using this Store
14650          * as a Record cache should refresh its view.
14651          * @param {Store} this
14652          */
14653         datachanged : true,
14654         /**
14655          * @event metachange
14656          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14657          * @param {Store} this
14658          * @param {Object} meta The JSON metadata
14659          */
14660         metachange : true,
14661         /**
14662          * @event add
14663          * Fires when Records have been added to the Store
14664          * @param {Store} this
14665          * @param {Roo.data.Record[]} records The array of Records added
14666          * @param {Number} index The index at which the record(s) were added
14667          */
14668         add : true,
14669         /**
14670          * @event remove
14671          * Fires when a Record has been removed from the Store
14672          * @param {Store} this
14673          * @param {Roo.data.Record} record The Record that was removed
14674          * @param {Number} index The index at which the record was removed
14675          */
14676         remove : true,
14677         /**
14678          * @event update
14679          * Fires when a Record has been updated
14680          * @param {Store} this
14681          * @param {Roo.data.Record} record The Record that was updated
14682          * @param {String} operation The update operation being performed.  Value may be one of:
14683          * <pre><code>
14684  Roo.data.Record.EDIT
14685  Roo.data.Record.REJECT
14686  Roo.data.Record.COMMIT
14687          * </code></pre>
14688          */
14689         update : true,
14690         /**
14691          * @event clear
14692          * Fires when the data cache has been cleared.
14693          * @param {Store} this
14694          */
14695         clear : true,
14696         /**
14697          * @event beforeload
14698          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14699          * the load action will be canceled.
14700          * @param {Store} this
14701          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14702          */
14703         beforeload : true,
14704         /**
14705          * @event beforeloadadd
14706          * Fires after a new set of Records has been loaded.
14707          * @param {Store} this
14708          * @param {Roo.data.Record[]} records The Records that were loaded
14709          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14710          */
14711         beforeloadadd : true,
14712         /**
14713          * @event load
14714          * Fires after a new set of Records has been loaded, before they are added to the store.
14715          * @param {Store} this
14716          * @param {Roo.data.Record[]} records The Records that were loaded
14717          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14718          * @params {Object} return from reader
14719          */
14720         load : true,
14721         /**
14722          * @event loadexception
14723          * Fires if an exception occurs in the Proxy during loading.
14724          * Called with the signature of the Proxy's "loadexception" event.
14725          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14726          * 
14727          * @param {Proxy} 
14728          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14729          * @param {Object} load options 
14730          * @param {Object} jsonData from your request (normally this contains the Exception)
14731          */
14732         loadexception : true
14733     });
14734     
14735     if(this.proxy){
14736         this.proxy = Roo.factory(this.proxy, Roo.data);
14737         this.proxy.xmodule = this.xmodule || false;
14738         this.relayEvents(this.proxy,  ["loadexception"]);
14739     }
14740     this.sortToggle = {};
14741     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14742
14743     Roo.data.Store.superclass.constructor.call(this);
14744
14745     if(this.inlineData){
14746         this.loadData(this.inlineData);
14747         delete this.inlineData;
14748     }
14749 };
14750
14751 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14752      /**
14753     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14754     * without a remote query - used by combo/forms at present.
14755     */
14756     
14757     /**
14758     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14759     */
14760     /**
14761     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14762     */
14763     /**
14764     * @cfg {Roo.data.Reader} reader [required]  The Reader object which processes the data object and returns
14765     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14766     */
14767     /**
14768     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14769     * on any HTTP request
14770     */
14771     /**
14772     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14773     */
14774     /**
14775     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14776     */
14777     multiSort: false,
14778     /**
14779     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14780     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14781     */
14782     remoteSort : false,
14783
14784     /**
14785     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14786      * loaded or when a record is removed. (defaults to false).
14787     */
14788     pruneModifiedRecords : false,
14789
14790     // private
14791     lastOptions : null,
14792
14793     /**
14794      * Add Records to the Store and fires the add event.
14795      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14796      */
14797     add : function(records){
14798         records = [].concat(records);
14799         for(var i = 0, len = records.length; i < len; i++){
14800             records[i].join(this);
14801         }
14802         var index = this.data.length;
14803         this.data.addAll(records);
14804         this.fireEvent("add", this, records, index);
14805     },
14806
14807     /**
14808      * Remove a Record from the Store and fires the remove event.
14809      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14810      */
14811     remove : function(record){
14812         var index = this.data.indexOf(record);
14813         this.data.removeAt(index);
14814  
14815         if(this.pruneModifiedRecords){
14816             this.modified.remove(record);
14817         }
14818         this.fireEvent("remove", this, record, index);
14819     },
14820
14821     /**
14822      * Remove all Records from the Store and fires the clear event.
14823      */
14824     removeAll : function(){
14825         this.data.clear();
14826         if(this.pruneModifiedRecords){
14827             this.modified = [];
14828         }
14829         this.fireEvent("clear", this);
14830     },
14831
14832     /**
14833      * Inserts Records to the Store at the given index and fires the add event.
14834      * @param {Number} index The start index at which to insert the passed Records.
14835      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14836      */
14837     insert : function(index, records){
14838         records = [].concat(records);
14839         for(var i = 0, len = records.length; i < len; i++){
14840             this.data.insert(index, records[i]);
14841             records[i].join(this);
14842         }
14843         this.fireEvent("add", this, records, index);
14844     },
14845
14846     /**
14847      * Get the index within the cache of the passed Record.
14848      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14849      * @return {Number} The index of the passed Record. Returns -1 if not found.
14850      */
14851     indexOf : function(record){
14852         return this.data.indexOf(record);
14853     },
14854
14855     /**
14856      * Get the index within the cache of the Record with the passed id.
14857      * @param {String} id The id of the Record to find.
14858      * @return {Number} The index of the Record. Returns -1 if not found.
14859      */
14860     indexOfId : function(id){
14861         return this.data.indexOfKey(id);
14862     },
14863
14864     /**
14865      * Get the Record with the specified id.
14866      * @param {String} id The id of the Record to find.
14867      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14868      */
14869     getById : function(id){
14870         return this.data.key(id);
14871     },
14872
14873     /**
14874      * Get the Record at the specified index.
14875      * @param {Number} index The index of the Record to find.
14876      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14877      */
14878     getAt : function(index){
14879         return this.data.itemAt(index);
14880     },
14881
14882     /**
14883      * Returns a range of Records between specified indices.
14884      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14885      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14886      * @return {Roo.data.Record[]} An array of Records
14887      */
14888     getRange : function(start, end){
14889         return this.data.getRange(start, end);
14890     },
14891
14892     // private
14893     storeOptions : function(o){
14894         o = Roo.apply({}, o);
14895         delete o.callback;
14896         delete o.scope;
14897         this.lastOptions = o;
14898     },
14899
14900     /**
14901      * Loads the Record cache from the configured Proxy using the configured Reader.
14902      * <p>
14903      * If using remote paging, then the first load call must specify the <em>start</em>
14904      * and <em>limit</em> properties in the options.params property to establish the initial
14905      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14906      * <p>
14907      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14908      * and this call will return before the new data has been loaded. Perform any post-processing
14909      * in a callback function, or in a "load" event handler.</strong>
14910      * <p>
14911      * @param {Object} options An object containing properties which control loading options:<ul>
14912      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14913      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14914      * passed the following arguments:<ul>
14915      * <li>r : Roo.data.Record[]</li>
14916      * <li>options: Options object from the load call</li>
14917      * <li>success: Boolean success indicator</li></ul></li>
14918      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14919      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14920      * </ul>
14921      */
14922     load : function(options){
14923         options = options || {};
14924         if(this.fireEvent("beforeload", this, options) !== false){
14925             this.storeOptions(options);
14926             var p = Roo.apply(options.params || {}, this.baseParams);
14927             // if meta was not loaded from remote source.. try requesting it.
14928             if (!this.reader.metaFromRemote) {
14929                 p._requestMeta = 1;
14930             }
14931             if(this.sortInfo && this.remoteSort){
14932                 var pn = this.paramNames;
14933                 p[pn["sort"]] = this.sortInfo.field;
14934                 p[pn["dir"]] = this.sortInfo.direction;
14935             }
14936             if (this.multiSort) {
14937                 var pn = this.paramNames;
14938                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14939             }
14940             
14941             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14942         }
14943     },
14944
14945     /**
14946      * Reloads the Record cache from the configured Proxy using the configured Reader and
14947      * the options from the last load operation performed.
14948      * @param {Object} options (optional) An object containing properties which may override the options
14949      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14950      * the most recently used options are reused).
14951      */
14952     reload : function(options){
14953         this.load(Roo.applyIf(options||{}, this.lastOptions));
14954     },
14955
14956     // private
14957     // Called as a callback by the Reader during a load operation.
14958     loadRecords : function(o, options, success){
14959         if(!o || success === false){
14960             if(success !== false){
14961                 this.fireEvent("load", this, [], options, o);
14962             }
14963             if(options.callback){
14964                 options.callback.call(options.scope || this, [], options, false);
14965             }
14966             return;
14967         }
14968         // if data returned failure - throw an exception.
14969         if (o.success === false) {
14970             // show a message if no listener is registered.
14971             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14972                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14973             }
14974             // loadmask wil be hooked into this..
14975             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14976             return;
14977         }
14978         var r = o.records, t = o.totalRecords || r.length;
14979         
14980         this.fireEvent("beforeloadadd", this, r, options, o);
14981         
14982         if(!options || options.add !== true){
14983             if(this.pruneModifiedRecords){
14984                 this.modified = [];
14985             }
14986             for(var i = 0, len = r.length; i < len; i++){
14987                 r[i].join(this);
14988             }
14989             if(this.snapshot){
14990                 this.data = this.snapshot;
14991                 delete this.snapshot;
14992             }
14993             this.data.clear();
14994             this.data.addAll(r);
14995             this.totalLength = t;
14996             this.applySort();
14997             this.fireEvent("datachanged", this);
14998         }else{
14999             this.totalLength = Math.max(t, this.data.length+r.length);
15000             this.add(r);
15001         }
15002         
15003         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15004                 
15005             var e = new Roo.data.Record({});
15006
15007             e.set(this.parent.displayField, this.parent.emptyTitle);
15008             e.set(this.parent.valueField, '');
15009
15010             this.insert(0, e);
15011         }
15012             
15013         this.fireEvent("load", this, r, options, o);
15014         if(options.callback){
15015             options.callback.call(options.scope || this, r, options, true);
15016         }
15017     },
15018
15019
15020     /**
15021      * Loads data from a passed data block. A Reader which understands the format of the data
15022      * must have been configured in the constructor.
15023      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15024      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15025      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15026      */
15027     loadData : function(o, append){
15028         var r = this.reader.readRecords(o);
15029         this.loadRecords(r, {add: append}, true);
15030     },
15031     
15032      /**
15033      * using 'cn' the nested child reader read the child array into it's child stores.
15034      * @param {Object} rec The record with a 'children array
15035      */
15036     loadDataFromChildren : function(rec)
15037     {
15038         this.loadData(this.reader.toLoadData(rec));
15039     },
15040     
15041
15042     /**
15043      * Gets the number of cached records.
15044      * <p>
15045      * <em>If using paging, this may not be the total size of the dataset. If the data object
15046      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15047      * the data set size</em>
15048      */
15049     getCount : function(){
15050         return this.data.length || 0;
15051     },
15052
15053     /**
15054      * Gets the total number of records in the dataset as returned by the server.
15055      * <p>
15056      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15057      * the dataset size</em>
15058      */
15059     getTotalCount : function(){
15060         return this.totalLength || 0;
15061     },
15062
15063     /**
15064      * Returns the sort state of the Store as an object with two properties:
15065      * <pre><code>
15066  field {String} The name of the field by which the Records are sorted
15067  direction {String} The sort order, "ASC" or "DESC"
15068      * </code></pre>
15069      */
15070     getSortState : function(){
15071         return this.sortInfo;
15072     },
15073
15074     // private
15075     applySort : function(){
15076         if(this.sortInfo && !this.remoteSort){
15077             var s = this.sortInfo, f = s.field;
15078             var st = this.fields.get(f).sortType;
15079             var fn = function(r1, r2){
15080                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15081                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15082             };
15083             this.data.sort(s.direction, fn);
15084             if(this.snapshot && this.snapshot != this.data){
15085                 this.snapshot.sort(s.direction, fn);
15086             }
15087         }
15088     },
15089
15090     /**
15091      * Sets the default sort column and order to be used by the next load operation.
15092      * @param {String} fieldName The name of the field to sort by.
15093      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15094      */
15095     setDefaultSort : function(field, dir){
15096         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15097     },
15098
15099     /**
15100      * Sort the Records.
15101      * If remote sorting is used, the sort is performed on the server, and the cache is
15102      * reloaded. If local sorting is used, the cache is sorted internally.
15103      * @param {String} fieldName The name of the field to sort by.
15104      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15105      */
15106     sort : function(fieldName, dir){
15107         var f = this.fields.get(fieldName);
15108         if(!dir){
15109             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15110             
15111             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15112                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15113             }else{
15114                 dir = f.sortDir;
15115             }
15116         }
15117         this.sortToggle[f.name] = dir;
15118         this.sortInfo = {field: f.name, direction: dir};
15119         if(!this.remoteSort){
15120             this.applySort();
15121             this.fireEvent("datachanged", this);
15122         }else{
15123             this.load(this.lastOptions);
15124         }
15125     },
15126
15127     /**
15128      * Calls the specified function for each of the Records in the cache.
15129      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15130      * Returning <em>false</em> aborts and exits the iteration.
15131      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15132      */
15133     each : function(fn, scope){
15134         this.data.each(fn, scope);
15135     },
15136
15137     /**
15138      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15139      * (e.g., during paging).
15140      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15141      */
15142     getModifiedRecords : function(){
15143         return this.modified;
15144     },
15145
15146     // private
15147     createFilterFn : function(property, value, anyMatch){
15148         if(!value.exec){ // not a regex
15149             value = String(value);
15150             if(value.length == 0){
15151                 return false;
15152             }
15153             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15154         }
15155         return function(r){
15156             return value.test(r.data[property]);
15157         };
15158     },
15159
15160     /**
15161      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15162      * @param {String} property A field on your records
15163      * @param {Number} start The record index to start at (defaults to 0)
15164      * @param {Number} end The last record index to include (defaults to length - 1)
15165      * @return {Number} The sum
15166      */
15167     sum : function(property, start, end){
15168         var rs = this.data.items, v = 0;
15169         start = start || 0;
15170         end = (end || end === 0) ? end : rs.length-1;
15171
15172         for(var i = start; i <= end; i++){
15173             v += (rs[i].data[property] || 0);
15174         }
15175         return v;
15176     },
15177
15178     /**
15179      * Filter the records by a specified property.
15180      * @param {String} field A field on your records
15181      * @param {String/RegExp} value Either a string that the field
15182      * should start with or a RegExp to test against the field
15183      * @param {Boolean} anyMatch True to match any part not just the beginning
15184      */
15185     filter : function(property, value, anyMatch){
15186         var fn = this.createFilterFn(property, value, anyMatch);
15187         return fn ? this.filterBy(fn) : this.clearFilter();
15188     },
15189
15190     /**
15191      * Filter by a function. The specified function will be called with each
15192      * record in this data source. If the function returns true the record is included,
15193      * otherwise it is filtered.
15194      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15195      * @param {Object} scope (optional) The scope of the function (defaults to this)
15196      */
15197     filterBy : function(fn, scope){
15198         this.snapshot = this.snapshot || this.data;
15199         this.data = this.queryBy(fn, scope||this);
15200         this.fireEvent("datachanged", this);
15201     },
15202
15203     /**
15204      * Query the records by a specified property.
15205      * @param {String} field A field on your records
15206      * @param {String/RegExp} value Either a string that the field
15207      * should start with or a RegExp to test against the field
15208      * @param {Boolean} anyMatch True to match any part not just the beginning
15209      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15210      */
15211     query : function(property, value, anyMatch){
15212         var fn = this.createFilterFn(property, value, anyMatch);
15213         return fn ? this.queryBy(fn) : this.data.clone();
15214     },
15215
15216     /**
15217      * Query by a function. The specified function will be called with each
15218      * record in this data source. If the function returns true the record is included
15219      * in the results.
15220      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15221      * @param {Object} scope (optional) The scope of the function (defaults to this)
15222       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15223      **/
15224     queryBy : function(fn, scope){
15225         var data = this.snapshot || this.data;
15226         return data.filterBy(fn, scope||this);
15227     },
15228
15229     /**
15230      * Collects unique values for a particular dataIndex from this store.
15231      * @param {String} dataIndex The property to collect
15232      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15233      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15234      * @return {Array} An array of the unique values
15235      **/
15236     collect : function(dataIndex, allowNull, bypassFilter){
15237         var d = (bypassFilter === true && this.snapshot) ?
15238                 this.snapshot.items : this.data.items;
15239         var v, sv, r = [], l = {};
15240         for(var i = 0, len = d.length; i < len; i++){
15241             v = d[i].data[dataIndex];
15242             sv = String(v);
15243             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15244                 l[sv] = true;
15245                 r[r.length] = v;
15246             }
15247         }
15248         return r;
15249     },
15250
15251     /**
15252      * Revert to a view of the Record cache with no filtering applied.
15253      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15254      */
15255     clearFilter : function(suppressEvent){
15256         if(this.snapshot && this.snapshot != this.data){
15257             this.data = this.snapshot;
15258             delete this.snapshot;
15259             if(suppressEvent !== true){
15260                 this.fireEvent("datachanged", this);
15261             }
15262         }
15263     },
15264
15265     // private
15266     afterEdit : function(record){
15267         if(this.modified.indexOf(record) == -1){
15268             this.modified.push(record);
15269         }
15270         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15271     },
15272     
15273     // private
15274     afterReject : function(record){
15275         this.modified.remove(record);
15276         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15277     },
15278
15279     // private
15280     afterCommit : function(record){
15281         this.modified.remove(record);
15282         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15283     },
15284
15285     /**
15286      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15287      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15288      */
15289     commitChanges : function(){
15290         var m = this.modified.slice(0);
15291         this.modified = [];
15292         for(var i = 0, len = m.length; i < len; i++){
15293             m[i].commit();
15294         }
15295     },
15296
15297     /**
15298      * Cancel outstanding changes on all changed records.
15299      */
15300     rejectChanges : function(){
15301         var m = this.modified.slice(0);
15302         this.modified = [];
15303         for(var i = 0, len = m.length; i < len; i++){
15304             m[i].reject();
15305         }
15306     },
15307
15308     onMetaChange : function(meta, rtype, o){
15309         this.recordType = rtype;
15310         this.fields = rtype.prototype.fields;
15311         delete this.snapshot;
15312         this.sortInfo = meta.sortInfo || this.sortInfo;
15313         this.modified = [];
15314         this.fireEvent('metachange', this, this.reader.meta);
15315     },
15316     
15317     moveIndex : function(data, type)
15318     {
15319         var index = this.indexOf(data);
15320         
15321         var newIndex = index + type;
15322         
15323         this.remove(data);
15324         
15325         this.insert(newIndex, data);
15326         
15327     }
15328 });/*
15329  * Based on:
15330  * Ext JS Library 1.1.1
15331  * Copyright(c) 2006-2007, Ext JS, LLC.
15332  *
15333  * Originally Released Under LGPL - original licence link has changed is not relivant.
15334  *
15335  * Fork - LGPL
15336  * <script type="text/javascript">
15337  */
15338
15339 /**
15340  * @class Roo.data.SimpleStore
15341  * @extends Roo.data.Store
15342  * Small helper class to make creating Stores from Array data easier.
15343  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15344  * @cfg {Array} fields An array of field definition objects, or field name strings.
15345  * @cfg {Object} an existing reader (eg. copied from another store)
15346  * @cfg {Array} data The multi-dimensional array of data
15347  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15348  * @cfg {Roo.data.Reader} reader  [not-required] 
15349  * @constructor
15350  * @param {Object} config
15351  */
15352 Roo.data.SimpleStore = function(config)
15353 {
15354     Roo.data.SimpleStore.superclass.constructor.call(this, {
15355         isLocal : true,
15356         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15357                 id: config.id
15358             },
15359             Roo.data.Record.create(config.fields)
15360         ),
15361         proxy : new Roo.data.MemoryProxy(config.data)
15362     });
15363     this.load();
15364 };
15365 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15366  * Based on:
15367  * Ext JS Library 1.1.1
15368  * Copyright(c) 2006-2007, Ext JS, LLC.
15369  *
15370  * Originally Released Under LGPL - original licence link has changed is not relivant.
15371  *
15372  * Fork - LGPL
15373  * <script type="text/javascript">
15374  */
15375
15376 /**
15377 /**
15378  * @extends Roo.data.Store
15379  * @class Roo.data.JsonStore
15380  * Small helper class to make creating Stores for JSON data easier. <br/>
15381 <pre><code>
15382 var store = new Roo.data.JsonStore({
15383     url: 'get-images.php',
15384     root: 'images',
15385     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15386 });
15387 </code></pre>
15388  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15389  * JsonReader and HttpProxy (unless inline data is provided).</b>
15390  * @cfg {Array} fields An array of field definition objects, or field name strings.
15391  * @constructor
15392  * @param {Object} config
15393  */
15394 Roo.data.JsonStore = function(c){
15395     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15396         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15397         reader: new Roo.data.JsonReader(c, c.fields)
15398     }));
15399 };
15400 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15401  * Based on:
15402  * Ext JS Library 1.1.1
15403  * Copyright(c) 2006-2007, Ext JS, LLC.
15404  *
15405  * Originally Released Under LGPL - original licence link has changed is not relivant.
15406  *
15407  * Fork - LGPL
15408  * <script type="text/javascript">
15409  */
15410
15411  
15412 Roo.data.Field = function(config){
15413     if(typeof config == "string"){
15414         config = {name: config};
15415     }
15416     Roo.apply(this, config);
15417     
15418     if(!this.type){
15419         this.type = "auto";
15420     }
15421     
15422     var st = Roo.data.SortTypes;
15423     // named sortTypes are supported, here we look them up
15424     if(typeof this.sortType == "string"){
15425         this.sortType = st[this.sortType];
15426     }
15427     
15428     // set default sortType for strings and dates
15429     if(!this.sortType){
15430         switch(this.type){
15431             case "string":
15432                 this.sortType = st.asUCString;
15433                 break;
15434             case "date":
15435                 this.sortType = st.asDate;
15436                 break;
15437             default:
15438                 this.sortType = st.none;
15439         }
15440     }
15441
15442     // define once
15443     var stripRe = /[\$,%]/g;
15444
15445     // prebuilt conversion function for this field, instead of
15446     // switching every time we're reading a value
15447     if(!this.convert){
15448         var cv, dateFormat = this.dateFormat;
15449         switch(this.type){
15450             case "":
15451             case "auto":
15452             case undefined:
15453                 cv = function(v){ return v; };
15454                 break;
15455             case "string":
15456                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15457                 break;
15458             case "int":
15459                 cv = function(v){
15460                     return v !== undefined && v !== null && v !== '' ?
15461                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15462                     };
15463                 break;
15464             case "float":
15465                 cv = function(v){
15466                     return v !== undefined && v !== null && v !== '' ?
15467                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15468                     };
15469                 break;
15470             case "bool":
15471             case "boolean":
15472                 cv = function(v){ return v === true || v === "true" || v == 1; };
15473                 break;
15474             case "date":
15475                 cv = function(v){
15476                     if(!v){
15477                         return '';
15478                     }
15479                     if(v instanceof Date){
15480                         return v;
15481                     }
15482                     if(dateFormat){
15483                         if(dateFormat == "timestamp"){
15484                             return new Date(v*1000);
15485                         }
15486                         return Date.parseDate(v, dateFormat);
15487                     }
15488                     var parsed = Date.parse(v);
15489                     return parsed ? new Date(parsed) : null;
15490                 };
15491              break;
15492             
15493         }
15494         this.convert = cv;
15495     }
15496 };
15497
15498 Roo.data.Field.prototype = {
15499     dateFormat: null,
15500     defaultValue: "",
15501     mapping: null,
15502     sortType : null,
15503     sortDir : "ASC"
15504 };/*
15505  * Based on:
15506  * Ext JS Library 1.1.1
15507  * Copyright(c) 2006-2007, Ext JS, LLC.
15508  *
15509  * Originally Released Under LGPL - original licence link has changed is not relivant.
15510  *
15511  * Fork - LGPL
15512  * <script type="text/javascript">
15513  */
15514  
15515 // Base class for reading structured data from a data source.  This class is intended to be
15516 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15517
15518 /**
15519  * @class Roo.data.DataReader
15520  * Base class for reading structured data from a data source.  This class is intended to be
15521  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15522  */
15523
15524 Roo.data.DataReader = function(meta, recordType){
15525     
15526     this.meta = meta;
15527     
15528     this.recordType = recordType instanceof Array ? 
15529         Roo.data.Record.create(recordType) : recordType;
15530 };
15531
15532 Roo.data.DataReader.prototype = {
15533     
15534     
15535     readerType : 'Data',
15536      /**
15537      * Create an empty record
15538      * @param {Object} data (optional) - overlay some values
15539      * @return {Roo.data.Record} record created.
15540      */
15541     newRow :  function(d) {
15542         var da =  {};
15543         this.recordType.prototype.fields.each(function(c) {
15544             switch( c.type) {
15545                 case 'int' : da[c.name] = 0; break;
15546                 case 'date' : da[c.name] = new Date(); break;
15547                 case 'float' : da[c.name] = 0.0; break;
15548                 case 'boolean' : da[c.name] = false; break;
15549                 default : da[c.name] = ""; break;
15550             }
15551             
15552         });
15553         return new this.recordType(Roo.apply(da, d));
15554     }
15555     
15556     
15557 };/*
15558  * Based on:
15559  * Ext JS Library 1.1.1
15560  * Copyright(c) 2006-2007, Ext JS, LLC.
15561  *
15562  * Originally Released Under LGPL - original licence link has changed is not relivant.
15563  *
15564  * Fork - LGPL
15565  * <script type="text/javascript">
15566  */
15567
15568 /**
15569  * @class Roo.data.DataProxy
15570  * @extends Roo.data.Observable
15571  * @abstract
15572  * This class is an abstract base class for implementations which provide retrieval of
15573  * unformatted data objects.<br>
15574  * <p>
15575  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15576  * (of the appropriate type which knows how to parse the data object) to provide a block of
15577  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15578  * <p>
15579  * Custom implementations must implement the load method as described in
15580  * {@link Roo.data.HttpProxy#load}.
15581  */
15582 Roo.data.DataProxy = function(){
15583     this.addEvents({
15584         /**
15585          * @event beforeload
15586          * Fires before a network request is made to retrieve a data object.
15587          * @param {Object} This DataProxy object.
15588          * @param {Object} params The params parameter to the load function.
15589          */
15590         beforeload : true,
15591         /**
15592          * @event load
15593          * Fires before the load method's callback is called.
15594          * @param {Object} This DataProxy object.
15595          * @param {Object} o The data object.
15596          * @param {Object} arg The callback argument object passed to the load function.
15597          */
15598         load : true,
15599         /**
15600          * @event loadexception
15601          * Fires if an Exception occurs during data retrieval.
15602          * @param {Object} This DataProxy object.
15603          * @param {Object} o The data object.
15604          * @param {Object} arg The callback argument object passed to the load function.
15605          * @param {Object} e The Exception.
15606          */
15607         loadexception : true
15608     });
15609     Roo.data.DataProxy.superclass.constructor.call(this);
15610 };
15611
15612 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15613
15614     /**
15615      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15616      */
15617 /*
15618  * Based on:
15619  * Ext JS Library 1.1.1
15620  * Copyright(c) 2006-2007, Ext JS, LLC.
15621  *
15622  * Originally Released Under LGPL - original licence link has changed is not relivant.
15623  *
15624  * Fork - LGPL
15625  * <script type="text/javascript">
15626  */
15627 /**
15628  * @class Roo.data.MemoryProxy
15629  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15630  * to the Reader when its load method is called.
15631  * @constructor
15632  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15633  */
15634 Roo.data.MemoryProxy = function(data){
15635     if (data.data) {
15636         data = data.data;
15637     }
15638     Roo.data.MemoryProxy.superclass.constructor.call(this);
15639     this.data = data;
15640 };
15641
15642 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15643     
15644     /**
15645      * Load data from the requested source (in this case an in-memory
15646      * data object passed to the constructor), read the data object into
15647      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15648      * process that block using the passed callback.
15649      * @param {Object} params This parameter is not used by the MemoryProxy class.
15650      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15651      * object into a block of Roo.data.Records.
15652      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15653      * The function must be passed <ul>
15654      * <li>The Record block object</li>
15655      * <li>The "arg" argument from the load function</li>
15656      * <li>A boolean success indicator</li>
15657      * </ul>
15658      * @param {Object} scope The scope in which to call the callback
15659      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15660      */
15661     load : function(params, reader, callback, scope, arg){
15662         params = params || {};
15663         var result;
15664         try {
15665             result = reader.readRecords(params.data ? params.data :this.data);
15666         }catch(e){
15667             this.fireEvent("loadexception", this, arg, null, e);
15668             callback.call(scope, null, arg, false);
15669             return;
15670         }
15671         callback.call(scope, result, arg, true);
15672     },
15673     
15674     // private
15675     update : function(params, records){
15676         
15677     }
15678 });/*
15679  * Based on:
15680  * Ext JS Library 1.1.1
15681  * Copyright(c) 2006-2007, Ext JS, LLC.
15682  *
15683  * Originally Released Under LGPL - original licence link has changed is not relivant.
15684  *
15685  * Fork - LGPL
15686  * <script type="text/javascript">
15687  */
15688 /**
15689  * @class Roo.data.HttpProxy
15690  * @extends Roo.data.DataProxy
15691  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15692  * configured to reference a certain URL.<br><br>
15693  * <p>
15694  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15695  * from which the running page was served.<br><br>
15696  * <p>
15697  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15698  * <p>
15699  * Be aware that to enable the browser to parse an XML document, the server must set
15700  * the Content-Type header in the HTTP response to "text/xml".
15701  * @constructor
15702  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15703  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15704  * will be used to make the request.
15705  */
15706 Roo.data.HttpProxy = function(conn){
15707     Roo.data.HttpProxy.superclass.constructor.call(this);
15708     // is conn a conn config or a real conn?
15709     this.conn = conn;
15710     this.useAjax = !conn || !conn.events;
15711   
15712 };
15713
15714 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15715     // thse are take from connection...
15716     
15717     /**
15718      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15719      */
15720     /**
15721      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15722      * extra parameters to each request made by this object. (defaults to undefined)
15723      */
15724     /**
15725      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15726      *  to each request made by this object. (defaults to undefined)
15727      */
15728     /**
15729      * @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)
15730      */
15731     /**
15732      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15733      */
15734      /**
15735      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15736      * @type Boolean
15737      */
15738   
15739
15740     /**
15741      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15742      * @type Boolean
15743      */
15744     /**
15745      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15746      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15747      * a finer-grained basis than the DataProxy events.
15748      */
15749     getConnection : function(){
15750         return this.useAjax ? Roo.Ajax : this.conn;
15751     },
15752
15753     /**
15754      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15755      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15756      * process that block using the passed callback.
15757      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15758      * for the request to the remote server.
15759      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15760      * object into a block of Roo.data.Records.
15761      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15762      * The function must be passed <ul>
15763      * <li>The Record block object</li>
15764      * <li>The "arg" argument from the load function</li>
15765      * <li>A boolean success indicator</li>
15766      * </ul>
15767      * @param {Object} scope The scope in which to call the callback
15768      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15769      */
15770     load : function(params, reader, callback, scope, arg){
15771         if(this.fireEvent("beforeload", this, params) !== false){
15772             var  o = {
15773                 params : params || {},
15774                 request: {
15775                     callback : callback,
15776                     scope : scope,
15777                     arg : arg
15778                 },
15779                 reader: reader,
15780                 callback : this.loadResponse,
15781                 scope: this
15782             };
15783             if(this.useAjax){
15784                 Roo.applyIf(o, this.conn);
15785                 if(this.activeRequest){
15786                     Roo.Ajax.abort(this.activeRequest);
15787                 }
15788                 this.activeRequest = Roo.Ajax.request(o);
15789             }else{
15790                 this.conn.request(o);
15791             }
15792         }else{
15793             callback.call(scope||this, null, arg, false);
15794         }
15795     },
15796
15797     // private
15798     loadResponse : function(o, success, response){
15799         delete this.activeRequest;
15800         if(!success){
15801             this.fireEvent("loadexception", this, o, response);
15802             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15803             return;
15804         }
15805         var result;
15806         try {
15807             result = o.reader.read(response);
15808         }catch(e){
15809             this.fireEvent("loadexception", this, o, response, e);
15810             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15811             return;
15812         }
15813         
15814         this.fireEvent("load", this, o, o.request.arg);
15815         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15816     },
15817
15818     // private
15819     update : function(dataSet){
15820
15821     },
15822
15823     // private
15824     updateResponse : function(dataSet){
15825
15826     }
15827 });/*
15828  * Based on:
15829  * Ext JS Library 1.1.1
15830  * Copyright(c) 2006-2007, Ext JS, LLC.
15831  *
15832  * Originally Released Under LGPL - original licence link has changed is not relivant.
15833  *
15834  * Fork - LGPL
15835  * <script type="text/javascript">
15836  */
15837
15838 /**
15839  * @class Roo.data.ScriptTagProxy
15840  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15841  * other than the originating domain of the running page.<br><br>
15842  * <p>
15843  * <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
15844  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15845  * <p>
15846  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15847  * source code that is used as the source inside a &lt;script> tag.<br><br>
15848  * <p>
15849  * In order for the browser to process the returned data, the server must wrap the data object
15850  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15851  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15852  * depending on whether the callback name was passed:
15853  * <p>
15854  * <pre><code>
15855 boolean scriptTag = false;
15856 String cb = request.getParameter("callback");
15857 if (cb != null) {
15858     scriptTag = true;
15859     response.setContentType("text/javascript");
15860 } else {
15861     response.setContentType("application/x-json");
15862 }
15863 Writer out = response.getWriter();
15864 if (scriptTag) {
15865     out.write(cb + "(");
15866 }
15867 out.print(dataBlock.toJsonString());
15868 if (scriptTag) {
15869     out.write(");");
15870 }
15871 </pre></code>
15872  *
15873  * @constructor
15874  * @param {Object} config A configuration object.
15875  */
15876 Roo.data.ScriptTagProxy = function(config){
15877     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15878     Roo.apply(this, config);
15879     this.head = document.getElementsByTagName("head")[0];
15880 };
15881
15882 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15883
15884 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15885     /**
15886      * @cfg {String} url The URL from which to request the data object.
15887      */
15888     /**
15889      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15890      */
15891     timeout : 30000,
15892     /**
15893      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15894      * the server the name of the callback function set up by the load call to process the returned data object.
15895      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15896      * javascript output which calls this named function passing the data object as its only parameter.
15897      */
15898     callbackParam : "callback",
15899     /**
15900      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15901      * name to the request.
15902      */
15903     nocache : true,
15904
15905     /**
15906      * Load data from the configured URL, read the data object into
15907      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15908      * process that block using the passed callback.
15909      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15910      * for the request to the remote server.
15911      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15912      * object into a block of Roo.data.Records.
15913      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15914      * The function must be passed <ul>
15915      * <li>The Record block object</li>
15916      * <li>The "arg" argument from the load function</li>
15917      * <li>A boolean success indicator</li>
15918      * </ul>
15919      * @param {Object} scope The scope in which to call the callback
15920      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15921      */
15922     load : function(params, reader, callback, scope, arg){
15923         if(this.fireEvent("beforeload", this, params) !== false){
15924
15925             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15926
15927             var url = this.url;
15928             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15929             if(this.nocache){
15930                 url += "&_dc=" + (new Date().getTime());
15931             }
15932             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15933             var trans = {
15934                 id : transId,
15935                 cb : "stcCallback"+transId,
15936                 scriptId : "stcScript"+transId,
15937                 params : params,
15938                 arg : arg,
15939                 url : url,
15940                 callback : callback,
15941                 scope : scope,
15942                 reader : reader
15943             };
15944             var conn = this;
15945
15946             window[trans.cb] = function(o){
15947                 conn.handleResponse(o, trans);
15948             };
15949
15950             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15951
15952             if(this.autoAbort !== false){
15953                 this.abort();
15954             }
15955
15956             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15957
15958             var script = document.createElement("script");
15959             script.setAttribute("src", url);
15960             script.setAttribute("type", "text/javascript");
15961             script.setAttribute("id", trans.scriptId);
15962             this.head.appendChild(script);
15963
15964             this.trans = trans;
15965         }else{
15966             callback.call(scope||this, null, arg, false);
15967         }
15968     },
15969
15970     // private
15971     isLoading : function(){
15972         return this.trans ? true : false;
15973     },
15974
15975     /**
15976      * Abort the current server request.
15977      */
15978     abort : function(){
15979         if(this.isLoading()){
15980             this.destroyTrans(this.trans);
15981         }
15982     },
15983
15984     // private
15985     destroyTrans : function(trans, isLoaded){
15986         this.head.removeChild(document.getElementById(trans.scriptId));
15987         clearTimeout(trans.timeoutId);
15988         if(isLoaded){
15989             window[trans.cb] = undefined;
15990             try{
15991                 delete window[trans.cb];
15992             }catch(e){}
15993         }else{
15994             // if hasn't been loaded, wait for load to remove it to prevent script error
15995             window[trans.cb] = function(){
15996                 window[trans.cb] = undefined;
15997                 try{
15998                     delete window[trans.cb];
15999                 }catch(e){}
16000             };
16001         }
16002     },
16003
16004     // private
16005     handleResponse : function(o, trans){
16006         this.trans = false;
16007         this.destroyTrans(trans, true);
16008         var result;
16009         try {
16010             result = trans.reader.readRecords(o);
16011         }catch(e){
16012             this.fireEvent("loadexception", this, o, trans.arg, e);
16013             trans.callback.call(trans.scope||window, null, trans.arg, false);
16014             return;
16015         }
16016         this.fireEvent("load", this, o, trans.arg);
16017         trans.callback.call(trans.scope||window, result, trans.arg, true);
16018     },
16019
16020     // private
16021     handleFailure : function(trans){
16022         this.trans = false;
16023         this.destroyTrans(trans, false);
16024         this.fireEvent("loadexception", this, null, trans.arg);
16025         trans.callback.call(trans.scope||window, null, trans.arg, false);
16026     }
16027 });/*
16028  * Based on:
16029  * Ext JS Library 1.1.1
16030  * Copyright(c) 2006-2007, Ext JS, LLC.
16031  *
16032  * Originally Released Under LGPL - original licence link has changed is not relivant.
16033  *
16034  * Fork - LGPL
16035  * <script type="text/javascript">
16036  */
16037
16038 /**
16039  * @class Roo.data.JsonReader
16040  * @extends Roo.data.DataReader
16041  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16042  * based on mappings in a provided Roo.data.Record constructor.
16043  * 
16044  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16045  * in the reply previously. 
16046  * 
16047  * <p>
16048  * Example code:
16049  * <pre><code>
16050 var RecordDef = Roo.data.Record.create([
16051     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16052     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16053 ]);
16054 var myReader = new Roo.data.JsonReader({
16055     totalProperty: "results",    // The property which contains the total dataset size (optional)
16056     root: "rows",                // The property which contains an Array of row objects
16057     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16058 }, RecordDef);
16059 </code></pre>
16060  * <p>
16061  * This would consume a JSON file like this:
16062  * <pre><code>
16063 { 'results': 2, 'rows': [
16064     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16065     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16066 }
16067 </code></pre>
16068  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16069  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16070  * paged from the remote server.
16071  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16072  * @cfg {String} root name of the property which contains the Array of row objects.
16073  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16074  * @cfg {Array} fields Array of field definition objects
16075  * @constructor
16076  * Create a new JsonReader
16077  * @param {Object} meta Metadata configuration options
16078  * @param {Object} recordType Either an Array of field definition objects,
16079  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16080  */
16081 Roo.data.JsonReader = function(meta, recordType){
16082     
16083     meta = meta || {};
16084     // set some defaults:
16085     Roo.applyIf(meta, {
16086         totalProperty: 'total',
16087         successProperty : 'success',
16088         root : 'data',
16089         id : 'id'
16090     });
16091     
16092     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16093 };
16094 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16095     
16096     readerType : 'Json',
16097     
16098     /**
16099      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16100      * Used by Store query builder to append _requestMeta to params.
16101      * 
16102      */
16103     metaFromRemote : false,
16104     /**
16105      * This method is only used by a DataProxy which has retrieved data from a remote server.
16106      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16107      * @return {Object} data A data block which is used by an Roo.data.Store object as
16108      * a cache of Roo.data.Records.
16109      */
16110     read : function(response){
16111         var json = response.responseText;
16112        
16113         var o = /* eval:var:o */ eval("("+json+")");
16114         if(!o) {
16115             throw {message: "JsonReader.read: Json object not found"};
16116         }
16117         
16118         if(o.metaData){
16119             
16120             delete this.ef;
16121             this.metaFromRemote = true;
16122             this.meta = o.metaData;
16123             this.recordType = Roo.data.Record.create(o.metaData.fields);
16124             this.onMetaChange(this.meta, this.recordType, o);
16125         }
16126         return this.readRecords(o);
16127     },
16128
16129     // private function a store will implement
16130     onMetaChange : function(meta, recordType, o){
16131
16132     },
16133
16134     /**
16135          * @ignore
16136          */
16137     simpleAccess: function(obj, subsc) {
16138         return obj[subsc];
16139     },
16140
16141         /**
16142          * @ignore
16143          */
16144     getJsonAccessor: function(){
16145         var re = /[\[\.]/;
16146         return function(expr) {
16147             try {
16148                 return(re.test(expr))
16149                     ? new Function("obj", "return obj." + expr)
16150                     : function(obj){
16151                         return obj[expr];
16152                     };
16153             } catch(e){}
16154             return Roo.emptyFn;
16155         };
16156     }(),
16157
16158     /**
16159      * Create a data block containing Roo.data.Records from an XML document.
16160      * @param {Object} o An object which contains an Array of row objects in the property specified
16161      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16162      * which contains the total size of the dataset.
16163      * @return {Object} data A data block which is used by an Roo.data.Store object as
16164      * a cache of Roo.data.Records.
16165      */
16166     readRecords : function(o){
16167         /**
16168          * After any data loads, the raw JSON data is available for further custom processing.
16169          * @type Object
16170          */
16171         this.o = o;
16172         var s = this.meta, Record = this.recordType,
16173             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16174
16175 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16176         if (!this.ef) {
16177             if(s.totalProperty) {
16178                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16179                 }
16180                 if(s.successProperty) {
16181                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16182                 }
16183                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16184                 if (s.id) {
16185                         var g = this.getJsonAccessor(s.id);
16186                         this.getId = function(rec) {
16187                                 var r = g(rec);  
16188                                 return (r === undefined || r === "") ? null : r;
16189                         };
16190                 } else {
16191                         this.getId = function(){return null;};
16192                 }
16193             this.ef = [];
16194             for(var jj = 0; jj < fl; jj++){
16195                 f = fi[jj];
16196                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16197                 this.ef[jj] = this.getJsonAccessor(map);
16198             }
16199         }
16200
16201         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16202         if(s.totalProperty){
16203             var vt = parseInt(this.getTotal(o), 10);
16204             if(!isNaN(vt)){
16205                 totalRecords = vt;
16206             }
16207         }
16208         if(s.successProperty){
16209             var vs = this.getSuccess(o);
16210             if(vs === false || vs === 'false'){
16211                 success = false;
16212             }
16213         }
16214         var records = [];
16215         for(var i = 0; i < c; i++){
16216                 var n = root[i];
16217             var values = {};
16218             var id = this.getId(n);
16219             for(var j = 0; j < fl; j++){
16220                 f = fi[j];
16221             var v = this.ef[j](n);
16222             if (!f.convert) {
16223                 Roo.log('missing convert for ' + f.name);
16224                 Roo.log(f);
16225                 continue;
16226             }
16227             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16228             }
16229             var record = new Record(values, id);
16230             record.json = n;
16231             records[i] = record;
16232         }
16233         return {
16234             raw : o,
16235             success : success,
16236             records : records,
16237             totalRecords : totalRecords
16238         };
16239     },
16240     // used when loading children.. @see loadDataFromChildren
16241     toLoadData: function(rec)
16242     {
16243         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16244         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16245         return { data : data, total : data.length };
16246         
16247     }
16248 });/*
16249  * Based on:
16250  * Ext JS Library 1.1.1
16251  * Copyright(c) 2006-2007, Ext JS, LLC.
16252  *
16253  * Originally Released Under LGPL - original licence link has changed is not relivant.
16254  *
16255  * Fork - LGPL
16256  * <script type="text/javascript">
16257  */
16258
16259 /**
16260  * @class Roo.data.ArrayReader
16261  * @extends Roo.data.DataReader
16262  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16263  * Each element of that Array represents a row of data fields. The
16264  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16265  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16266  * <p>
16267  * Example code:.
16268  * <pre><code>
16269 var RecordDef = Roo.data.Record.create([
16270     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16271     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16272 ]);
16273 var myReader = new Roo.data.ArrayReader({
16274     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16275 }, RecordDef);
16276 </code></pre>
16277  * <p>
16278  * This would consume an Array like this:
16279  * <pre><code>
16280 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16281   </code></pre>
16282  
16283  * @constructor
16284  * Create a new JsonReader
16285  * @param {Object} meta Metadata configuration options.
16286  * @param {Object|Array} recordType Either an Array of field definition objects
16287  * 
16288  * @cfg {Array} fields Array of field definition objects
16289  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16290  * as specified to {@link Roo.data.Record#create},
16291  * or an {@link Roo.data.Record} object
16292  *
16293  * 
16294  * created using {@link Roo.data.Record#create}.
16295  */
16296 Roo.data.ArrayReader = function(meta, recordType)
16297 {    
16298     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16299 };
16300
16301 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16302     
16303       /**
16304      * Create a data block containing Roo.data.Records from an XML document.
16305      * @param {Object} o An Array of row objects which represents the dataset.
16306      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16307      * a cache of Roo.data.Records.
16308      */
16309     readRecords : function(o)
16310     {
16311         var sid = this.meta ? this.meta.id : null;
16312         var recordType = this.recordType, fields = recordType.prototype.fields;
16313         var records = [];
16314         var root = o;
16315         for(var i = 0; i < root.length; i++){
16316             var n = root[i];
16317             var values = {};
16318             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16319             for(var j = 0, jlen = fields.length; j < jlen; j++){
16320                 var f = fields.items[j];
16321                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16322                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16323                 v = f.convert(v);
16324                 values[f.name] = v;
16325             }
16326             var record = new recordType(values, id);
16327             record.json = n;
16328             records[records.length] = record;
16329         }
16330         return {
16331             records : records,
16332             totalRecords : records.length
16333         };
16334     },
16335     // used when loading children.. @see loadDataFromChildren
16336     toLoadData: function(rec)
16337     {
16338         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16339         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16340         
16341     }
16342     
16343     
16344 });/*
16345  * - LGPL
16346  * * 
16347  */
16348
16349 /**
16350  * @class Roo.bootstrap.ComboBox
16351  * @extends Roo.bootstrap.TriggerField
16352  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16353  * @cfg {Boolean} append (true|false) default false
16354  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16355  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16356  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16357  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16358  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16359  * @cfg {Boolean} animate default true
16360  * @cfg {Boolean} emptyResultText only for touch device
16361  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16362  * @cfg {String} emptyTitle default ''
16363  * @cfg {Number} width fixed with? experimental
16364  * @constructor
16365  * Create a new ComboBox.
16366  * @param {Object} config Configuration options
16367  */
16368 Roo.bootstrap.ComboBox = function(config){
16369     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16370     this.addEvents({
16371         /**
16372          * @event expand
16373          * Fires when the dropdown list is expanded
16374         * @param {Roo.bootstrap.ComboBox} combo This combo box
16375         */
16376         'expand' : true,
16377         /**
16378          * @event collapse
16379          * Fires when the dropdown list is collapsed
16380         * @param {Roo.bootstrap.ComboBox} combo This combo box
16381         */
16382         'collapse' : true,
16383         /**
16384          * @event beforeselect
16385          * Fires before a list item is selected. Return false to cancel the selection.
16386         * @param {Roo.bootstrap.ComboBox} combo This combo box
16387         * @param {Roo.data.Record} record The data record returned from the underlying store
16388         * @param {Number} index The index of the selected item in the dropdown list
16389         */
16390         'beforeselect' : true,
16391         /**
16392          * @event select
16393          * Fires when a list item is selected
16394         * @param {Roo.bootstrap.ComboBox} combo This combo box
16395         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16396         * @param {Number} index The index of the selected item in the dropdown list
16397         */
16398         'select' : true,
16399         /**
16400          * @event beforequery
16401          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16402          * The event object passed has these properties:
16403         * @param {Roo.bootstrap.ComboBox} combo This combo box
16404         * @param {String} query The query
16405         * @param {Boolean} forceAll true to force "all" query
16406         * @param {Boolean} cancel true to cancel the query
16407         * @param {Object} e The query event object
16408         */
16409         'beforequery': true,
16410          /**
16411          * @event add
16412          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16413         * @param {Roo.bootstrap.ComboBox} combo This combo box
16414         */
16415         'add' : true,
16416         /**
16417          * @event edit
16418          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16419         * @param {Roo.bootstrap.ComboBox} combo This combo box
16420         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16421         */
16422         'edit' : true,
16423         /**
16424          * @event remove
16425          * Fires when the remove value from the combobox array
16426         * @param {Roo.bootstrap.ComboBox} combo This combo box
16427         */
16428         'remove' : true,
16429         /**
16430          * @event afterremove
16431          * Fires when the remove value from the combobox array
16432         * @param {Roo.bootstrap.ComboBox} combo This combo box
16433         */
16434         'afterremove' : true,
16435         /**
16436          * @event specialfilter
16437          * Fires when specialfilter
16438             * @param {Roo.bootstrap.ComboBox} combo This combo box
16439             */
16440         'specialfilter' : true,
16441         /**
16442          * @event tick
16443          * Fires when tick the element
16444             * @param {Roo.bootstrap.ComboBox} combo This combo box
16445             */
16446         'tick' : true,
16447         /**
16448          * @event touchviewdisplay
16449          * Fires when touch view require special display (default is using displayField)
16450             * @param {Roo.bootstrap.ComboBox} combo This combo box
16451             * @param {Object} cfg set html .
16452             */
16453         'touchviewdisplay' : true
16454         
16455     });
16456     
16457     this.item = [];
16458     this.tickItems = [];
16459     
16460     this.selectedIndex = -1;
16461     if(this.mode == 'local'){
16462         if(config.queryDelay === undefined){
16463             this.queryDelay = 10;
16464         }
16465         if(config.minChars === undefined){
16466             this.minChars = 0;
16467         }
16468     }
16469 };
16470
16471 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16472      
16473     /**
16474      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16475      * rendering into an Roo.Editor, defaults to false)
16476      */
16477     /**
16478      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16479      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16480      */
16481     /**
16482      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16483      */
16484     /**
16485      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16486      * the dropdown list (defaults to undefined, with no header element)
16487      */
16488
16489      /**
16490      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16491      */
16492      
16493      /**
16494      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16495      */
16496     listWidth: undefined,
16497     /**
16498      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16499      * mode = 'remote' or 'text' if mode = 'local')
16500      */
16501     displayField: undefined,
16502     
16503     /**
16504      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16505      * mode = 'remote' or 'value' if mode = 'local'). 
16506      * Note: use of a valueField requires the user make a selection
16507      * in order for a value to be mapped.
16508      */
16509     valueField: undefined,
16510     /**
16511      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16512      */
16513     modalTitle : '',
16514     
16515     /**
16516      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16517      * field's data value (defaults to the underlying DOM element's name)
16518      */
16519     hiddenName: undefined,
16520     /**
16521      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16522      */
16523     listClass: '',
16524     /**
16525      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16526      */
16527     selectedClass: 'active',
16528     
16529     /**
16530      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16531      */
16532     shadow:'sides',
16533     /**
16534      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16535      * anchor positions (defaults to 'tl-bl')
16536      */
16537     listAlign: 'tl-bl?',
16538     /**
16539      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16540      */
16541     maxHeight: 300,
16542     /**
16543      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16544      * query specified by the allQuery config option (defaults to 'query')
16545      */
16546     triggerAction: 'query',
16547     /**
16548      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16549      * (defaults to 4, does not apply if editable = false)
16550      */
16551     minChars : 4,
16552     /**
16553      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16554      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16555      */
16556     typeAhead: false,
16557     /**
16558      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16559      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16560      */
16561     queryDelay: 500,
16562     /**
16563      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16564      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16565      */
16566     pageSize: 0,
16567     /**
16568      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16569      * when editable = true (defaults to false)
16570      */
16571     selectOnFocus:false,
16572     /**
16573      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16574      */
16575     queryParam: 'query',
16576     /**
16577      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16578      * when mode = 'remote' (defaults to 'Loading...')
16579      */
16580     loadingText: 'Loading...',
16581     /**
16582      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16583      */
16584     resizable: false,
16585     /**
16586      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16587      */
16588     handleHeight : 8,
16589     /**
16590      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16591      * traditional select (defaults to true)
16592      */
16593     editable: true,
16594     /**
16595      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16596      */
16597     allQuery: '',
16598     /**
16599      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16600      */
16601     mode: 'remote',
16602     /**
16603      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16604      * listWidth has a higher value)
16605      */
16606     minListWidth : 70,
16607     /**
16608      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16609      * allow the user to set arbitrary text into the field (defaults to false)
16610      */
16611     forceSelection:false,
16612     /**
16613      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16614      * if typeAhead = true (defaults to 250)
16615      */
16616     typeAheadDelay : 250,
16617     /**
16618      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16619      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16620      */
16621     valueNotFoundText : undefined,
16622     /**
16623      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16624      */
16625     blockFocus : false,
16626     
16627     /**
16628      * @cfg {Boolean} disableClear Disable showing of clear button.
16629      */
16630     disableClear : false,
16631     /**
16632      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16633      */
16634     alwaysQuery : false,
16635     
16636     /**
16637      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16638      */
16639     multiple : false,
16640     
16641     /**
16642      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16643      */
16644     invalidClass : "has-warning",
16645     
16646     /**
16647      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16648      */
16649     validClass : "has-success",
16650     
16651     /**
16652      * @cfg {Boolean} specialFilter (true|false) special filter default false
16653      */
16654     specialFilter : false,
16655     
16656     /**
16657      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16658      */
16659     mobileTouchView : true,
16660     
16661     /**
16662      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16663      */
16664     useNativeIOS : false,
16665     
16666     /**
16667      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16668      */
16669     mobile_restrict_height : false,
16670     
16671     ios_options : false,
16672     
16673     //private
16674     addicon : false,
16675     editicon: false,
16676     
16677     page: 0,
16678     hasQuery: false,
16679     append: false,
16680     loadNext: false,
16681     autoFocus : true,
16682     tickable : false,
16683     btnPosition : 'right',
16684     triggerList : true,
16685     showToggleBtn : true,
16686     animate : true,
16687     emptyResultText: 'Empty',
16688     triggerText : 'Select',
16689     emptyTitle : '',
16690     width : false,
16691     
16692     // element that contains real text value.. (when hidden is used..)
16693     
16694     getAutoCreate : function()
16695     {   
16696         var cfg = false;
16697         //render
16698         /*
16699          * Render classic select for iso
16700          */
16701         
16702         if(Roo.isIOS && this.useNativeIOS){
16703             cfg = this.getAutoCreateNativeIOS();
16704             return cfg;
16705         }
16706         
16707         /*
16708          * Touch Devices
16709          */
16710         
16711         if(Roo.isTouch && this.mobileTouchView){
16712             cfg = this.getAutoCreateTouchView();
16713             return cfg;;
16714         }
16715         
16716         /*
16717          *  Normal ComboBox
16718          */
16719         if(!this.tickable){
16720             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16721             return cfg;
16722         }
16723         
16724         /*
16725          *  ComboBox with tickable selections
16726          */
16727              
16728         var align = this.labelAlign || this.parentLabelAlign();
16729         
16730         cfg = {
16731             cls : 'form-group roo-combobox-tickable' //input-group
16732         };
16733         
16734         var btn_text_select = '';
16735         var btn_text_done = '';
16736         var btn_text_cancel = '';
16737         
16738         if (this.btn_text_show) {
16739             btn_text_select = 'Select';
16740             btn_text_done = 'Done';
16741             btn_text_cancel = 'Cancel'; 
16742         }
16743         
16744         var buttons = {
16745             tag : 'div',
16746             cls : 'tickable-buttons',
16747             cn : [
16748                 {
16749                     tag : 'button',
16750                     type : 'button',
16751                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16752                     //html : this.triggerText
16753                     html: btn_text_select
16754                 },
16755                 {
16756                     tag : 'button',
16757                     type : 'button',
16758                     name : 'ok',
16759                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16760                     //html : 'Done'
16761                     html: btn_text_done
16762                 },
16763                 {
16764                     tag : 'button',
16765                     type : 'button',
16766                     name : 'cancel',
16767                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16768                     //html : 'Cancel'
16769                     html: btn_text_cancel
16770                 }
16771             ]
16772         };
16773         
16774         if(this.editable){
16775             buttons.cn.unshift({
16776                 tag: 'input',
16777                 cls: 'roo-select2-search-field-input'
16778             });
16779         }
16780         
16781         var _this = this;
16782         
16783         Roo.each(buttons.cn, function(c){
16784             if (_this.size) {
16785                 c.cls += ' btn-' + _this.size;
16786             }
16787
16788             if (_this.disabled) {
16789                 c.disabled = true;
16790             }
16791         });
16792         
16793         var box = {
16794             tag: 'div',
16795             style : 'display: contents',
16796             cn: [
16797                 {
16798                     tag: 'input',
16799                     type : 'hidden',
16800                     cls: 'form-hidden-field'
16801                 },
16802                 {
16803                     tag: 'ul',
16804                     cls: 'roo-select2-choices',
16805                     cn:[
16806                         {
16807                             tag: 'li',
16808                             cls: 'roo-select2-search-field',
16809                             cn: [
16810                                 buttons
16811                             ]
16812                         }
16813                     ]
16814                 }
16815             ]
16816         };
16817         
16818         var combobox = {
16819             cls: 'roo-select2-container input-group roo-select2-container-multi',
16820             cn: [
16821                 
16822                 box
16823 //                {
16824 //                    tag: 'ul',
16825 //                    cls: 'typeahead typeahead-long dropdown-menu',
16826 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16827 //                }
16828             ]
16829         };
16830         
16831         if(this.hasFeedback && !this.allowBlank){
16832             
16833             var feedback = {
16834                 tag: 'span',
16835                 cls: 'glyphicon form-control-feedback'
16836             };
16837
16838             combobox.cn.push(feedback);
16839         }
16840         
16841         
16842         
16843         var indicator = {
16844             tag : 'i',
16845             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16846             tooltip : 'This field is required'
16847         };
16848         if (Roo.bootstrap.version == 4) {
16849             indicator = {
16850                 tag : 'i',
16851                 style : 'display:none'
16852             };
16853         }
16854         if (align ==='left' && this.fieldLabel.length) {
16855             
16856             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16857             
16858             cfg.cn = [
16859                 indicator,
16860                 {
16861                     tag: 'label',
16862                     'for' :  id,
16863                     cls : 'control-label col-form-label',
16864                     html : this.fieldLabel
16865
16866                 },
16867                 {
16868                     cls : "", 
16869                     cn: [
16870                         combobox
16871                     ]
16872                 }
16873
16874             ];
16875             
16876             var labelCfg = cfg.cn[1];
16877             var contentCfg = cfg.cn[2];
16878             
16879
16880             if(this.indicatorpos == 'right'){
16881                 
16882                 cfg.cn = [
16883                     {
16884                         tag: 'label',
16885                         'for' :  id,
16886                         cls : 'control-label col-form-label',
16887                         cn : [
16888                             {
16889                                 tag : 'span',
16890                                 html : this.fieldLabel
16891                             },
16892                             indicator
16893                         ]
16894                     },
16895                     {
16896                         cls : "",
16897                         cn: [
16898                             combobox
16899                         ]
16900                     }
16901
16902                 ];
16903                 
16904                 
16905                 
16906                 labelCfg = cfg.cn[0];
16907                 contentCfg = cfg.cn[1];
16908             
16909             }
16910             
16911             if(this.labelWidth > 12){
16912                 labelCfg.style = "width: " + this.labelWidth + 'px';
16913             }
16914             if(this.width * 1 > 0){
16915                 contentCfg.style = "width: " + this.width + 'px';
16916             }
16917             if(this.labelWidth < 13 && this.labelmd == 0){
16918                 this.labelmd = this.labelWidth;
16919             }
16920             
16921             if(this.labellg > 0){
16922                 labelCfg.cls += ' col-lg-' + this.labellg;
16923                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16924             }
16925             
16926             if(this.labelmd > 0){
16927                 labelCfg.cls += ' col-md-' + this.labelmd;
16928                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16929             }
16930             
16931             if(this.labelsm > 0){
16932                 labelCfg.cls += ' col-sm-' + this.labelsm;
16933                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16934             }
16935             
16936             if(this.labelxs > 0){
16937                 labelCfg.cls += ' col-xs-' + this.labelxs;
16938                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16939             }
16940                 
16941                 
16942         } else if ( this.fieldLabel.length) {
16943 //                Roo.log(" label");
16944                  cfg.cn = [
16945                    indicator,
16946                     {
16947                         tag: 'label',
16948                         //cls : 'input-group-addon',
16949                         html : this.fieldLabel
16950                     },
16951                     combobox
16952                 ];
16953                 
16954                 if(this.indicatorpos == 'right'){
16955                     cfg.cn = [
16956                         {
16957                             tag: 'label',
16958                             //cls : 'input-group-addon',
16959                             html : this.fieldLabel
16960                         },
16961                         indicator,
16962                         combobox
16963                     ];
16964                     
16965                 }
16966
16967         } else {
16968             
16969 //                Roo.log(" no label && no align");
16970                 cfg = combobox
16971                      
16972                 
16973         }
16974          
16975         var settings=this;
16976         ['xs','sm','md','lg'].map(function(size){
16977             if (settings[size]) {
16978                 cfg.cls += ' col-' + size + '-' + settings[size];
16979             }
16980         });
16981         
16982         return cfg;
16983         
16984     },
16985     
16986     _initEventsCalled : false,
16987     
16988     // private
16989     initEvents: function()
16990     {   
16991         if (this._initEventsCalled) { // as we call render... prevent looping...
16992             return;
16993         }
16994         this._initEventsCalled = true;
16995         
16996         if (!this.store) {
16997             throw "can not find store for combo";
16998         }
16999         
17000         this.indicator = this.indicatorEl();
17001         
17002         this.store = Roo.factory(this.store, Roo.data);
17003         this.store.parent = this;
17004         
17005         // if we are building from html. then this element is so complex, that we can not really
17006         // use the rendered HTML.
17007         // so we have to trash and replace the previous code.
17008         if (Roo.XComponent.build_from_html) {
17009             // remove this element....
17010             var e = this.el.dom, k=0;
17011             while (e ) { e = e.previousSibling;  ++k;}
17012
17013             this.el.remove();
17014             
17015             this.el=false;
17016             this.rendered = false;
17017             
17018             this.render(this.parent().getChildContainer(true), k);
17019         }
17020         
17021         if(Roo.isIOS && this.useNativeIOS){
17022             this.initIOSView();
17023             return;
17024         }
17025         
17026         /*
17027          * Touch Devices
17028          */
17029         
17030         if(Roo.isTouch && this.mobileTouchView){
17031             this.initTouchView();
17032             return;
17033         }
17034         
17035         if(this.tickable){
17036             this.initTickableEvents();
17037             return;
17038         }
17039         
17040         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17041         
17042         if(this.hiddenName){
17043             
17044             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17045             
17046             this.hiddenField.dom.value =
17047                 this.hiddenValue !== undefined ? this.hiddenValue :
17048                 this.value !== undefined ? this.value : '';
17049
17050             // prevent input submission
17051             this.el.dom.removeAttribute('name');
17052             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17053              
17054              
17055         }
17056         //if(Roo.isGecko){
17057         //    this.el.dom.setAttribute('autocomplete', 'off');
17058         //}
17059         
17060         var cls = 'x-combo-list';
17061         
17062         //this.list = new Roo.Layer({
17063         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17064         //});
17065         
17066         var _this = this;
17067         
17068         (function(){
17069             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17070             _this.list.setWidth(lw);
17071         }).defer(100);
17072         
17073         this.list.on('mouseover', this.onViewOver, this);
17074         this.list.on('mousemove', this.onViewMove, this);
17075         this.list.on('scroll', this.onViewScroll, this);
17076         
17077         /*
17078         this.list.swallowEvent('mousewheel');
17079         this.assetHeight = 0;
17080
17081         if(this.title){
17082             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17083             this.assetHeight += this.header.getHeight();
17084         }
17085
17086         this.innerList = this.list.createChild({cls:cls+'-inner'});
17087         this.innerList.on('mouseover', this.onViewOver, this);
17088         this.innerList.on('mousemove', this.onViewMove, this);
17089         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17090         
17091         if(this.allowBlank && !this.pageSize && !this.disableClear){
17092             this.footer = this.list.createChild({cls:cls+'-ft'});
17093             this.pageTb = new Roo.Toolbar(this.footer);
17094            
17095         }
17096         if(this.pageSize){
17097             this.footer = this.list.createChild({cls:cls+'-ft'});
17098             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17099                     {pageSize: this.pageSize});
17100             
17101         }
17102         
17103         if (this.pageTb && this.allowBlank && !this.disableClear) {
17104             var _this = this;
17105             this.pageTb.add(new Roo.Toolbar.Fill(), {
17106                 cls: 'x-btn-icon x-btn-clear',
17107                 text: '&#160;',
17108                 handler: function()
17109                 {
17110                     _this.collapse();
17111                     _this.clearValue();
17112                     _this.onSelect(false, -1);
17113                 }
17114             });
17115         }
17116         if (this.footer) {
17117             this.assetHeight += this.footer.getHeight();
17118         }
17119         */
17120             
17121         if(!this.tpl){
17122             this.tpl = Roo.bootstrap.version == 4 ?
17123                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17124                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17125         }
17126
17127         this.view = new Roo.View(this.list, this.tpl, {
17128             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17129         });
17130         //this.view.wrapEl.setDisplayed(false);
17131         this.view.on('click', this.onViewClick, this);
17132         
17133         
17134         this.store.on('beforeload', this.onBeforeLoad, this);
17135         this.store.on('load', this.onLoad, this);
17136         this.store.on('loadexception', this.onLoadException, this);
17137         /*
17138         if(this.resizable){
17139             this.resizer = new Roo.Resizable(this.list,  {
17140                pinned:true, handles:'se'
17141             });
17142             this.resizer.on('resize', function(r, w, h){
17143                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17144                 this.listWidth = w;
17145                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17146                 this.restrictHeight();
17147             }, this);
17148             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17149         }
17150         */
17151         if(!this.editable){
17152             this.editable = true;
17153             this.setEditable(false);
17154         }
17155         
17156         /*
17157         
17158         if (typeof(this.events.add.listeners) != 'undefined') {
17159             
17160             this.addicon = this.wrap.createChild(
17161                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17162        
17163             this.addicon.on('click', function(e) {
17164                 this.fireEvent('add', this);
17165             }, this);
17166         }
17167         if (typeof(this.events.edit.listeners) != 'undefined') {
17168             
17169             this.editicon = this.wrap.createChild(
17170                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17171             if (this.addicon) {
17172                 this.editicon.setStyle('margin-left', '40px');
17173             }
17174             this.editicon.on('click', function(e) {
17175                 
17176                 // we fire even  if inothing is selected..
17177                 this.fireEvent('edit', this, this.lastData );
17178                 
17179             }, this);
17180         }
17181         */
17182         
17183         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17184             "up" : function(e){
17185                 this.inKeyMode = true;
17186                 this.selectPrev();
17187             },
17188
17189             "down" : function(e){
17190                 if(!this.isExpanded()){
17191                     this.onTriggerClick();
17192                 }else{
17193                     this.inKeyMode = true;
17194                     this.selectNext();
17195                 }
17196             },
17197
17198             "enter" : function(e){
17199 //                this.onViewClick();
17200                 //return true;
17201                 this.collapse();
17202                 
17203                 if(this.fireEvent("specialkey", this, e)){
17204                     this.onViewClick(false);
17205                 }
17206                 
17207                 return true;
17208             },
17209
17210             "esc" : function(e){
17211                 this.collapse();
17212             },
17213
17214             "tab" : function(e){
17215                 this.collapse();
17216                 
17217                 if(this.fireEvent("specialkey", this, e)){
17218                     this.onViewClick(false);
17219                 }
17220                 
17221                 return true;
17222             },
17223
17224             scope : this,
17225
17226             doRelay : function(foo, bar, hname){
17227                 if(hname == 'down' || this.scope.isExpanded()){
17228                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17229                 }
17230                 return true;
17231             },
17232
17233             forceKeyDown: true
17234         });
17235         
17236         
17237         this.queryDelay = Math.max(this.queryDelay || 10,
17238                 this.mode == 'local' ? 10 : 250);
17239         
17240         
17241         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17242         
17243         if(this.typeAhead){
17244             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17245         }
17246         if(this.editable !== false){
17247             this.inputEl().on("keyup", this.onKeyUp, this);
17248         }
17249         if(this.forceSelection){
17250             this.inputEl().on('blur', this.doForce, this);
17251         }
17252         
17253         if(this.multiple){
17254             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17255             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17256         }
17257     },
17258     
17259     initTickableEvents: function()
17260     {   
17261         this.createList();
17262         
17263         if(this.hiddenName){
17264             
17265             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17266             
17267             this.hiddenField.dom.value =
17268                 this.hiddenValue !== undefined ? this.hiddenValue :
17269                 this.value !== undefined ? this.value : '';
17270
17271             // prevent input submission
17272             this.el.dom.removeAttribute('name');
17273             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17274              
17275              
17276         }
17277         
17278 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17279         
17280         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17281         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17282         if(this.triggerList){
17283             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17284         }
17285          
17286         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17287         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17288         
17289         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17290         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17291         
17292         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17293         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17294         
17295         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17296         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17297         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17298         
17299         this.okBtn.hide();
17300         this.cancelBtn.hide();
17301         
17302         var _this = this;
17303         
17304         (function(){
17305             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17306             _this.list.setWidth(lw);
17307         }).defer(100);
17308         
17309         this.list.on('mouseover', this.onViewOver, this);
17310         this.list.on('mousemove', this.onViewMove, this);
17311         
17312         this.list.on('scroll', this.onViewScroll, this);
17313         
17314         if(!this.tpl){
17315             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17316                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17317         }
17318
17319         this.view = new Roo.View(this.list, this.tpl, {
17320             singleSelect:true,
17321             tickable:true,
17322             parent:this,
17323             store: this.store,
17324             selectedClass: this.selectedClass
17325         });
17326         
17327         //this.view.wrapEl.setDisplayed(false);
17328         this.view.on('click', this.onViewClick, this);
17329         
17330         
17331         
17332         this.store.on('beforeload', this.onBeforeLoad, this);
17333         this.store.on('load', this.onLoad, this);
17334         this.store.on('loadexception', this.onLoadException, this);
17335         
17336         if(this.editable){
17337             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17338                 "up" : function(e){
17339                     this.inKeyMode = true;
17340                     this.selectPrev();
17341                 },
17342
17343                 "down" : function(e){
17344                     this.inKeyMode = true;
17345                     this.selectNext();
17346                 },
17347
17348                 "enter" : function(e){
17349                     if(this.fireEvent("specialkey", this, e)){
17350                         this.onViewClick(false);
17351                     }
17352                     
17353                     return true;
17354                 },
17355
17356                 "esc" : function(e){
17357                     this.onTickableFooterButtonClick(e, false, false);
17358                 },
17359
17360                 "tab" : function(e){
17361                     this.fireEvent("specialkey", this, e);
17362                     
17363                     this.onTickableFooterButtonClick(e, false, false);
17364                     
17365                     return true;
17366                 },
17367
17368                 scope : this,
17369
17370                 doRelay : function(e, fn, key){
17371                     if(this.scope.isExpanded()){
17372                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17373                     }
17374                     return true;
17375                 },
17376
17377                 forceKeyDown: true
17378             });
17379         }
17380         
17381         this.queryDelay = Math.max(this.queryDelay || 10,
17382                 this.mode == 'local' ? 10 : 250);
17383         
17384         
17385         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17386         
17387         if(this.typeAhead){
17388             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17389         }
17390         
17391         if(this.editable !== false){
17392             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17393         }
17394         
17395         this.indicator = this.indicatorEl();
17396         
17397         if(this.indicator){
17398             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17399             this.indicator.hide();
17400         }
17401         
17402     },
17403
17404     onDestroy : function(){
17405         if(this.view){
17406             this.view.setStore(null);
17407             this.view.el.removeAllListeners();
17408             this.view.el.remove();
17409             this.view.purgeListeners();
17410         }
17411         if(this.list){
17412             this.list.dom.innerHTML  = '';
17413         }
17414         
17415         if(this.store){
17416             this.store.un('beforeload', this.onBeforeLoad, this);
17417             this.store.un('load', this.onLoad, this);
17418             this.store.un('loadexception', this.onLoadException, this);
17419         }
17420         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17421     },
17422
17423     // private
17424     fireKey : function(e){
17425         if(e.isNavKeyPress() && !this.list.isVisible()){
17426             this.fireEvent("specialkey", this, e);
17427         }
17428     },
17429
17430     // private
17431     onResize: function(w, h)
17432     {
17433         
17434         
17435 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17436 //        
17437 //        if(typeof w != 'number'){
17438 //            // we do not handle it!?!?
17439 //            return;
17440 //        }
17441 //        var tw = this.trigger.getWidth();
17442 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17443 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17444 //        var x = w - tw;
17445 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17446 //            
17447 //        //this.trigger.setStyle('left', x+'px');
17448 //        
17449 //        if(this.list && this.listWidth === undefined){
17450 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17451 //            this.list.setWidth(lw);
17452 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17453 //        }
17454         
17455     
17456         
17457     },
17458
17459     /**
17460      * Allow or prevent the user from directly editing the field text.  If false is passed,
17461      * the user will only be able to select from the items defined in the dropdown list.  This method
17462      * is the runtime equivalent of setting the 'editable' config option at config time.
17463      * @param {Boolean} value True to allow the user to directly edit the field text
17464      */
17465     setEditable : function(value){
17466         if(value == this.editable){
17467             return;
17468         }
17469         this.editable = value;
17470         if(!value){
17471             this.inputEl().dom.setAttribute('readOnly', true);
17472             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17473             this.inputEl().addClass('x-combo-noedit');
17474         }else{
17475             this.inputEl().dom.removeAttribute('readOnly');
17476             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17477             this.inputEl().removeClass('x-combo-noedit');
17478         }
17479     },
17480
17481     // private
17482     
17483     onBeforeLoad : function(combo,opts){
17484         if(!this.hasFocus){
17485             return;
17486         }
17487          if (!opts.add) {
17488             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17489          }
17490         this.restrictHeight();
17491         this.selectedIndex = -1;
17492     },
17493
17494     // private
17495     onLoad : function(){
17496         
17497         this.hasQuery = false;
17498         
17499         if(!this.hasFocus){
17500             return;
17501         }
17502         
17503         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17504             this.loading.hide();
17505         }
17506         
17507         if(this.store.getCount() > 0){
17508             
17509             this.expand();
17510             this.restrictHeight();
17511             if(this.lastQuery == this.allQuery){
17512                 if(this.editable && !this.tickable){
17513                     this.inputEl().dom.select();
17514                 }
17515                 
17516                 if(
17517                     !this.selectByValue(this.value, true) &&
17518                     this.autoFocus && 
17519                     (
17520                         !this.store.lastOptions ||
17521                         typeof(this.store.lastOptions.add) == 'undefined' || 
17522                         this.store.lastOptions.add != true
17523                     )
17524                 ){
17525                     this.select(0, true);
17526                 }
17527             }else{
17528                 if(this.autoFocus){
17529                     this.selectNext();
17530                 }
17531                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17532                     this.taTask.delay(this.typeAheadDelay);
17533                 }
17534             }
17535         }else{
17536             this.onEmptyResults();
17537         }
17538         
17539         //this.el.focus();
17540     },
17541     // private
17542     onLoadException : function()
17543     {
17544         this.hasQuery = false;
17545         
17546         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17547             this.loading.hide();
17548         }
17549         
17550         if(this.tickable && this.editable){
17551             return;
17552         }
17553         
17554         this.collapse();
17555         // only causes errors at present
17556         //Roo.log(this.store.reader.jsonData);
17557         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17558             // fixme
17559             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17560         //}
17561         
17562         
17563     },
17564     // private
17565     onTypeAhead : function(){
17566         if(this.store.getCount() > 0){
17567             var r = this.store.getAt(0);
17568             var newValue = r.data[this.displayField];
17569             var len = newValue.length;
17570             var selStart = this.getRawValue().length;
17571             
17572             if(selStart != len){
17573                 this.setRawValue(newValue);
17574                 this.selectText(selStart, newValue.length);
17575             }
17576         }
17577     },
17578
17579     // private
17580     onSelect : function(record, index){
17581         
17582         if(this.fireEvent('beforeselect', this, record, index) !== false){
17583         
17584             this.setFromData(index > -1 ? record.data : false);
17585             
17586             this.collapse();
17587             this.fireEvent('select', this, record, index);
17588         }
17589     },
17590
17591     /**
17592      * Returns the currently selected field value or empty string if no value is set.
17593      * @return {String} value The selected value
17594      */
17595     getValue : function()
17596     {
17597         if(Roo.isIOS && this.useNativeIOS){
17598             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17599         }
17600         
17601         if(this.multiple){
17602             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17603         }
17604         
17605         if(this.valueField){
17606             return typeof this.value != 'undefined' ? this.value : '';
17607         }else{
17608             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17609         }
17610     },
17611     
17612     getRawValue : function()
17613     {
17614         if(Roo.isIOS && this.useNativeIOS){
17615             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17616         }
17617         
17618         var v = this.inputEl().getValue();
17619         
17620         return v;
17621     },
17622
17623     /**
17624      * Clears any text/value currently set in the field
17625      */
17626     clearValue : function(){
17627         
17628         if(this.hiddenField){
17629             this.hiddenField.dom.value = '';
17630         }
17631         this.value = '';
17632         this.setRawValue('');
17633         this.lastSelectionText = '';
17634         this.lastData = false;
17635         
17636         var close = this.closeTriggerEl();
17637         
17638         if(close){
17639             close.hide();
17640         }
17641         
17642         this.validate();
17643         
17644     },
17645
17646     /**
17647      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17648      * will be displayed in the field.  If the value does not match the data value of an existing item,
17649      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17650      * Otherwise the field will be blank (although the value will still be set).
17651      * @param {String} value The value to match
17652      */
17653     setValue : function(v)
17654     {
17655         if(Roo.isIOS && this.useNativeIOS){
17656             this.setIOSValue(v);
17657             return;
17658         }
17659         
17660         if(this.multiple){
17661             this.syncValue();
17662             return;
17663         }
17664         
17665         var text = v;
17666         if(this.valueField){
17667             var r = this.findRecord(this.valueField, v);
17668             if(r){
17669                 text = r.data[this.displayField];
17670             }else if(this.valueNotFoundText !== undefined){
17671                 text = this.valueNotFoundText;
17672             }
17673         }
17674         this.lastSelectionText = text;
17675         if(this.hiddenField){
17676             this.hiddenField.dom.value = v;
17677         }
17678         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17679         this.value = v;
17680         
17681         var close = this.closeTriggerEl();
17682         
17683         if(close){
17684             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17685         }
17686         
17687         this.validate();
17688     },
17689     /**
17690      * @property {Object} the last set data for the element
17691      */
17692     
17693     lastData : false,
17694     /**
17695      * Sets the value of the field based on a object which is related to the record format for the store.
17696      * @param {Object} value the value to set as. or false on reset?
17697      */
17698     setFromData : function(o){
17699         
17700         if(this.multiple){
17701             this.addItem(o);
17702             return;
17703         }
17704             
17705         var dv = ''; // display value
17706         var vv = ''; // value value..
17707         this.lastData = o;
17708         if (this.displayField) {
17709             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17710         } else {
17711             // this is an error condition!!!
17712             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17713         }
17714         
17715         if(this.valueField){
17716             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17717         }
17718         
17719         var close = this.closeTriggerEl();
17720         
17721         if(close){
17722             if(dv.length || vv * 1 > 0){
17723                 close.show() ;
17724                 this.blockFocus=true;
17725             } else {
17726                 close.hide();
17727             }             
17728         }
17729         
17730         if(this.hiddenField){
17731             this.hiddenField.dom.value = vv;
17732             
17733             this.lastSelectionText = dv;
17734             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17735             this.value = vv;
17736             return;
17737         }
17738         // no hidden field.. - we store the value in 'value', but still display
17739         // display field!!!!
17740         this.lastSelectionText = dv;
17741         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17742         this.value = vv;
17743         
17744         
17745         
17746     },
17747     // private
17748     reset : function(){
17749         // overridden so that last data is reset..
17750         
17751         if(this.multiple){
17752             this.clearItem();
17753             return;
17754         }
17755         
17756         this.setValue(this.originalValue);
17757         //this.clearInvalid();
17758         this.lastData = false;
17759         if (this.view) {
17760             this.view.clearSelections();
17761         }
17762         
17763         this.validate();
17764     },
17765     // private
17766     findRecord : function(prop, value){
17767         var record;
17768         if(this.store.getCount() > 0){
17769             this.store.each(function(r){
17770                 if(r.data[prop] == value){
17771                     record = r;
17772                     return false;
17773                 }
17774                 return true;
17775             });
17776         }
17777         return record;
17778     },
17779     
17780     getName: function()
17781     {
17782         // returns hidden if it's set..
17783         if (!this.rendered) {return ''};
17784         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17785         
17786     },
17787     // private
17788     onViewMove : function(e, t){
17789         this.inKeyMode = false;
17790     },
17791
17792     // private
17793     onViewOver : function(e, t){
17794         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17795             return;
17796         }
17797         var item = this.view.findItemFromChild(t);
17798         
17799         if(item){
17800             var index = this.view.indexOf(item);
17801             this.select(index, false);
17802         }
17803     },
17804
17805     // private
17806     onViewClick : function(view, doFocus, el, e)
17807     {
17808         var index = this.view.getSelectedIndexes()[0];
17809         
17810         var r = this.store.getAt(index);
17811         
17812         if(this.tickable){
17813             
17814             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17815                 return;
17816             }
17817             
17818             var rm = false;
17819             var _this = this;
17820             
17821             Roo.each(this.tickItems, function(v,k){
17822                 
17823                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17824                     Roo.log(v);
17825                     _this.tickItems.splice(k, 1);
17826                     
17827                     if(typeof(e) == 'undefined' && view == false){
17828                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17829                     }
17830                     
17831                     rm = true;
17832                     return;
17833                 }
17834             });
17835             
17836             if(rm){
17837                 return;
17838             }
17839             
17840             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17841                 this.tickItems.push(r.data);
17842             }
17843             
17844             if(typeof(e) == 'undefined' && view == false){
17845                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17846             }
17847                     
17848             return;
17849         }
17850         
17851         if(r){
17852             this.onSelect(r, index);
17853         }
17854         if(doFocus !== false && !this.blockFocus){
17855             this.inputEl().focus();
17856         }
17857     },
17858
17859     // private
17860     restrictHeight : function(){
17861         //this.innerList.dom.style.height = '';
17862         //var inner = this.innerList.dom;
17863         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17864         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17865         //this.list.beginUpdate();
17866         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17867         this.list.alignTo(this.inputEl(), this.listAlign);
17868         this.list.alignTo(this.inputEl(), this.listAlign);
17869         //this.list.endUpdate();
17870     },
17871
17872     // private
17873     onEmptyResults : function(){
17874         
17875         if(this.tickable && this.editable){
17876             this.hasFocus = false;
17877             this.restrictHeight();
17878             return;
17879         }
17880         
17881         this.collapse();
17882     },
17883
17884     /**
17885      * Returns true if the dropdown list is expanded, else false.
17886      */
17887     isExpanded : function(){
17888         return this.list.isVisible();
17889     },
17890
17891     /**
17892      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17893      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17894      * @param {String} value The data value of the item to select
17895      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17896      * selected item if it is not currently in view (defaults to true)
17897      * @return {Boolean} True if the value matched an item in the list, else false
17898      */
17899     selectByValue : function(v, scrollIntoView){
17900         if(v !== undefined && v !== null){
17901             var r = this.findRecord(this.valueField || this.displayField, v);
17902             if(r){
17903                 this.select(this.store.indexOf(r), scrollIntoView);
17904                 return true;
17905             }
17906         }
17907         return false;
17908     },
17909
17910     /**
17911      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17912      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17913      * @param {Number} index The zero-based index of the list item to select
17914      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17915      * selected item if it is not currently in view (defaults to true)
17916      */
17917     select : function(index, scrollIntoView){
17918         this.selectedIndex = index;
17919         this.view.select(index);
17920         if(scrollIntoView !== false){
17921             var el = this.view.getNode(index);
17922             /*
17923              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17924              */
17925             if(el){
17926                 this.list.scrollChildIntoView(el, false);
17927             }
17928         }
17929     },
17930
17931     // private
17932     selectNext : function(){
17933         var ct = this.store.getCount();
17934         if(ct > 0){
17935             if(this.selectedIndex == -1){
17936                 this.select(0);
17937             }else if(this.selectedIndex < ct-1){
17938                 this.select(this.selectedIndex+1);
17939             }
17940         }
17941     },
17942
17943     // private
17944     selectPrev : function(){
17945         var ct = this.store.getCount();
17946         if(ct > 0){
17947             if(this.selectedIndex == -1){
17948                 this.select(0);
17949             }else if(this.selectedIndex != 0){
17950                 this.select(this.selectedIndex-1);
17951             }
17952         }
17953     },
17954
17955     // private
17956     onKeyUp : function(e){
17957         if(this.editable !== false && !e.isSpecialKey()){
17958             this.lastKey = e.getKey();
17959             this.dqTask.delay(this.queryDelay);
17960         }
17961     },
17962
17963     // private
17964     validateBlur : function(){
17965         return !this.list || !this.list.isVisible();   
17966     },
17967
17968     // private
17969     initQuery : function(){
17970         
17971         var v = this.getRawValue();
17972         
17973         if(this.tickable && this.editable){
17974             v = this.tickableInputEl().getValue();
17975         }
17976         
17977         this.doQuery(v);
17978     },
17979
17980     // private
17981     doForce : function(){
17982         if(this.inputEl().dom.value.length > 0){
17983             this.inputEl().dom.value =
17984                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17985              
17986         }
17987     },
17988
17989     /**
17990      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17991      * query allowing the query action to be canceled if needed.
17992      * @param {String} query The SQL query to execute
17993      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17994      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17995      * saved in the current store (defaults to false)
17996      */
17997     doQuery : function(q, forceAll){
17998         
17999         if(q === undefined || q === null){
18000             q = '';
18001         }
18002         var qe = {
18003             query: q,
18004             forceAll: forceAll,
18005             combo: this,
18006             cancel:false
18007         };
18008         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18009             return false;
18010         }
18011         q = qe.query;
18012         
18013         forceAll = qe.forceAll;
18014         if(forceAll === true || (q.length >= this.minChars)){
18015             
18016             this.hasQuery = true;
18017             
18018             if(this.lastQuery != q || this.alwaysQuery){
18019                 this.lastQuery = q;
18020                 if(this.mode == 'local'){
18021                     this.selectedIndex = -1;
18022                     if(forceAll){
18023                         this.store.clearFilter();
18024                     }else{
18025                         
18026                         if(this.specialFilter){
18027                             this.fireEvent('specialfilter', this);
18028                             this.onLoad();
18029                             return;
18030                         }
18031                         
18032                         this.store.filter(this.displayField, q);
18033                     }
18034                     
18035                     this.store.fireEvent("datachanged", this.store);
18036                     
18037                     this.onLoad();
18038                     
18039                     
18040                 }else{
18041                     
18042                     this.store.baseParams[this.queryParam] = q;
18043                     
18044                     var options = {params : this.getParams(q)};
18045                     
18046                     if(this.loadNext){
18047                         options.add = true;
18048                         options.params.start = this.page * this.pageSize;
18049                     }
18050                     
18051                     this.store.load(options);
18052                     
18053                     /*
18054                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18055                      *  we should expand the list on onLoad
18056                      *  so command out it
18057                      */
18058 //                    this.expand();
18059                 }
18060             }else{
18061                 this.selectedIndex = -1;
18062                 this.onLoad();   
18063             }
18064         }
18065         
18066         this.loadNext = false;
18067     },
18068     
18069     // private
18070     getParams : function(q){
18071         var p = {};
18072         //p[this.queryParam] = q;
18073         
18074         if(this.pageSize){
18075             p.start = 0;
18076             p.limit = this.pageSize;
18077         }
18078         return p;
18079     },
18080
18081     /**
18082      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18083      */
18084     collapse : function(){
18085         if(!this.isExpanded()){
18086             return;
18087         }
18088         
18089         this.list.hide();
18090         
18091         this.hasFocus = false;
18092         
18093         if(this.tickable){
18094             this.okBtn.hide();
18095             this.cancelBtn.hide();
18096             this.trigger.show();
18097             
18098             if(this.editable){
18099                 this.tickableInputEl().dom.value = '';
18100                 this.tickableInputEl().blur();
18101             }
18102             
18103         }
18104         
18105         Roo.get(document).un('mousedown', this.collapseIf, this);
18106         Roo.get(document).un('mousewheel', this.collapseIf, this);
18107         if (!this.editable) {
18108             Roo.get(document).un('keydown', this.listKeyPress, this);
18109         }
18110         this.fireEvent('collapse', this);
18111         
18112         this.validate();
18113     },
18114
18115     // private
18116     collapseIf : function(e){
18117         var in_combo  = e.within(this.el);
18118         var in_list =  e.within(this.list);
18119         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18120         
18121         if (in_combo || in_list || is_list) {
18122             //e.stopPropagation();
18123             return;
18124         }
18125         
18126         if(this.tickable){
18127             this.onTickableFooterButtonClick(e, false, false);
18128         }
18129
18130         this.collapse();
18131         
18132     },
18133
18134     /**
18135      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18136      */
18137     expand : function(){
18138        
18139         if(this.isExpanded() || !this.hasFocus){
18140             return;
18141         }
18142         
18143         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18144         this.list.setWidth(lw);
18145         
18146         Roo.log('expand');
18147         
18148         this.list.show();
18149         
18150         this.restrictHeight();
18151         
18152         if(this.tickable){
18153             
18154             this.tickItems = Roo.apply([], this.item);
18155             
18156             this.okBtn.show();
18157             this.cancelBtn.show();
18158             this.trigger.hide();
18159             
18160             if(this.editable){
18161                 this.tickableInputEl().focus();
18162             }
18163             
18164         }
18165         
18166         Roo.get(document).on('mousedown', this.collapseIf, this);
18167         Roo.get(document).on('mousewheel', this.collapseIf, this);
18168         if (!this.editable) {
18169             Roo.get(document).on('keydown', this.listKeyPress, this);
18170         }
18171         
18172         this.fireEvent('expand', this);
18173     },
18174
18175     // private
18176     // Implements the default empty TriggerField.onTriggerClick function
18177     onTriggerClick : function(e)
18178     {
18179         Roo.log('trigger click');
18180         
18181         if(this.disabled || !this.triggerList){
18182             return;
18183         }
18184         
18185         this.page = 0;
18186         this.loadNext = false;
18187         
18188         if(this.isExpanded()){
18189             this.collapse();
18190             if (!this.blockFocus) {
18191                 this.inputEl().focus();
18192             }
18193             
18194         }else {
18195             this.hasFocus = true;
18196             if(this.triggerAction == 'all') {
18197                 this.doQuery(this.allQuery, true);
18198             } else {
18199                 this.doQuery(this.getRawValue());
18200             }
18201             if (!this.blockFocus) {
18202                 this.inputEl().focus();
18203             }
18204         }
18205     },
18206     
18207     onTickableTriggerClick : function(e)
18208     {
18209         if(this.disabled){
18210             return;
18211         }
18212         
18213         this.page = 0;
18214         this.loadNext = false;
18215         this.hasFocus = true;
18216         
18217         if(this.triggerAction == 'all') {
18218             this.doQuery(this.allQuery, true);
18219         } else {
18220             this.doQuery(this.getRawValue());
18221         }
18222     },
18223     
18224     onSearchFieldClick : function(e)
18225     {
18226         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18227             this.onTickableFooterButtonClick(e, false, false);
18228             return;
18229         }
18230         
18231         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18232             return;
18233         }
18234         
18235         this.page = 0;
18236         this.loadNext = false;
18237         this.hasFocus = true;
18238         
18239         if(this.triggerAction == 'all') {
18240             this.doQuery(this.allQuery, true);
18241         } else {
18242             this.doQuery(this.getRawValue());
18243         }
18244     },
18245     
18246     listKeyPress : function(e)
18247     {
18248         //Roo.log('listkeypress');
18249         // scroll to first matching element based on key pres..
18250         if (e.isSpecialKey()) {
18251             return false;
18252         }
18253         var k = String.fromCharCode(e.getKey()).toUpperCase();
18254         //Roo.log(k);
18255         var match  = false;
18256         var csel = this.view.getSelectedNodes();
18257         var cselitem = false;
18258         if (csel.length) {
18259             var ix = this.view.indexOf(csel[0]);
18260             cselitem  = this.store.getAt(ix);
18261             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18262                 cselitem = false;
18263             }
18264             
18265         }
18266         
18267         this.store.each(function(v) { 
18268             if (cselitem) {
18269                 // start at existing selection.
18270                 if (cselitem.id == v.id) {
18271                     cselitem = false;
18272                 }
18273                 return true;
18274             }
18275                 
18276             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18277                 match = this.store.indexOf(v);
18278                 return false;
18279             }
18280             return true;
18281         }, this);
18282         
18283         if (match === false) {
18284             return true; // no more action?
18285         }
18286         // scroll to?
18287         this.view.select(match);
18288         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18289         sn.scrollIntoView(sn.dom.parentNode, false);
18290     },
18291     
18292     onViewScroll : function(e, t){
18293         
18294         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){
18295             return;
18296         }
18297         
18298         this.hasQuery = true;
18299         
18300         this.loading = this.list.select('.loading', true).first();
18301         
18302         if(this.loading === null){
18303             this.list.createChild({
18304                 tag: 'div',
18305                 cls: 'loading roo-select2-more-results roo-select2-active',
18306                 html: 'Loading more results...'
18307             });
18308             
18309             this.loading = this.list.select('.loading', true).first();
18310             
18311             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18312             
18313             this.loading.hide();
18314         }
18315         
18316         this.loading.show();
18317         
18318         var _combo = this;
18319         
18320         this.page++;
18321         this.loadNext = true;
18322         
18323         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18324         
18325         return;
18326     },
18327     
18328     addItem : function(o)
18329     {   
18330         var dv = ''; // display value
18331         
18332         if (this.displayField) {
18333             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18334         } else {
18335             // this is an error condition!!!
18336             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18337         }
18338         
18339         if(!dv.length){
18340             return;
18341         }
18342         
18343         var choice = this.choices.createChild({
18344             tag: 'li',
18345             cls: 'roo-select2-search-choice',
18346             cn: [
18347                 {
18348                     tag: 'div',
18349                     html: dv
18350                 },
18351                 {
18352                     tag: 'a',
18353                     href: '#',
18354                     cls: 'roo-select2-search-choice-close fa fa-times',
18355                     tabindex: '-1'
18356                 }
18357             ]
18358             
18359         }, this.searchField);
18360         
18361         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18362         
18363         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18364         
18365         this.item.push(o);
18366         
18367         this.lastData = o;
18368         
18369         this.syncValue();
18370         
18371         this.inputEl().dom.value = '';
18372         
18373         this.validate();
18374     },
18375     
18376     onRemoveItem : function(e, _self, o)
18377     {
18378         e.preventDefault();
18379         
18380         this.lastItem = Roo.apply([], this.item);
18381         
18382         var index = this.item.indexOf(o.data) * 1;
18383         
18384         if( index < 0){
18385             Roo.log('not this item?!');
18386             return;
18387         }
18388         
18389         this.item.splice(index, 1);
18390         o.item.remove();
18391         
18392         this.syncValue();
18393         
18394         this.fireEvent('remove', this, e);
18395         
18396         this.validate();
18397         
18398     },
18399     
18400     syncValue : function()
18401     {
18402         if(!this.item.length){
18403             this.clearValue();
18404             return;
18405         }
18406             
18407         var value = [];
18408         var _this = this;
18409         Roo.each(this.item, function(i){
18410             if(_this.valueField){
18411                 value.push(i[_this.valueField]);
18412                 return;
18413             }
18414
18415             value.push(i);
18416         });
18417
18418         this.value = value.join(',');
18419
18420         if(this.hiddenField){
18421             this.hiddenField.dom.value = this.value;
18422         }
18423         
18424         this.store.fireEvent("datachanged", this.store);
18425         
18426         this.validate();
18427     },
18428     
18429     clearItem : function()
18430     {
18431         if(!this.multiple){
18432             return;
18433         }
18434         
18435         this.item = [];
18436         
18437         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18438            c.remove();
18439         });
18440         
18441         this.syncValue();
18442         
18443         this.validate();
18444         
18445         if(this.tickable && !Roo.isTouch){
18446             this.view.refresh();
18447         }
18448     },
18449     
18450     inputEl: function ()
18451     {
18452         if(Roo.isIOS && this.useNativeIOS){
18453             return this.el.select('select.roo-ios-select', true).first();
18454         }
18455         
18456         if(Roo.isTouch && this.mobileTouchView){
18457             return this.el.select('input.form-control',true).first();
18458         }
18459         
18460         if(this.tickable){
18461             return this.searchField;
18462         }
18463         
18464         return this.el.select('input.form-control',true).first();
18465     },
18466     
18467     onTickableFooterButtonClick : function(e, btn, el)
18468     {
18469         e.preventDefault();
18470         
18471         this.lastItem = Roo.apply([], this.item);
18472         
18473         if(btn && btn.name == 'cancel'){
18474             this.tickItems = Roo.apply([], this.item);
18475             this.collapse();
18476             return;
18477         }
18478         
18479         this.clearItem();
18480         
18481         var _this = this;
18482         
18483         Roo.each(this.tickItems, function(o){
18484             _this.addItem(o);
18485         });
18486         
18487         this.collapse();
18488         
18489     },
18490     
18491     validate : function()
18492     {
18493         if(this.getVisibilityEl().hasClass('hidden')){
18494             return true;
18495         }
18496         
18497         var v = this.getRawValue();
18498         
18499         if(this.multiple){
18500             v = this.getValue();
18501         }
18502         
18503         if(this.disabled || this.allowBlank || v.length){
18504             this.markValid();
18505             return true;
18506         }
18507         
18508         this.markInvalid();
18509         return false;
18510     },
18511     
18512     tickableInputEl : function()
18513     {
18514         if(!this.tickable || !this.editable){
18515             return this.inputEl();
18516         }
18517         
18518         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18519     },
18520     
18521     
18522     getAutoCreateTouchView : function()
18523     {
18524         var id = Roo.id();
18525         
18526         var cfg = {
18527             cls: 'form-group' //input-group
18528         };
18529         
18530         var input =  {
18531             tag: 'input',
18532             id : id,
18533             type : this.inputType,
18534             cls : 'form-control x-combo-noedit',
18535             autocomplete: 'new-password',
18536             placeholder : this.placeholder || '',
18537             readonly : true
18538         };
18539         
18540         if (this.name) {
18541             input.name = this.name;
18542         }
18543         
18544         if (this.size) {
18545             input.cls += ' input-' + this.size;
18546         }
18547         
18548         if (this.disabled) {
18549             input.disabled = true;
18550         }
18551         
18552         var inputblock = {
18553             cls : 'roo-combobox-wrap',
18554             cn : [
18555                 input
18556             ]
18557         };
18558         
18559         if(this.before){
18560             inputblock.cls += ' input-group';
18561             
18562             inputblock.cn.unshift({
18563                 tag :'span',
18564                 cls : 'input-group-addon input-group-prepend input-group-text',
18565                 html : this.before
18566             });
18567         }
18568         
18569         if(this.removable && !this.multiple){
18570             inputblock.cls += ' roo-removable';
18571             
18572             inputblock.cn.push({
18573                 tag: 'button',
18574                 html : 'x',
18575                 cls : 'roo-combo-removable-btn close'
18576             });
18577         }
18578
18579         if(this.hasFeedback && !this.allowBlank){
18580             
18581             inputblock.cls += ' has-feedback';
18582             
18583             inputblock.cn.push({
18584                 tag: 'span',
18585                 cls: 'glyphicon form-control-feedback'
18586             });
18587             
18588         }
18589         
18590         if (this.after) {
18591             
18592             inputblock.cls += (this.before) ? '' : ' input-group';
18593             
18594             inputblock.cn.push({
18595                 tag :'span',
18596                 cls : 'input-group-addon input-group-append input-group-text',
18597                 html : this.after
18598             });
18599         }
18600
18601         
18602         var ibwrap = inputblock;
18603         
18604         if(this.multiple){
18605             ibwrap = {
18606                 tag: 'ul',
18607                 cls: 'roo-select2-choices',
18608                 cn:[
18609                     {
18610                         tag: 'li',
18611                         cls: 'roo-select2-search-field',
18612                         cn: [
18613
18614                             inputblock
18615                         ]
18616                     }
18617                 ]
18618             };
18619         
18620             
18621         }
18622         
18623         var combobox = {
18624             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18625             cn: [
18626                 {
18627                     tag: 'input',
18628                     type : 'hidden',
18629                     cls: 'form-hidden-field'
18630                 },
18631                 ibwrap
18632             ]
18633         };
18634         
18635         if(!this.multiple && this.showToggleBtn){
18636             
18637             var caret = {
18638                 cls: 'caret'
18639             };
18640             
18641             if (this.caret != false) {
18642                 caret = {
18643                      tag: 'i',
18644                      cls: 'fa fa-' + this.caret
18645                 };
18646                 
18647             }
18648             
18649             combobox.cn.push({
18650                 tag :'span',
18651                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18652                 cn : [
18653                     Roo.bootstrap.version == 3 ? caret : '',
18654                     {
18655                         tag: 'span',
18656                         cls: 'combobox-clear',
18657                         cn  : [
18658                             {
18659                                 tag : 'i',
18660                                 cls: 'icon-remove'
18661                             }
18662                         ]
18663                     }
18664                 ]
18665
18666             })
18667         }
18668         
18669         if(this.multiple){
18670             combobox.cls += ' roo-select2-container-multi';
18671         }
18672         
18673         var required =  this.allowBlank ?  {
18674                     tag : 'i',
18675                     style: 'display: none'
18676                 } : {
18677                    tag : 'i',
18678                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18679                    tooltip : 'This field is required'
18680                 };
18681         
18682         var align = this.labelAlign || this.parentLabelAlign();
18683         
18684         if (align ==='left' && this.fieldLabel.length) {
18685
18686             cfg.cn = [
18687                 required,
18688                 {
18689                     tag: 'label',
18690                     cls : 'control-label col-form-label',
18691                     html : this.fieldLabel
18692
18693                 },
18694                 {
18695                     cls : 'roo-combobox-wrap ', 
18696                     cn: [
18697                         combobox
18698                     ]
18699                 }
18700             ];
18701             
18702             var labelCfg = cfg.cn[1];
18703             var contentCfg = cfg.cn[2];
18704             
18705
18706             if(this.indicatorpos == 'right'){
18707                 cfg.cn = [
18708                     {
18709                         tag: 'label',
18710                         'for' :  id,
18711                         cls : 'control-label col-form-label',
18712                         cn : [
18713                             {
18714                                 tag : 'span',
18715                                 html : this.fieldLabel
18716                             },
18717                             required
18718                         ]
18719                     },
18720                     {
18721                         cls : "roo-combobox-wrap ",
18722                         cn: [
18723                             combobox
18724                         ]
18725                     }
18726
18727                 ];
18728                 
18729                 labelCfg = cfg.cn[0];
18730                 contentCfg = cfg.cn[1];
18731             }
18732             
18733            
18734             
18735             if(this.labelWidth > 12){
18736                 labelCfg.style = "width: " + this.labelWidth + 'px';
18737             }
18738            
18739             if(this.labelWidth < 13 && this.labelmd == 0){
18740                 this.labelmd = this.labelWidth;
18741             }
18742             
18743             if(this.labellg > 0){
18744                 labelCfg.cls += ' col-lg-' + this.labellg;
18745                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18746             }
18747             
18748             if(this.labelmd > 0){
18749                 labelCfg.cls += ' col-md-' + this.labelmd;
18750                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18751             }
18752             
18753             if(this.labelsm > 0){
18754                 labelCfg.cls += ' col-sm-' + this.labelsm;
18755                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18756             }
18757             
18758             if(this.labelxs > 0){
18759                 labelCfg.cls += ' col-xs-' + this.labelxs;
18760                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18761             }
18762                 
18763                 
18764         } else if ( this.fieldLabel.length) {
18765             cfg.cn = [
18766                required,
18767                 {
18768                     tag: 'label',
18769                     cls : 'control-label',
18770                     html : this.fieldLabel
18771
18772                 },
18773                 {
18774                     cls : '', 
18775                     cn: [
18776                         combobox
18777                     ]
18778                 }
18779             ];
18780             
18781             if(this.indicatorpos == 'right'){
18782                 cfg.cn = [
18783                     {
18784                         tag: 'label',
18785                         cls : 'control-label',
18786                         html : this.fieldLabel,
18787                         cn : [
18788                             required
18789                         ]
18790                     },
18791                     {
18792                         cls : '', 
18793                         cn: [
18794                             combobox
18795                         ]
18796                     }
18797                 ];
18798             }
18799         } else {
18800             cfg.cn = combobox;    
18801         }
18802         
18803         
18804         var settings = this;
18805         
18806         ['xs','sm','md','lg'].map(function(size){
18807             if (settings[size]) {
18808                 cfg.cls += ' col-' + size + '-' + settings[size];
18809             }
18810         });
18811         
18812         return cfg;
18813     },
18814     
18815     initTouchView : function()
18816     {
18817         this.renderTouchView();
18818         
18819         this.touchViewEl.on('scroll', function(){
18820             this.el.dom.scrollTop = 0;
18821         }, this);
18822         
18823         this.originalValue = this.getValue();
18824         
18825         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18826         
18827         this.inputEl().on("click", this.showTouchView, this);
18828         if (this.triggerEl) {
18829             this.triggerEl.on("click", this.showTouchView, this);
18830         }
18831         
18832         
18833         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18834         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18835         
18836         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18837         
18838         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18839         this.store.on('load', this.onTouchViewLoad, this);
18840         this.store.on('loadexception', this.onTouchViewLoadException, this);
18841         
18842         if(this.hiddenName){
18843             
18844             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18845             
18846             this.hiddenField.dom.value =
18847                 this.hiddenValue !== undefined ? this.hiddenValue :
18848                 this.value !== undefined ? this.value : '';
18849         
18850             this.el.dom.removeAttribute('name');
18851             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18852         }
18853         
18854         if(this.multiple){
18855             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18856             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18857         }
18858         
18859         if(this.removable && !this.multiple){
18860             var close = this.closeTriggerEl();
18861             if(close){
18862                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18863                 close.on('click', this.removeBtnClick, this, close);
18864             }
18865         }
18866         /*
18867          * fix the bug in Safari iOS8
18868          */
18869         this.inputEl().on("focus", function(e){
18870             document.activeElement.blur();
18871         }, this);
18872         
18873         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18874         
18875         return;
18876         
18877         
18878     },
18879     
18880     renderTouchView : function()
18881     {
18882         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18883         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18884         
18885         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18886         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18887         
18888         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18889         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18890         this.touchViewBodyEl.setStyle('overflow', 'auto');
18891         
18892         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18893         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18894         
18895         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18896         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18897         
18898     },
18899     
18900     showTouchView : function()
18901     {
18902         if(this.disabled){
18903             return;
18904         }
18905         
18906         this.touchViewHeaderEl.hide();
18907
18908         if(this.modalTitle.length){
18909             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18910             this.touchViewHeaderEl.show();
18911         }
18912
18913         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18914         this.touchViewEl.show();
18915
18916         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18917         
18918         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18919         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18920
18921         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18922
18923         if(this.modalTitle.length){
18924             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18925         }
18926         
18927         this.touchViewBodyEl.setHeight(bodyHeight);
18928
18929         if(this.animate){
18930             var _this = this;
18931             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18932         }else{
18933             this.touchViewEl.addClass(['in','show']);
18934         }
18935         
18936         if(this._touchViewMask){
18937             Roo.get(document.body).addClass("x-body-masked");
18938             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18939             this._touchViewMask.setStyle('z-index', 10000);
18940             this._touchViewMask.addClass('show');
18941         }
18942         
18943         this.doTouchViewQuery();
18944         
18945     },
18946     
18947     hideTouchView : function()
18948     {
18949         this.touchViewEl.removeClass(['in','show']);
18950
18951         if(this.animate){
18952             var _this = this;
18953             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18954         }else{
18955             this.touchViewEl.setStyle('display', 'none');
18956         }
18957         
18958         if(this._touchViewMask){
18959             this._touchViewMask.removeClass('show');
18960             Roo.get(document.body).removeClass("x-body-masked");
18961         }
18962     },
18963     
18964     setTouchViewValue : function()
18965     {
18966         if(this.multiple){
18967             this.clearItem();
18968         
18969             var _this = this;
18970
18971             Roo.each(this.tickItems, function(o){
18972                 this.addItem(o);
18973             }, this);
18974         }
18975         
18976         this.hideTouchView();
18977     },
18978     
18979     doTouchViewQuery : function()
18980     {
18981         var qe = {
18982             query: '',
18983             forceAll: true,
18984             combo: this,
18985             cancel:false
18986         };
18987         
18988         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18989             return false;
18990         }
18991         
18992         if(!this.alwaysQuery || this.mode == 'local'){
18993             this.onTouchViewLoad();
18994             return;
18995         }
18996         
18997         this.store.load();
18998     },
18999     
19000     onTouchViewBeforeLoad : function(combo,opts)
19001     {
19002         return;
19003     },
19004
19005     // private
19006     onTouchViewLoad : function()
19007     {
19008         if(this.store.getCount() < 1){
19009             this.onTouchViewEmptyResults();
19010             return;
19011         }
19012         
19013         this.clearTouchView();
19014         
19015         var rawValue = this.getRawValue();
19016         
19017         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19018         
19019         this.tickItems = [];
19020         
19021         this.store.data.each(function(d, rowIndex){
19022             var row = this.touchViewListGroup.createChild(template);
19023             
19024             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19025                 row.addClass(d.data.cls);
19026             }
19027             
19028             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19029                 var cfg = {
19030                     data : d.data,
19031                     html : d.data[this.displayField]
19032                 };
19033                 
19034                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19035                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19036                 }
19037             }
19038             row.removeClass('selected');
19039             if(!this.multiple && this.valueField &&
19040                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19041             {
19042                 // radio buttons..
19043                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19044                 row.addClass('selected');
19045             }
19046             
19047             if(this.multiple && this.valueField &&
19048                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19049             {
19050                 
19051                 // checkboxes...
19052                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19053                 this.tickItems.push(d.data);
19054             }
19055             
19056             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19057             
19058         }, this);
19059         
19060         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19061         
19062         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19063
19064         if(this.modalTitle.length){
19065             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19066         }
19067
19068         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19069         
19070         if(this.mobile_restrict_height && listHeight < bodyHeight){
19071             this.touchViewBodyEl.setHeight(listHeight);
19072         }
19073         
19074         var _this = this;
19075         
19076         if(firstChecked && listHeight > bodyHeight){
19077             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19078         }
19079         
19080     },
19081     
19082     onTouchViewLoadException : function()
19083     {
19084         this.hideTouchView();
19085     },
19086     
19087     onTouchViewEmptyResults : function()
19088     {
19089         this.clearTouchView();
19090         
19091         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19092         
19093         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19094         
19095     },
19096     
19097     clearTouchView : function()
19098     {
19099         this.touchViewListGroup.dom.innerHTML = '';
19100     },
19101     
19102     onTouchViewClick : function(e, el, o)
19103     {
19104         e.preventDefault();
19105         
19106         var row = o.row;
19107         var rowIndex = o.rowIndex;
19108         
19109         var r = this.store.getAt(rowIndex);
19110         
19111         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19112             
19113             if(!this.multiple){
19114                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19115                     c.dom.removeAttribute('checked');
19116                 }, this);
19117
19118                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19119
19120                 this.setFromData(r.data);
19121
19122                 var close = this.closeTriggerEl();
19123
19124                 if(close){
19125                     close.show();
19126                 }
19127
19128                 this.hideTouchView();
19129
19130                 this.fireEvent('select', this, r, rowIndex);
19131
19132                 return;
19133             }
19134
19135             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19136                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19137                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19138                 return;
19139             }
19140
19141             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19142             this.addItem(r.data);
19143             this.tickItems.push(r.data);
19144         }
19145     },
19146     
19147     getAutoCreateNativeIOS : function()
19148     {
19149         var cfg = {
19150             cls: 'form-group' //input-group,
19151         };
19152         
19153         var combobox =  {
19154             tag: 'select',
19155             cls : 'roo-ios-select'
19156         };
19157         
19158         if (this.name) {
19159             combobox.name = this.name;
19160         }
19161         
19162         if (this.disabled) {
19163             combobox.disabled = true;
19164         }
19165         
19166         var settings = this;
19167         
19168         ['xs','sm','md','lg'].map(function(size){
19169             if (settings[size]) {
19170                 cfg.cls += ' col-' + size + '-' + settings[size];
19171             }
19172         });
19173         
19174         cfg.cn = combobox;
19175         
19176         return cfg;
19177         
19178     },
19179     
19180     initIOSView : function()
19181     {
19182         this.store.on('load', this.onIOSViewLoad, this);
19183         
19184         return;
19185     },
19186     
19187     onIOSViewLoad : function()
19188     {
19189         if(this.store.getCount() < 1){
19190             return;
19191         }
19192         
19193         this.clearIOSView();
19194         
19195         if(this.allowBlank) {
19196             
19197             var default_text = '-- SELECT --';
19198             
19199             if(this.placeholder.length){
19200                 default_text = this.placeholder;
19201             }
19202             
19203             if(this.emptyTitle.length){
19204                 default_text += ' - ' + this.emptyTitle + ' -';
19205             }
19206             
19207             var opt = this.inputEl().createChild({
19208                 tag: 'option',
19209                 value : 0,
19210                 html : default_text
19211             });
19212             
19213             var o = {};
19214             o[this.valueField] = 0;
19215             o[this.displayField] = default_text;
19216             
19217             this.ios_options.push({
19218                 data : o,
19219                 el : opt
19220             });
19221             
19222         }
19223         
19224         this.store.data.each(function(d, rowIndex){
19225             
19226             var html = '';
19227             
19228             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19229                 html = d.data[this.displayField];
19230             }
19231             
19232             var value = '';
19233             
19234             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19235                 value = d.data[this.valueField];
19236             }
19237             
19238             var option = {
19239                 tag: 'option',
19240                 value : value,
19241                 html : html
19242             };
19243             
19244             if(this.value == d.data[this.valueField]){
19245                 option['selected'] = true;
19246             }
19247             
19248             var opt = this.inputEl().createChild(option);
19249             
19250             this.ios_options.push({
19251                 data : d.data,
19252                 el : opt
19253             });
19254             
19255         }, this);
19256         
19257         this.inputEl().on('change', function(){
19258            this.fireEvent('select', this);
19259         }, this);
19260         
19261     },
19262     
19263     clearIOSView: function()
19264     {
19265         this.inputEl().dom.innerHTML = '';
19266         
19267         this.ios_options = [];
19268     },
19269     
19270     setIOSValue: function(v)
19271     {
19272         this.value = v;
19273         
19274         if(!this.ios_options){
19275             return;
19276         }
19277         
19278         Roo.each(this.ios_options, function(opts){
19279            
19280            opts.el.dom.removeAttribute('selected');
19281            
19282            if(opts.data[this.valueField] != v){
19283                return;
19284            }
19285            
19286            opts.el.dom.setAttribute('selected', true);
19287            
19288         }, this);
19289     }
19290
19291     /** 
19292     * @cfg {Boolean} grow 
19293     * @hide 
19294     */
19295     /** 
19296     * @cfg {Number} growMin 
19297     * @hide 
19298     */
19299     /** 
19300     * @cfg {Number} growMax 
19301     * @hide 
19302     */
19303     /**
19304      * @hide
19305      * @method autoSize
19306      */
19307 });
19308
19309 Roo.apply(Roo.bootstrap.ComboBox,  {
19310     
19311     header : {
19312         tag: 'div',
19313         cls: 'modal-header',
19314         cn: [
19315             {
19316                 tag: 'h4',
19317                 cls: 'modal-title'
19318             }
19319         ]
19320     },
19321     
19322     body : {
19323         tag: 'div',
19324         cls: 'modal-body',
19325         cn: [
19326             {
19327                 tag: 'ul',
19328                 cls: 'list-group'
19329             }
19330         ]
19331     },
19332     
19333     listItemRadio : {
19334         tag: 'li',
19335         cls: 'list-group-item',
19336         cn: [
19337             {
19338                 tag: 'span',
19339                 cls: 'roo-combobox-list-group-item-value'
19340             },
19341             {
19342                 tag: 'div',
19343                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19344                 cn: [
19345                     {
19346                         tag: 'input',
19347                         type: 'radio'
19348                     },
19349                     {
19350                         tag: 'label'
19351                     }
19352                 ]
19353             }
19354         ]
19355     },
19356     
19357     listItemCheckbox : {
19358         tag: 'li',
19359         cls: 'list-group-item',
19360         cn: [
19361             {
19362                 tag: 'span',
19363                 cls: 'roo-combobox-list-group-item-value'
19364             },
19365             {
19366                 tag: 'div',
19367                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19368                 cn: [
19369                     {
19370                         tag: 'input',
19371                         type: 'checkbox'
19372                     },
19373                     {
19374                         tag: 'label'
19375                     }
19376                 ]
19377             }
19378         ]
19379     },
19380     
19381     emptyResult : {
19382         tag: 'div',
19383         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19384     },
19385     
19386     footer : {
19387         tag: 'div',
19388         cls: 'modal-footer',
19389         cn: [
19390             {
19391                 tag: 'div',
19392                 cls: 'row',
19393                 cn: [
19394                     {
19395                         tag: 'div',
19396                         cls: 'col-xs-6 text-left',
19397                         cn: {
19398                             tag: 'button',
19399                             cls: 'btn btn-danger roo-touch-view-cancel',
19400                             html: 'Cancel'
19401                         }
19402                     },
19403                     {
19404                         tag: 'div',
19405                         cls: 'col-xs-6 text-right',
19406                         cn: {
19407                             tag: 'button',
19408                             cls: 'btn btn-success roo-touch-view-ok',
19409                             html: 'OK'
19410                         }
19411                     }
19412                 ]
19413             }
19414         ]
19415         
19416     }
19417 });
19418
19419 Roo.apply(Roo.bootstrap.ComboBox,  {
19420     
19421     touchViewTemplate : {
19422         tag: 'div',
19423         cls: 'modal fade roo-combobox-touch-view',
19424         cn: [
19425             {
19426                 tag: 'div',
19427                 cls: 'modal-dialog',
19428                 style : 'position:fixed', // we have to fix position....
19429                 cn: [
19430                     {
19431                         tag: 'div',
19432                         cls: 'modal-content',
19433                         cn: [
19434                             Roo.bootstrap.ComboBox.header,
19435                             Roo.bootstrap.ComboBox.body,
19436                             Roo.bootstrap.ComboBox.footer
19437                         ]
19438                     }
19439                 ]
19440             }
19441         ]
19442     }
19443 });/*
19444  * Based on:
19445  * Ext JS Library 1.1.1
19446  * Copyright(c) 2006-2007, Ext JS, LLC.
19447  *
19448  * Originally Released Under LGPL - original licence link has changed is not relivant.
19449  *
19450  * Fork - LGPL
19451  * <script type="text/javascript">
19452  */
19453
19454 /**
19455  * @class Roo.View
19456  * @extends Roo.util.Observable
19457  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19458  * This class also supports single and multi selection modes. <br>
19459  * Create a data model bound view:
19460  <pre><code>
19461  var store = new Roo.data.Store(...);
19462
19463  var view = new Roo.View({
19464     el : "my-element",
19465     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19466  
19467     singleSelect: true,
19468     selectedClass: "ydataview-selected",
19469     store: store
19470  });
19471
19472  // listen for node click?
19473  view.on("click", function(vw, index, node, e){
19474  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19475  });
19476
19477  // load XML data
19478  dataModel.load("foobar.xml");
19479  </code></pre>
19480  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19481  * <br><br>
19482  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19483  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19484  * 
19485  * Note: old style constructor is still suported (container, template, config)
19486  * 
19487  * @constructor
19488  * Create a new View
19489  * @param {Object} config The config object
19490  * 
19491  */
19492 Roo.View = function(config, depreciated_tpl, depreciated_config){
19493     
19494     this.parent = false;
19495     
19496     if (typeof(depreciated_tpl) == 'undefined') {
19497         // new way.. - universal constructor.
19498         Roo.apply(this, config);
19499         this.el  = Roo.get(this.el);
19500     } else {
19501         // old format..
19502         this.el  = Roo.get(config);
19503         this.tpl = depreciated_tpl;
19504         Roo.apply(this, depreciated_config);
19505     }
19506     this.wrapEl  = this.el.wrap().wrap();
19507     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19508     
19509     
19510     if(typeof(this.tpl) == "string"){
19511         this.tpl = new Roo.Template(this.tpl);
19512     } else {
19513         // support xtype ctors..
19514         this.tpl = new Roo.factory(this.tpl, Roo);
19515     }
19516     
19517     
19518     this.tpl.compile();
19519     
19520     /** @private */
19521     this.addEvents({
19522         /**
19523          * @event beforeclick
19524          * Fires before a click is processed. Returns false to cancel the default action.
19525          * @param {Roo.View} this
19526          * @param {Number} index The index of the target node
19527          * @param {HTMLElement} node The target node
19528          * @param {Roo.EventObject} e The raw event object
19529          */
19530             "beforeclick" : true,
19531         /**
19532          * @event click
19533          * Fires when a template node is clicked.
19534          * @param {Roo.View} this
19535          * @param {Number} index The index of the target node
19536          * @param {HTMLElement} node The target node
19537          * @param {Roo.EventObject} e The raw event object
19538          */
19539             "click" : true,
19540         /**
19541          * @event dblclick
19542          * Fires when a template node is double clicked.
19543          * @param {Roo.View} this
19544          * @param {Number} index The index of the target node
19545          * @param {HTMLElement} node The target node
19546          * @param {Roo.EventObject} e The raw event object
19547          */
19548             "dblclick" : true,
19549         /**
19550          * @event contextmenu
19551          * Fires when a template node is right clicked.
19552          * @param {Roo.View} this
19553          * @param {Number} index The index of the target node
19554          * @param {HTMLElement} node The target node
19555          * @param {Roo.EventObject} e The raw event object
19556          */
19557             "contextmenu" : true,
19558         /**
19559          * @event selectionchange
19560          * Fires when the selected nodes change.
19561          * @param {Roo.View} this
19562          * @param {Array} selections Array of the selected nodes
19563          */
19564             "selectionchange" : true,
19565     
19566         /**
19567          * @event beforeselect
19568          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19569          * @param {Roo.View} this
19570          * @param {HTMLElement} node The node to be selected
19571          * @param {Array} selections Array of currently selected nodes
19572          */
19573             "beforeselect" : true,
19574         /**
19575          * @event preparedata
19576          * Fires on every row to render, to allow you to change the data.
19577          * @param {Roo.View} this
19578          * @param {Object} data to be rendered (change this)
19579          */
19580           "preparedata" : true
19581           
19582           
19583         });
19584
19585
19586
19587     this.el.on({
19588         "click": this.onClick,
19589         "dblclick": this.onDblClick,
19590         "contextmenu": this.onContextMenu,
19591         scope:this
19592     });
19593
19594     this.selections = [];
19595     this.nodes = [];
19596     this.cmp = new Roo.CompositeElementLite([]);
19597     if(this.store){
19598         this.store = Roo.factory(this.store, Roo.data);
19599         this.setStore(this.store, true);
19600     }
19601     
19602     if ( this.footer && this.footer.xtype) {
19603            
19604          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19605         
19606         this.footer.dataSource = this.store;
19607         this.footer.container = fctr;
19608         this.footer = Roo.factory(this.footer, Roo);
19609         fctr.insertFirst(this.el);
19610         
19611         // this is a bit insane - as the paging toolbar seems to detach the el..
19612 //        dom.parentNode.parentNode.parentNode
19613          // they get detached?
19614     }
19615     
19616     
19617     Roo.View.superclass.constructor.call(this);
19618     
19619     
19620 };
19621
19622 Roo.extend(Roo.View, Roo.util.Observable, {
19623     
19624      /**
19625      * @cfg {Roo.data.Store} store Data store to load data from.
19626      */
19627     store : false,
19628     
19629     /**
19630      * @cfg {String|Roo.Element} el The container element.
19631      */
19632     el : '',
19633     
19634     /**
19635      * @cfg {String|Roo.Template} tpl The template used by this View 
19636      */
19637     tpl : false,
19638     /**
19639      * @cfg {String} dataName the named area of the template to use as the data area
19640      *                          Works with domtemplates roo-name="name"
19641      */
19642     dataName: false,
19643     /**
19644      * @cfg {String} selectedClass The css class to add to selected nodes
19645      */
19646     selectedClass : "x-view-selected",
19647      /**
19648      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19649      */
19650     emptyText : "",
19651     
19652     /**
19653      * @cfg {String} text to display on mask (default Loading)
19654      */
19655     mask : false,
19656     /**
19657      * @cfg {Boolean} multiSelect Allow multiple selection
19658      */
19659     multiSelect : false,
19660     /**
19661      * @cfg {Boolean} singleSelect Allow single selection
19662      */
19663     singleSelect:  false,
19664     
19665     /**
19666      * @cfg {Boolean} toggleSelect - selecting 
19667      */
19668     toggleSelect : false,
19669     
19670     /**
19671      * @cfg {Boolean} tickable - selecting 
19672      */
19673     tickable : false,
19674     
19675     /**
19676      * Returns the element this view is bound to.
19677      * @return {Roo.Element}
19678      */
19679     getEl : function(){
19680         return this.wrapEl;
19681     },
19682     
19683     
19684
19685     /**
19686      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19687      */
19688     refresh : function(){
19689         //Roo.log('refresh');
19690         var t = this.tpl;
19691         
19692         // if we are using something like 'domtemplate', then
19693         // the what gets used is:
19694         // t.applySubtemplate(NAME, data, wrapping data..)
19695         // the outer template then get' applied with
19696         //     the store 'extra data'
19697         // and the body get's added to the
19698         //      roo-name="data" node?
19699         //      <span class='roo-tpl-{name}'></span> ?????
19700         
19701         
19702         
19703         this.clearSelections();
19704         this.el.update("");
19705         var html = [];
19706         var records = this.store.getRange();
19707         if(records.length < 1) {
19708             
19709             // is this valid??  = should it render a template??
19710             
19711             this.el.update(this.emptyText);
19712             return;
19713         }
19714         var el = this.el;
19715         if (this.dataName) {
19716             this.el.update(t.apply(this.store.meta)); //????
19717             el = this.el.child('.roo-tpl-' + this.dataName);
19718         }
19719         
19720         for(var i = 0, len = records.length; i < len; i++){
19721             var data = this.prepareData(records[i].data, i, records[i]);
19722             this.fireEvent("preparedata", this, data, i, records[i]);
19723             
19724             var d = Roo.apply({}, data);
19725             
19726             if(this.tickable){
19727                 Roo.apply(d, {'roo-id' : Roo.id()});
19728                 
19729                 var _this = this;
19730             
19731                 Roo.each(this.parent.item, function(item){
19732                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19733                         return;
19734                     }
19735                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19736                 });
19737             }
19738             
19739             html[html.length] = Roo.util.Format.trim(
19740                 this.dataName ?
19741                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19742                     t.apply(d)
19743             );
19744         }
19745         
19746         
19747         
19748         el.update(html.join(""));
19749         this.nodes = el.dom.childNodes;
19750         this.updateIndexes(0);
19751     },
19752     
19753
19754     /**
19755      * Function to override to reformat the data that is sent to
19756      * the template for each node.
19757      * DEPRICATED - use the preparedata event handler.
19758      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19759      * a JSON object for an UpdateManager bound view).
19760      */
19761     prepareData : function(data, index, record)
19762     {
19763         this.fireEvent("preparedata", this, data, index, record);
19764         return data;
19765     },
19766
19767     onUpdate : function(ds, record){
19768         // Roo.log('on update');   
19769         this.clearSelections();
19770         var index = this.store.indexOf(record);
19771         var n = this.nodes[index];
19772         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19773         n.parentNode.removeChild(n);
19774         this.updateIndexes(index, index);
19775     },
19776
19777     
19778     
19779 // --------- FIXME     
19780     onAdd : function(ds, records, index)
19781     {
19782         //Roo.log(['on Add', ds, records, index] );        
19783         this.clearSelections();
19784         if(this.nodes.length == 0){
19785             this.refresh();
19786             return;
19787         }
19788         var n = this.nodes[index];
19789         for(var i = 0, len = records.length; i < len; i++){
19790             var d = this.prepareData(records[i].data, i, records[i]);
19791             if(n){
19792                 this.tpl.insertBefore(n, d);
19793             }else{
19794                 
19795                 this.tpl.append(this.el, d);
19796             }
19797         }
19798         this.updateIndexes(index);
19799     },
19800
19801     onRemove : function(ds, record, index){
19802        // Roo.log('onRemove');
19803         this.clearSelections();
19804         var el = this.dataName  ?
19805             this.el.child('.roo-tpl-' + this.dataName) :
19806             this.el; 
19807         
19808         el.dom.removeChild(this.nodes[index]);
19809         this.updateIndexes(index);
19810     },
19811
19812     /**
19813      * Refresh an individual node.
19814      * @param {Number} index
19815      */
19816     refreshNode : function(index){
19817         this.onUpdate(this.store, this.store.getAt(index));
19818     },
19819
19820     updateIndexes : function(startIndex, endIndex){
19821         var ns = this.nodes;
19822         startIndex = startIndex || 0;
19823         endIndex = endIndex || ns.length - 1;
19824         for(var i = startIndex; i <= endIndex; i++){
19825             ns[i].nodeIndex = i;
19826         }
19827     },
19828
19829     /**
19830      * Changes the data store this view uses and refresh the view.
19831      * @param {Store} store
19832      */
19833     setStore : function(store, initial){
19834         if(!initial && this.store){
19835             this.store.un("datachanged", this.refresh);
19836             this.store.un("add", this.onAdd);
19837             this.store.un("remove", this.onRemove);
19838             this.store.un("update", this.onUpdate);
19839             this.store.un("clear", this.refresh);
19840             this.store.un("beforeload", this.onBeforeLoad);
19841             this.store.un("load", this.onLoad);
19842             this.store.un("loadexception", this.onLoad);
19843         }
19844         if(store){
19845           
19846             store.on("datachanged", this.refresh, this);
19847             store.on("add", this.onAdd, this);
19848             store.on("remove", this.onRemove, this);
19849             store.on("update", this.onUpdate, this);
19850             store.on("clear", this.refresh, this);
19851             store.on("beforeload", this.onBeforeLoad, this);
19852             store.on("load", this.onLoad, this);
19853             store.on("loadexception", this.onLoad, this);
19854         }
19855         
19856         if(store){
19857             this.refresh();
19858         }
19859     },
19860     /**
19861      * onbeforeLoad - masks the loading area.
19862      *
19863      */
19864     onBeforeLoad : function(store,opts)
19865     {
19866          //Roo.log('onBeforeLoad');   
19867         if (!opts.add) {
19868             this.el.update("");
19869         }
19870         this.el.mask(this.mask ? this.mask : "Loading" ); 
19871     },
19872     onLoad : function ()
19873     {
19874         this.el.unmask();
19875     },
19876     
19877
19878     /**
19879      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19880      * @param {HTMLElement} node
19881      * @return {HTMLElement} The template node
19882      */
19883     findItemFromChild : function(node){
19884         var el = this.dataName  ?
19885             this.el.child('.roo-tpl-' + this.dataName,true) :
19886             this.el.dom; 
19887         
19888         if(!node || node.parentNode == el){
19889                     return node;
19890             }
19891             var p = node.parentNode;
19892             while(p && p != el){
19893             if(p.parentNode == el){
19894                 return p;
19895             }
19896             p = p.parentNode;
19897         }
19898             return null;
19899     },
19900
19901     /** @ignore */
19902     onClick : function(e){
19903         var item = this.findItemFromChild(e.getTarget());
19904         if(item){
19905             var index = this.indexOf(item);
19906             if(this.onItemClick(item, index, e) !== false){
19907                 this.fireEvent("click", this, index, item, e);
19908             }
19909         }else{
19910             this.clearSelections();
19911         }
19912     },
19913
19914     /** @ignore */
19915     onContextMenu : function(e){
19916         var item = this.findItemFromChild(e.getTarget());
19917         if(item){
19918             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19919         }
19920     },
19921
19922     /** @ignore */
19923     onDblClick : function(e){
19924         var item = this.findItemFromChild(e.getTarget());
19925         if(item){
19926             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19927         }
19928     },
19929
19930     onItemClick : function(item, index, e)
19931     {
19932         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19933             return false;
19934         }
19935         if (this.toggleSelect) {
19936             var m = this.isSelected(item) ? 'unselect' : 'select';
19937             //Roo.log(m);
19938             var _t = this;
19939             _t[m](item, true, false);
19940             return true;
19941         }
19942         if(this.multiSelect || this.singleSelect){
19943             if(this.multiSelect && e.shiftKey && this.lastSelection){
19944                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19945             }else{
19946                 this.select(item, this.multiSelect && e.ctrlKey);
19947                 this.lastSelection = item;
19948             }
19949             
19950             if(!this.tickable){
19951                 e.preventDefault();
19952             }
19953             
19954         }
19955         return true;
19956     },
19957
19958     /**
19959      * Get the number of selected nodes.
19960      * @return {Number}
19961      */
19962     getSelectionCount : function(){
19963         return this.selections.length;
19964     },
19965
19966     /**
19967      * Get the currently selected nodes.
19968      * @return {Array} An array of HTMLElements
19969      */
19970     getSelectedNodes : function(){
19971         return this.selections;
19972     },
19973
19974     /**
19975      * Get the indexes of the selected nodes.
19976      * @return {Array}
19977      */
19978     getSelectedIndexes : function(){
19979         var indexes = [], s = this.selections;
19980         for(var i = 0, len = s.length; i < len; i++){
19981             indexes.push(s[i].nodeIndex);
19982         }
19983         return indexes;
19984     },
19985
19986     /**
19987      * Clear all selections
19988      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19989      */
19990     clearSelections : function(suppressEvent){
19991         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19992             this.cmp.elements = this.selections;
19993             this.cmp.removeClass(this.selectedClass);
19994             this.selections = [];
19995             if(!suppressEvent){
19996                 this.fireEvent("selectionchange", this, this.selections);
19997             }
19998         }
19999     },
20000
20001     /**
20002      * Returns true if the passed node is selected
20003      * @param {HTMLElement/Number} node The node or node index
20004      * @return {Boolean}
20005      */
20006     isSelected : function(node){
20007         var s = this.selections;
20008         if(s.length < 1){
20009             return false;
20010         }
20011         node = this.getNode(node);
20012         return s.indexOf(node) !== -1;
20013     },
20014
20015     /**
20016      * Selects nodes.
20017      * @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
20018      * @param {Boolean} keepExisting (optional) true to keep existing selections
20019      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20020      */
20021     select : function(nodeInfo, keepExisting, suppressEvent){
20022         if(nodeInfo instanceof Array){
20023             if(!keepExisting){
20024                 this.clearSelections(true);
20025             }
20026             for(var i = 0, len = nodeInfo.length; i < len; i++){
20027                 this.select(nodeInfo[i], true, true);
20028             }
20029             return;
20030         } 
20031         var node = this.getNode(nodeInfo);
20032         if(!node || this.isSelected(node)){
20033             return; // already selected.
20034         }
20035         if(!keepExisting){
20036             this.clearSelections(true);
20037         }
20038         
20039         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20040             Roo.fly(node).addClass(this.selectedClass);
20041             this.selections.push(node);
20042             if(!suppressEvent){
20043                 this.fireEvent("selectionchange", this, this.selections);
20044             }
20045         }
20046         
20047         
20048     },
20049       /**
20050      * Unselects nodes.
20051      * @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
20052      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20053      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20054      */
20055     unselect : function(nodeInfo, keepExisting, suppressEvent)
20056     {
20057         if(nodeInfo instanceof Array){
20058             Roo.each(this.selections, function(s) {
20059                 this.unselect(s, nodeInfo);
20060             }, this);
20061             return;
20062         }
20063         var node = this.getNode(nodeInfo);
20064         if(!node || !this.isSelected(node)){
20065             //Roo.log("not selected");
20066             return; // not selected.
20067         }
20068         // fireevent???
20069         var ns = [];
20070         Roo.each(this.selections, function(s) {
20071             if (s == node ) {
20072                 Roo.fly(node).removeClass(this.selectedClass);
20073
20074                 return;
20075             }
20076             ns.push(s);
20077         },this);
20078         
20079         this.selections= ns;
20080         this.fireEvent("selectionchange", this, this.selections);
20081     },
20082
20083     /**
20084      * Gets a template node.
20085      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20086      * @return {HTMLElement} The node or null if it wasn't found
20087      */
20088     getNode : function(nodeInfo){
20089         if(typeof nodeInfo == "string"){
20090             return document.getElementById(nodeInfo);
20091         }else if(typeof nodeInfo == "number"){
20092             return this.nodes[nodeInfo];
20093         }
20094         return nodeInfo;
20095     },
20096
20097     /**
20098      * Gets a range template nodes.
20099      * @param {Number} startIndex
20100      * @param {Number} endIndex
20101      * @return {Array} An array of nodes
20102      */
20103     getNodes : function(start, end){
20104         var ns = this.nodes;
20105         start = start || 0;
20106         end = typeof end == "undefined" ? ns.length - 1 : end;
20107         var nodes = [];
20108         if(start <= end){
20109             for(var i = start; i <= end; i++){
20110                 nodes.push(ns[i]);
20111             }
20112         } else{
20113             for(var i = start; i >= end; i--){
20114                 nodes.push(ns[i]);
20115             }
20116         }
20117         return nodes;
20118     },
20119
20120     /**
20121      * Finds the index of the passed node
20122      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20123      * @return {Number} The index of the node or -1
20124      */
20125     indexOf : function(node){
20126         node = this.getNode(node);
20127         if(typeof node.nodeIndex == "number"){
20128             return node.nodeIndex;
20129         }
20130         var ns = this.nodes;
20131         for(var i = 0, len = ns.length; i < len; i++){
20132             if(ns[i] == node){
20133                 return i;
20134             }
20135         }
20136         return -1;
20137     }
20138 });
20139 /*
20140  * - LGPL
20141  *
20142  * based on jquery fullcalendar
20143  * 
20144  */
20145
20146 Roo.bootstrap = Roo.bootstrap || {};
20147 /**
20148  * @class Roo.bootstrap.Calendar
20149  * @extends Roo.bootstrap.Component
20150  * Bootstrap Calendar class
20151  * @cfg {Boolean} loadMask (true|false) default false
20152  * @cfg {Object} header generate the user specific header of the calendar, default false
20153
20154  * @constructor
20155  * Create a new Container
20156  * @param {Object} config The config object
20157  */
20158
20159
20160
20161 Roo.bootstrap.Calendar = function(config){
20162     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20163      this.addEvents({
20164         /**
20165              * @event select
20166              * Fires when a date is selected
20167              * @param {DatePicker} this
20168              * @param {Date} date The selected date
20169              */
20170         'select': true,
20171         /**
20172              * @event monthchange
20173              * Fires when the displayed month changes 
20174              * @param {DatePicker} this
20175              * @param {Date} date The selected month
20176              */
20177         'monthchange': true,
20178         /**
20179              * @event evententer
20180              * Fires when mouse over an event
20181              * @param {Calendar} this
20182              * @param {event} Event
20183              */
20184         'evententer': true,
20185         /**
20186              * @event eventleave
20187              * Fires when the mouse leaves an
20188              * @param {Calendar} this
20189              * @param {event}
20190              */
20191         'eventleave': true,
20192         /**
20193              * @event eventclick
20194              * Fires when the mouse click an
20195              * @param {Calendar} this
20196              * @param {event}
20197              */
20198         'eventclick': true
20199         
20200     });
20201
20202 };
20203
20204 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20205     
20206           /**
20207      * @cfg {Roo.data.Store} store
20208      * The data source for the calendar
20209      */
20210         store : false,
20211      /**
20212      * @cfg {Number} startDay
20213      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20214      */
20215     startDay : 0,
20216     
20217     loadMask : false,
20218     
20219     header : false,
20220       
20221     getAutoCreate : function(){
20222         
20223         
20224         var fc_button = function(name, corner, style, content ) {
20225             return Roo.apply({},{
20226                 tag : 'span',
20227                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20228                          (corner.length ?
20229                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20230                             ''
20231                         ),
20232                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20233                 unselectable: 'on'
20234             });
20235         };
20236         
20237         var header = {};
20238         
20239         if(!this.header){
20240             header = {
20241                 tag : 'table',
20242                 cls : 'fc-header',
20243                 style : 'width:100%',
20244                 cn : [
20245                     {
20246                         tag: 'tr',
20247                         cn : [
20248                             {
20249                                 tag : 'td',
20250                                 cls : 'fc-header-left',
20251                                 cn : [
20252                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20253                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20254                                     { tag: 'span', cls: 'fc-header-space' },
20255                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20256
20257
20258                                 ]
20259                             },
20260
20261                             {
20262                                 tag : 'td',
20263                                 cls : 'fc-header-center',
20264                                 cn : [
20265                                     {
20266                                         tag: 'span',
20267                                         cls: 'fc-header-title',
20268                                         cn : {
20269                                             tag: 'H2',
20270                                             html : 'month / year'
20271                                         }
20272                                     }
20273
20274                                 ]
20275                             },
20276                             {
20277                                 tag : 'td',
20278                                 cls : 'fc-header-right',
20279                                 cn : [
20280                               /*      fc_button('month', 'left', '', 'month' ),
20281                                     fc_button('week', '', '', 'week' ),
20282                                     fc_button('day', 'right', '', 'day' )
20283                                 */    
20284
20285                                 ]
20286                             }
20287
20288                         ]
20289                     }
20290                 ]
20291             };
20292         }
20293         
20294         header = this.header;
20295         
20296        
20297         var cal_heads = function() {
20298             var ret = [];
20299             // fixme - handle this.
20300             
20301             for (var i =0; i < Date.dayNames.length; i++) {
20302                 var d = Date.dayNames[i];
20303                 ret.push({
20304                     tag: 'th',
20305                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20306                     html : d.substring(0,3)
20307                 });
20308                 
20309             }
20310             ret[0].cls += ' fc-first';
20311             ret[6].cls += ' fc-last';
20312             return ret;
20313         };
20314         var cal_cell = function(n) {
20315             return  {
20316                 tag: 'td',
20317                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20318                 cn : [
20319                     {
20320                         cn : [
20321                             {
20322                                 cls: 'fc-day-number',
20323                                 html: 'D'
20324                             },
20325                             {
20326                                 cls: 'fc-day-content',
20327                              
20328                                 cn : [
20329                                      {
20330                                         style: 'position: relative;' // height: 17px;
20331                                     }
20332                                 ]
20333                             }
20334                             
20335                             
20336                         ]
20337                     }
20338                 ]
20339                 
20340             }
20341         };
20342         var cal_rows = function() {
20343             
20344             var ret = [];
20345             for (var r = 0; r < 6; r++) {
20346                 var row= {
20347                     tag : 'tr',
20348                     cls : 'fc-week',
20349                     cn : []
20350                 };
20351                 
20352                 for (var i =0; i < Date.dayNames.length; i++) {
20353                     var d = Date.dayNames[i];
20354                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20355
20356                 }
20357                 row.cn[0].cls+=' fc-first';
20358                 row.cn[0].cn[0].style = 'min-height:90px';
20359                 row.cn[6].cls+=' fc-last';
20360                 ret.push(row);
20361                 
20362             }
20363             ret[0].cls += ' fc-first';
20364             ret[4].cls += ' fc-prev-last';
20365             ret[5].cls += ' fc-last';
20366             return ret;
20367             
20368         };
20369         
20370         var cal_table = {
20371             tag: 'table',
20372             cls: 'fc-border-separate',
20373             style : 'width:100%',
20374             cellspacing  : 0,
20375             cn : [
20376                 { 
20377                     tag: 'thead',
20378                     cn : [
20379                         { 
20380                             tag: 'tr',
20381                             cls : 'fc-first fc-last',
20382                             cn : cal_heads()
20383                         }
20384                     ]
20385                 },
20386                 { 
20387                     tag: 'tbody',
20388                     cn : cal_rows()
20389                 }
20390                   
20391             ]
20392         };
20393          
20394          var cfg = {
20395             cls : 'fc fc-ltr',
20396             cn : [
20397                 header,
20398                 {
20399                     cls : 'fc-content',
20400                     style : "position: relative;",
20401                     cn : [
20402                         {
20403                             cls : 'fc-view fc-view-month fc-grid',
20404                             style : 'position: relative',
20405                             unselectable : 'on',
20406                             cn : [
20407                                 {
20408                                     cls : 'fc-event-container',
20409                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20410                                 },
20411                                 cal_table
20412                             ]
20413                         }
20414                     ]
20415     
20416                 }
20417            ] 
20418             
20419         };
20420         
20421          
20422         
20423         return cfg;
20424     },
20425     
20426     
20427     initEvents : function()
20428     {
20429         if(!this.store){
20430             throw "can not find store for calendar";
20431         }
20432         
20433         var mark = {
20434             tag: "div",
20435             cls:"x-dlg-mask",
20436             style: "text-align:center",
20437             cn: [
20438                 {
20439                     tag: "div",
20440                     style: "background-color:white;width:50%;margin:250 auto",
20441                     cn: [
20442                         {
20443                             tag: "img",
20444                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20445                         },
20446                         {
20447                             tag: "span",
20448                             html: "Loading"
20449                         }
20450                         
20451                     ]
20452                 }
20453             ]
20454         };
20455         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20456         
20457         var size = this.el.select('.fc-content', true).first().getSize();
20458         this.maskEl.setSize(size.width, size.height);
20459         this.maskEl.enableDisplayMode("block");
20460         if(!this.loadMask){
20461             this.maskEl.hide();
20462         }
20463         
20464         this.store = Roo.factory(this.store, Roo.data);
20465         this.store.on('load', this.onLoad, this);
20466         this.store.on('beforeload', this.onBeforeLoad, this);
20467         
20468         this.resize();
20469         
20470         this.cells = this.el.select('.fc-day',true);
20471         //Roo.log(this.cells);
20472         this.textNodes = this.el.query('.fc-day-number');
20473         this.cells.addClassOnOver('fc-state-hover');
20474         
20475         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20476         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20477         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20478         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20479         
20480         this.on('monthchange', this.onMonthChange, this);
20481         
20482         this.update(new Date().clearTime());
20483     },
20484     
20485     resize : function() {
20486         var sz  = this.el.getSize();
20487         
20488         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20489         this.el.select('.fc-day-content div',true).setHeight(34);
20490     },
20491     
20492     
20493     // private
20494     showPrevMonth : function(e){
20495         this.update(this.activeDate.add("mo", -1));
20496     },
20497     showToday : function(e){
20498         this.update(new Date().clearTime());
20499     },
20500     // private
20501     showNextMonth : function(e){
20502         this.update(this.activeDate.add("mo", 1));
20503     },
20504
20505     // private
20506     showPrevYear : function(){
20507         this.update(this.activeDate.add("y", -1));
20508     },
20509
20510     // private
20511     showNextYear : function(){
20512         this.update(this.activeDate.add("y", 1));
20513     },
20514
20515     
20516    // private
20517     update : function(date)
20518     {
20519         var vd = this.activeDate;
20520         this.activeDate = date;
20521 //        if(vd && this.el){
20522 //            var t = date.getTime();
20523 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20524 //                Roo.log('using add remove');
20525 //                
20526 //                this.fireEvent('monthchange', this, date);
20527 //                
20528 //                this.cells.removeClass("fc-state-highlight");
20529 //                this.cells.each(function(c){
20530 //                   if(c.dateValue == t){
20531 //                       c.addClass("fc-state-highlight");
20532 //                       setTimeout(function(){
20533 //                            try{c.dom.firstChild.focus();}catch(e){}
20534 //                       }, 50);
20535 //                       return false;
20536 //                   }
20537 //                   return true;
20538 //                });
20539 //                return;
20540 //            }
20541 //        }
20542         
20543         var days = date.getDaysInMonth();
20544         
20545         var firstOfMonth = date.getFirstDateOfMonth();
20546         var startingPos = firstOfMonth.getDay()-this.startDay;
20547         
20548         if(startingPos < this.startDay){
20549             startingPos += 7;
20550         }
20551         
20552         var pm = date.add(Date.MONTH, -1);
20553         var prevStart = pm.getDaysInMonth()-startingPos;
20554 //        
20555         this.cells = this.el.select('.fc-day',true);
20556         this.textNodes = this.el.query('.fc-day-number');
20557         this.cells.addClassOnOver('fc-state-hover');
20558         
20559         var cells = this.cells.elements;
20560         var textEls = this.textNodes;
20561         
20562         Roo.each(cells, function(cell){
20563             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20564         });
20565         
20566         days += startingPos;
20567
20568         // convert everything to numbers so it's fast
20569         var day = 86400000;
20570         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20571         //Roo.log(d);
20572         //Roo.log(pm);
20573         //Roo.log(prevStart);
20574         
20575         var today = new Date().clearTime().getTime();
20576         var sel = date.clearTime().getTime();
20577         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20578         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20579         var ddMatch = this.disabledDatesRE;
20580         var ddText = this.disabledDatesText;
20581         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20582         var ddaysText = this.disabledDaysText;
20583         var format = this.format;
20584         
20585         var setCellClass = function(cal, cell){
20586             cell.row = 0;
20587             cell.events = [];
20588             cell.more = [];
20589             //Roo.log('set Cell Class');
20590             cell.title = "";
20591             var t = d.getTime();
20592             
20593             //Roo.log(d);
20594             
20595             cell.dateValue = t;
20596             if(t == today){
20597                 cell.className += " fc-today";
20598                 cell.className += " fc-state-highlight";
20599                 cell.title = cal.todayText;
20600             }
20601             if(t == sel){
20602                 // disable highlight in other month..
20603                 //cell.className += " fc-state-highlight";
20604                 
20605             }
20606             // disabling
20607             if(t < min) {
20608                 cell.className = " fc-state-disabled";
20609                 cell.title = cal.minText;
20610                 return;
20611             }
20612             if(t > max) {
20613                 cell.className = " fc-state-disabled";
20614                 cell.title = cal.maxText;
20615                 return;
20616             }
20617             if(ddays){
20618                 if(ddays.indexOf(d.getDay()) != -1){
20619                     cell.title = ddaysText;
20620                     cell.className = " fc-state-disabled";
20621                 }
20622             }
20623             if(ddMatch && format){
20624                 var fvalue = d.dateFormat(format);
20625                 if(ddMatch.test(fvalue)){
20626                     cell.title = ddText.replace("%0", fvalue);
20627                     cell.className = " fc-state-disabled";
20628                 }
20629             }
20630             
20631             if (!cell.initialClassName) {
20632                 cell.initialClassName = cell.dom.className;
20633             }
20634             
20635             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20636         };
20637
20638         var i = 0;
20639         
20640         for(; i < startingPos; i++) {
20641             textEls[i].innerHTML = (++prevStart);
20642             d.setDate(d.getDate()+1);
20643             
20644             cells[i].className = "fc-past fc-other-month";
20645             setCellClass(this, cells[i]);
20646         }
20647         
20648         var intDay = 0;
20649         
20650         for(; i < days; i++){
20651             intDay = i - startingPos + 1;
20652             textEls[i].innerHTML = (intDay);
20653             d.setDate(d.getDate()+1);
20654             
20655             cells[i].className = ''; // "x-date-active";
20656             setCellClass(this, cells[i]);
20657         }
20658         var extraDays = 0;
20659         
20660         for(; i < 42; i++) {
20661             textEls[i].innerHTML = (++extraDays);
20662             d.setDate(d.getDate()+1);
20663             
20664             cells[i].className = "fc-future fc-other-month";
20665             setCellClass(this, cells[i]);
20666         }
20667         
20668         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20669         
20670         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20671         
20672         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20673         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20674         
20675         if(totalRows != 6){
20676             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20677             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20678         }
20679         
20680         this.fireEvent('monthchange', this, date);
20681         
20682         
20683         /*
20684         if(!this.internalRender){
20685             var main = this.el.dom.firstChild;
20686             var w = main.offsetWidth;
20687             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20688             Roo.fly(main).setWidth(w);
20689             this.internalRender = true;
20690             // opera does not respect the auto grow header center column
20691             // then, after it gets a width opera refuses to recalculate
20692             // without a second pass
20693             if(Roo.isOpera && !this.secondPass){
20694                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20695                 this.secondPass = true;
20696                 this.update.defer(10, this, [date]);
20697             }
20698         }
20699         */
20700         
20701     },
20702     
20703     findCell : function(dt) {
20704         dt = dt.clearTime().getTime();
20705         var ret = false;
20706         this.cells.each(function(c){
20707             //Roo.log("check " +c.dateValue + '?=' + dt);
20708             if(c.dateValue == dt){
20709                 ret = c;
20710                 return false;
20711             }
20712             return true;
20713         });
20714         
20715         return ret;
20716     },
20717     
20718     findCells : function(ev) {
20719         var s = ev.start.clone().clearTime().getTime();
20720        // Roo.log(s);
20721         var e= ev.end.clone().clearTime().getTime();
20722        // Roo.log(e);
20723         var ret = [];
20724         this.cells.each(function(c){
20725              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20726             
20727             if(c.dateValue > e){
20728                 return ;
20729             }
20730             if(c.dateValue < s){
20731                 return ;
20732             }
20733             ret.push(c);
20734         });
20735         
20736         return ret;    
20737     },
20738     
20739 //    findBestRow: function(cells)
20740 //    {
20741 //        var ret = 0;
20742 //        
20743 //        for (var i =0 ; i < cells.length;i++) {
20744 //            ret  = Math.max(cells[i].rows || 0,ret);
20745 //        }
20746 //        return ret;
20747 //        
20748 //    },
20749     
20750     
20751     addItem : function(ev)
20752     {
20753         // look for vertical location slot in
20754         var cells = this.findCells(ev);
20755         
20756 //        ev.row = this.findBestRow(cells);
20757         
20758         // work out the location.
20759         
20760         var crow = false;
20761         var rows = [];
20762         for(var i =0; i < cells.length; i++) {
20763             
20764             cells[i].row = cells[0].row;
20765             
20766             if(i == 0){
20767                 cells[i].row = cells[i].row + 1;
20768             }
20769             
20770             if (!crow) {
20771                 crow = {
20772                     start : cells[i],
20773                     end :  cells[i]
20774                 };
20775                 continue;
20776             }
20777             if (crow.start.getY() == cells[i].getY()) {
20778                 // on same row.
20779                 crow.end = cells[i];
20780                 continue;
20781             }
20782             // different row.
20783             rows.push(crow);
20784             crow = {
20785                 start: cells[i],
20786                 end : cells[i]
20787             };
20788             
20789         }
20790         
20791         rows.push(crow);
20792         ev.els = [];
20793         ev.rows = rows;
20794         ev.cells = cells;
20795         
20796         cells[0].events.push(ev);
20797         
20798         this.calevents.push(ev);
20799     },
20800     
20801     clearEvents: function() {
20802         
20803         if(!this.calevents){
20804             return;
20805         }
20806         
20807         Roo.each(this.cells.elements, function(c){
20808             c.row = 0;
20809             c.events = [];
20810             c.more = [];
20811         });
20812         
20813         Roo.each(this.calevents, function(e) {
20814             Roo.each(e.els, function(el) {
20815                 el.un('mouseenter' ,this.onEventEnter, this);
20816                 el.un('mouseleave' ,this.onEventLeave, this);
20817                 el.remove();
20818             },this);
20819         },this);
20820         
20821         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20822             e.remove();
20823         });
20824         
20825     },
20826     
20827     renderEvents: function()
20828     {   
20829         var _this = this;
20830         
20831         this.cells.each(function(c) {
20832             
20833             if(c.row < 5){
20834                 return;
20835             }
20836             
20837             var ev = c.events;
20838             
20839             var r = 4;
20840             if(c.row != c.events.length){
20841                 r = 4 - (4 - (c.row - c.events.length));
20842             }
20843             
20844             c.events = ev.slice(0, r);
20845             c.more = ev.slice(r);
20846             
20847             if(c.more.length && c.more.length == 1){
20848                 c.events.push(c.more.pop());
20849             }
20850             
20851             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20852             
20853         });
20854             
20855         this.cells.each(function(c) {
20856             
20857             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20858             
20859             
20860             for (var e = 0; e < c.events.length; e++){
20861                 var ev = c.events[e];
20862                 var rows = ev.rows;
20863                 
20864                 for(var i = 0; i < rows.length; i++) {
20865                 
20866                     // how many rows should it span..
20867
20868                     var  cfg = {
20869                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20870                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20871
20872                         unselectable : "on",
20873                         cn : [
20874                             {
20875                                 cls: 'fc-event-inner',
20876                                 cn : [
20877     //                                {
20878     //                                  tag:'span',
20879     //                                  cls: 'fc-event-time',
20880     //                                  html : cells.length > 1 ? '' : ev.time
20881     //                                },
20882                                     {
20883                                       tag:'span',
20884                                       cls: 'fc-event-title',
20885                                       html : String.format('{0}', ev.title)
20886                                     }
20887
20888
20889                                 ]
20890                             },
20891                             {
20892                                 cls: 'ui-resizable-handle ui-resizable-e',
20893                                 html : '&nbsp;&nbsp;&nbsp'
20894                             }
20895
20896                         ]
20897                     };
20898
20899                     if (i == 0) {
20900                         cfg.cls += ' fc-event-start';
20901                     }
20902                     if ((i+1) == rows.length) {
20903                         cfg.cls += ' fc-event-end';
20904                     }
20905
20906                     var ctr = _this.el.select('.fc-event-container',true).first();
20907                     var cg = ctr.createChild(cfg);
20908
20909                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20910                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20911
20912                     var r = (c.more.length) ? 1 : 0;
20913                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20914                     cg.setWidth(ebox.right - sbox.x -2);
20915
20916                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20917                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20918                     cg.on('click', _this.onEventClick, _this, ev);
20919
20920                     ev.els.push(cg);
20921                     
20922                 }
20923                 
20924             }
20925             
20926             
20927             if(c.more.length){
20928                 var  cfg = {
20929                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20930                     style : 'position: absolute',
20931                     unselectable : "on",
20932                     cn : [
20933                         {
20934                             cls: 'fc-event-inner',
20935                             cn : [
20936                                 {
20937                                   tag:'span',
20938                                   cls: 'fc-event-title',
20939                                   html : 'More'
20940                                 }
20941
20942
20943                             ]
20944                         },
20945                         {
20946                             cls: 'ui-resizable-handle ui-resizable-e',
20947                             html : '&nbsp;&nbsp;&nbsp'
20948                         }
20949
20950                     ]
20951                 };
20952
20953                 var ctr = _this.el.select('.fc-event-container',true).first();
20954                 var cg = ctr.createChild(cfg);
20955
20956                 var sbox = c.select('.fc-day-content',true).first().getBox();
20957                 var ebox = c.select('.fc-day-content',true).first().getBox();
20958                 //Roo.log(cg);
20959                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20960                 cg.setWidth(ebox.right - sbox.x -2);
20961
20962                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20963                 
20964             }
20965             
20966         });
20967         
20968         
20969         
20970     },
20971     
20972     onEventEnter: function (e, el,event,d) {
20973         this.fireEvent('evententer', this, el, event);
20974     },
20975     
20976     onEventLeave: function (e, el,event,d) {
20977         this.fireEvent('eventleave', this, el, event);
20978     },
20979     
20980     onEventClick: function (e, el,event,d) {
20981         this.fireEvent('eventclick', this, el, event);
20982     },
20983     
20984     onMonthChange: function () {
20985         this.store.load();
20986     },
20987     
20988     onMoreEventClick: function(e, el, more)
20989     {
20990         var _this = this;
20991         
20992         this.calpopover.placement = 'right';
20993         this.calpopover.setTitle('More');
20994         
20995         this.calpopover.setContent('');
20996         
20997         var ctr = this.calpopover.el.select('.popover-content', true).first();
20998         
20999         Roo.each(more, function(m){
21000             var cfg = {
21001                 cls : 'fc-event-hori fc-event-draggable',
21002                 html : m.title
21003             };
21004             var cg = ctr.createChild(cfg);
21005             
21006             cg.on('click', _this.onEventClick, _this, m);
21007         });
21008         
21009         this.calpopover.show(el);
21010         
21011         
21012     },
21013     
21014     onLoad: function () 
21015     {   
21016         this.calevents = [];
21017         var cal = this;
21018         
21019         if(this.store.getCount() > 0){
21020             this.store.data.each(function(d){
21021                cal.addItem({
21022                     id : d.data.id,
21023                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21024                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21025                     time : d.data.start_time,
21026                     title : d.data.title,
21027                     description : d.data.description,
21028                     venue : d.data.venue
21029                 });
21030             });
21031         }
21032         
21033         this.renderEvents();
21034         
21035         if(this.calevents.length && this.loadMask){
21036             this.maskEl.hide();
21037         }
21038     },
21039     
21040     onBeforeLoad: function()
21041     {
21042         this.clearEvents();
21043         if(this.loadMask){
21044             this.maskEl.show();
21045         }
21046     }
21047 });
21048
21049  
21050  /*
21051  * - LGPL
21052  *
21053  * element
21054  * 
21055  */
21056
21057 /**
21058  * @class Roo.bootstrap.Popover
21059  * @extends Roo.bootstrap.Component
21060  * Bootstrap Popover class
21061  * @cfg {String} html contents of the popover   (or false to use children..)
21062  * @cfg {String} title of popover (or false to hide)
21063  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21064  * @cfg {String} trigger click || hover (or false to trigger manually)
21065  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21066  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21067  *      - if false and it has a 'parent' then it will be automatically added to that element
21068  *      - if string - Roo.get  will be called 
21069  * @cfg {Number} delay - delay before showing
21070  
21071  * @constructor
21072  * Create a new Popover
21073  * @param {Object} config The config object
21074  */
21075
21076 Roo.bootstrap.Popover = function(config){
21077     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21078     
21079     this.addEvents({
21080         // raw events
21081          /**
21082          * @event show
21083          * After the popover show
21084          * 
21085          * @param {Roo.bootstrap.Popover} this
21086          */
21087         "show" : true,
21088         /**
21089          * @event hide
21090          * After the popover hide
21091          * 
21092          * @param {Roo.bootstrap.Popover} this
21093          */
21094         "hide" : true
21095     });
21096 };
21097
21098 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21099     
21100     title: false,
21101     html: false,
21102     
21103     placement : 'right',
21104     trigger : 'hover', // hover
21105     modal : false,
21106     delay : 0,
21107     
21108     over: false,
21109     
21110     can_build_overlaid : false,
21111     
21112     maskEl : false, // the mask element
21113     headerEl : false,
21114     contentEl : false,
21115     alignEl : false, // when show is called with an element - this get's stored.
21116     
21117     getChildContainer : function()
21118     {
21119         return this.contentEl;
21120         
21121     },
21122     getPopoverHeader : function()
21123     {
21124         this.title = true; // flag not to hide it..
21125         this.headerEl.addClass('p-0');
21126         return this.headerEl
21127     },
21128     
21129     
21130     getAutoCreate : function(){
21131          
21132         var cfg = {
21133            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21134            style: 'display:block',
21135            cn : [
21136                 {
21137                     cls : 'arrow'
21138                 },
21139                 {
21140                     cls : 'popover-inner ',
21141                     cn : [
21142                         {
21143                             tag: 'h3',
21144                             cls: 'popover-title popover-header',
21145                             html : this.title === false ? '' : this.title
21146                         },
21147                         {
21148                             cls : 'popover-content popover-body '  + (this.cls || ''),
21149                             html : this.html || ''
21150                         }
21151                     ]
21152                     
21153                 }
21154            ]
21155         };
21156         
21157         return cfg;
21158     },
21159     /**
21160      * @param {string} the title
21161      */
21162     setTitle: function(str)
21163     {
21164         this.title = str;
21165         if (this.el) {
21166             this.headerEl.dom.innerHTML = str;
21167         }
21168         
21169     },
21170     /**
21171      * @param {string} the body content
21172      */
21173     setContent: function(str)
21174     {
21175         this.html = str;
21176         if (this.contentEl) {
21177             this.contentEl.dom.innerHTML = str;
21178         }
21179         
21180     },
21181     // as it get's added to the bottom of the page.
21182     onRender : function(ct, position)
21183     {
21184         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21185         
21186         
21187         
21188         if(!this.el){
21189             var cfg = Roo.apply({},  this.getAutoCreate());
21190             cfg.id = Roo.id();
21191             
21192             if (this.cls) {
21193                 cfg.cls += ' ' + this.cls;
21194             }
21195             if (this.style) {
21196                 cfg.style = this.style;
21197             }
21198             //Roo.log("adding to ");
21199             this.el = Roo.get(document.body).createChild(cfg, position);
21200 //            Roo.log(this.el);
21201         }
21202         
21203         this.contentEl = this.el.select('.popover-content',true).first();
21204         this.headerEl =  this.el.select('.popover-title',true).first();
21205         
21206         var nitems = [];
21207         if(typeof(this.items) != 'undefined'){
21208             var items = this.items;
21209             delete this.items;
21210
21211             for(var i =0;i < items.length;i++) {
21212                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21213             }
21214         }
21215
21216         this.items = nitems;
21217         
21218         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21219         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21220         
21221         
21222         
21223         this.initEvents();
21224     },
21225     
21226     resizeMask : function()
21227     {
21228         this.maskEl.setSize(
21229             Roo.lib.Dom.getViewWidth(true),
21230             Roo.lib.Dom.getViewHeight(true)
21231         );
21232     },
21233     
21234     initEvents : function()
21235     {
21236         
21237         if (!this.modal) { 
21238             Roo.bootstrap.Popover.register(this);
21239         }
21240          
21241         this.arrowEl = this.el.select('.arrow',true).first();
21242         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21243         this.el.enableDisplayMode('block');
21244         this.el.hide();
21245  
21246         
21247         if (this.over === false && !this.parent()) {
21248             return; 
21249         }
21250         if (this.triggers === false) {
21251             return;
21252         }
21253          
21254         // support parent
21255         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21256         var triggers = this.trigger ? this.trigger.split(' ') : [];
21257         Roo.each(triggers, function(trigger) {
21258         
21259             if (trigger == 'click') {
21260                 on_el.on('click', this.toggle, this);
21261             } else if (trigger != 'manual') {
21262                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21263                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21264       
21265                 on_el.on(eventIn  ,this.enter, this);
21266                 on_el.on(eventOut, this.leave, this);
21267             }
21268         }, this);
21269     },
21270     
21271     
21272     // private
21273     timeout : null,
21274     hoverState : null,
21275     
21276     toggle : function () {
21277         this.hoverState == 'in' ? this.leave() : this.enter();
21278     },
21279     
21280     enter : function () {
21281         
21282         clearTimeout(this.timeout);
21283     
21284         this.hoverState = 'in';
21285     
21286         if (!this.delay || !this.delay.show) {
21287             this.show();
21288             return;
21289         }
21290         var _t = this;
21291         this.timeout = setTimeout(function () {
21292             if (_t.hoverState == 'in') {
21293                 _t.show();
21294             }
21295         }, this.delay.show)
21296     },
21297     
21298     leave : function() {
21299         clearTimeout(this.timeout);
21300     
21301         this.hoverState = 'out';
21302     
21303         if (!this.delay || !this.delay.hide) {
21304             this.hide();
21305             return;
21306         }
21307         var _t = this;
21308         this.timeout = setTimeout(function () {
21309             if (_t.hoverState == 'out') {
21310                 _t.hide();
21311             }
21312         }, this.delay.hide)
21313     },
21314     /**
21315      * Show the popover
21316      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21317      * @param {string} (left|right|top|bottom) position
21318      */
21319     show : function (on_el, placement)
21320     {
21321         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21322         on_el = on_el || false; // default to false
21323          
21324         if (!on_el) {
21325             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21326                 on_el = this.parent().el;
21327             } else if (this.over) {
21328                 on_el = Roo.get(this.over);
21329             }
21330             
21331         }
21332         
21333         this.alignEl = Roo.get( on_el );
21334
21335         if (!this.el) {
21336             this.render(document.body);
21337         }
21338         
21339         
21340          
21341         
21342         if (this.title === false) {
21343             this.headerEl.hide();
21344         }
21345         
21346        
21347         this.el.show();
21348         this.el.dom.style.display = 'block';
21349          
21350  
21351         if (this.alignEl) {
21352             this.updatePosition(this.placement, true);
21353              
21354         } else {
21355             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21356             var es = this.el.getSize();
21357             var x = Roo.lib.Dom.getViewWidth()/2;
21358             var y = Roo.lib.Dom.getViewHeight()/2;
21359             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21360             
21361         }
21362
21363         
21364         //var arrow = this.el.select('.arrow',true).first();
21365         //arrow.set(align[2], 
21366         
21367         this.el.addClass('in');
21368         
21369          
21370         
21371         this.hoverState = 'in';
21372         
21373         if (this.modal) {
21374             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21375             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21376             this.maskEl.dom.style.display = 'block';
21377             this.maskEl.addClass('show');
21378         }
21379         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21380  
21381         this.fireEvent('show', this);
21382         
21383     },
21384     /**
21385      * fire this manually after loading a grid in the table for example
21386      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21387      * @param {Boolean} try and move it if we cant get right position.
21388      */
21389     updatePosition : function(placement, try_move)
21390     {
21391         // allow for calling with no parameters
21392         placement = placement   ? placement :  this.placement;
21393         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21394         
21395         this.el.removeClass([
21396             'fade','top','bottom', 'left', 'right','in',
21397             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21398         ]);
21399         this.el.addClass(placement + ' bs-popover-' + placement);
21400         
21401         if (!this.alignEl ) {
21402             return false;
21403         }
21404         
21405         switch (placement) {
21406             case 'right':
21407                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21408                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21409                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21410                     //normal display... or moved up/down.
21411                     this.el.setXY(offset);
21412                     var xy = this.alignEl.getAnchorXY('tr', false);
21413                     xy[0]+=2;xy[1]+=5;
21414                     this.arrowEl.setXY(xy);
21415                     return true;
21416                 }
21417                 // continue through...
21418                 return this.updatePosition('left', false);
21419                 
21420             
21421             case 'left':
21422                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21423                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21424                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21425                     //normal display... or moved up/down.
21426                     this.el.setXY(offset);
21427                     var xy = this.alignEl.getAnchorXY('tl', false);
21428                     xy[0]-=10;xy[1]+=5; // << fix me
21429                     this.arrowEl.setXY(xy);
21430                     return true;
21431                 }
21432                 // call self...
21433                 return this.updatePosition('right', false);
21434             
21435             case 'top':
21436                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21437                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21438                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21439                     //normal display... or moved up/down.
21440                     this.el.setXY(offset);
21441                     var xy = this.alignEl.getAnchorXY('t', false);
21442                     xy[1]-=10; // << fix me
21443                     this.arrowEl.setXY(xy);
21444                     return true;
21445                 }
21446                 // fall through
21447                return this.updatePosition('bottom', false);
21448             
21449             case 'bottom':
21450                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21451                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21452                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21453                     //normal display... or moved up/down.
21454                     this.el.setXY(offset);
21455                     var xy = this.alignEl.getAnchorXY('b', false);
21456                      xy[1]+=2; // << fix me
21457                     this.arrowEl.setXY(xy);
21458                     return true;
21459                 }
21460                 // fall through
21461                 return this.updatePosition('top', false);
21462                 
21463             
21464         }
21465         
21466         
21467         return false;
21468     },
21469     
21470     hide : function()
21471     {
21472         this.el.setXY([0,0]);
21473         this.el.removeClass('in');
21474         this.el.hide();
21475         this.hoverState = null;
21476         this.maskEl.hide(); // always..
21477         this.fireEvent('hide', this);
21478     }
21479     
21480 });
21481
21482
21483 Roo.apply(Roo.bootstrap.Popover, {
21484
21485     alignment : {
21486         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21487         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21488         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21489         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21490     },
21491     
21492     zIndex : 20001,
21493
21494     clickHander : false,
21495     
21496     
21497
21498     onMouseDown : function(e)
21499     {
21500         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21501             /// what is nothing is showing..
21502             this.hideAll();
21503         }
21504          
21505     },
21506     
21507     
21508     popups : [],
21509     
21510     register : function(popup)
21511     {
21512         if (!Roo.bootstrap.Popover.clickHandler) {
21513             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21514         }
21515         // hide other popups.
21516         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21517         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21518         this.hideAll(); //<< why?
21519         //this.popups.push(popup);
21520     },
21521     hideAll : function()
21522     {
21523         this.popups.forEach(function(p) {
21524             p.hide();
21525         });
21526     },
21527     onShow : function() {
21528         Roo.bootstrap.Popover.popups.push(this);
21529     },
21530     onHide : function() {
21531         Roo.bootstrap.Popover.popups.remove(this);
21532     } 
21533
21534 });/*
21535  * - LGPL
21536  *
21537  * Card header - holder for the card header elements.
21538  * 
21539  */
21540
21541 /**
21542  * @class Roo.bootstrap.PopoverNav
21543  * @extends Roo.bootstrap.NavGroup
21544  * Bootstrap Popover header navigation class
21545  * @constructor
21546  * Create a new Popover Header Navigation 
21547  * @param {Object} config The config object
21548  */
21549
21550 Roo.bootstrap.PopoverNav = function(config){
21551     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21552 };
21553
21554 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21555     
21556     
21557     container_method : 'getPopoverHeader' 
21558     
21559      
21560     
21561     
21562    
21563 });
21564
21565  
21566
21567  /*
21568  * - LGPL
21569  *
21570  * Progress
21571  * 
21572  */
21573
21574 /**
21575  * @class Roo.bootstrap.Progress
21576  * @extends Roo.bootstrap.Component
21577  * Bootstrap Progress class
21578  * @cfg {Boolean} striped striped of the progress bar
21579  * @cfg {Boolean} active animated of the progress bar
21580  * 
21581  * 
21582  * @constructor
21583  * Create a new Progress
21584  * @param {Object} config The config object
21585  */
21586
21587 Roo.bootstrap.Progress = function(config){
21588     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21589 };
21590
21591 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21592     
21593     striped : false,
21594     active: false,
21595     
21596     getAutoCreate : function(){
21597         var cfg = {
21598             tag: 'div',
21599             cls: 'progress'
21600         };
21601         
21602         
21603         if(this.striped){
21604             cfg.cls += ' progress-striped';
21605         }
21606       
21607         if(this.active){
21608             cfg.cls += ' active';
21609         }
21610         
21611         
21612         return cfg;
21613     }
21614    
21615 });
21616
21617  
21618
21619  /*
21620  * - LGPL
21621  *
21622  * ProgressBar
21623  * 
21624  */
21625
21626 /**
21627  * @class Roo.bootstrap.ProgressBar
21628  * @extends Roo.bootstrap.Component
21629  * Bootstrap ProgressBar class
21630  * @cfg {Number} aria_valuenow aria-value now
21631  * @cfg {Number} aria_valuemin aria-value min
21632  * @cfg {Number} aria_valuemax aria-value max
21633  * @cfg {String} label label for the progress bar
21634  * @cfg {String} panel (success | info | warning | danger )
21635  * @cfg {String} role role of the progress bar
21636  * @cfg {String} sr_only text
21637  * 
21638  * 
21639  * @constructor
21640  * Create a new ProgressBar
21641  * @param {Object} config The config object
21642  */
21643
21644 Roo.bootstrap.ProgressBar = function(config){
21645     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21646 };
21647
21648 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21649     
21650     aria_valuenow : 0,
21651     aria_valuemin : 0,
21652     aria_valuemax : 100,
21653     label : false,
21654     panel : false,
21655     role : false,
21656     sr_only: false,
21657     
21658     getAutoCreate : function()
21659     {
21660         
21661         var cfg = {
21662             tag: 'div',
21663             cls: 'progress-bar',
21664             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21665         };
21666         
21667         if(this.sr_only){
21668             cfg.cn = {
21669                 tag: 'span',
21670                 cls: 'sr-only',
21671                 html: this.sr_only
21672             }
21673         }
21674         
21675         if(this.role){
21676             cfg.role = this.role;
21677         }
21678         
21679         if(this.aria_valuenow){
21680             cfg['aria-valuenow'] = this.aria_valuenow;
21681         }
21682         
21683         if(this.aria_valuemin){
21684             cfg['aria-valuemin'] = this.aria_valuemin;
21685         }
21686         
21687         if(this.aria_valuemax){
21688             cfg['aria-valuemax'] = this.aria_valuemax;
21689         }
21690         
21691         if(this.label && !this.sr_only){
21692             cfg.html = this.label;
21693         }
21694         
21695         if(this.panel){
21696             cfg.cls += ' progress-bar-' + this.panel;
21697         }
21698         
21699         return cfg;
21700     },
21701     
21702     update : function(aria_valuenow)
21703     {
21704         this.aria_valuenow = aria_valuenow;
21705         
21706         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21707     }
21708    
21709 });
21710
21711  
21712
21713  /*
21714  * - LGPL
21715  *
21716  * column
21717  * 
21718  */
21719
21720 /**
21721  * @class Roo.bootstrap.TabGroup
21722  * @extends Roo.bootstrap.Column
21723  * Bootstrap Column class
21724  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21725  * @cfg {Boolean} carousel true to make the group behave like a carousel
21726  * @cfg {Boolean} bullets show bullets for the panels
21727  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21728  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21729  * @cfg {Boolean} showarrow (true|false) show arrow default true
21730  * 
21731  * @constructor
21732  * Create a new TabGroup
21733  * @param {Object} config The config object
21734  */
21735
21736 Roo.bootstrap.TabGroup = function(config){
21737     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21738     if (!this.navId) {
21739         this.navId = Roo.id();
21740     }
21741     this.tabs = [];
21742     Roo.bootstrap.TabGroup.register(this);
21743     
21744 };
21745
21746 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21747     
21748     carousel : false,
21749     transition : false,
21750     bullets : 0,
21751     timer : 0,
21752     autoslide : false,
21753     slideFn : false,
21754     slideOnTouch : false,
21755     showarrow : true,
21756     
21757     getAutoCreate : function()
21758     {
21759         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21760         
21761         cfg.cls += ' tab-content';
21762         
21763         if (this.carousel) {
21764             cfg.cls += ' carousel slide';
21765             
21766             cfg.cn = [{
21767                cls : 'carousel-inner',
21768                cn : []
21769             }];
21770         
21771             if(this.bullets  && !Roo.isTouch){
21772                 
21773                 var bullets = {
21774                     cls : 'carousel-bullets',
21775                     cn : []
21776                 };
21777                
21778                 if(this.bullets_cls){
21779                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21780                 }
21781                 
21782                 bullets.cn.push({
21783                     cls : 'clear'
21784                 });
21785                 
21786                 cfg.cn[0].cn.push(bullets);
21787             }
21788             
21789             if(this.showarrow){
21790                 cfg.cn[0].cn.push({
21791                     tag : 'div',
21792                     class : 'carousel-arrow',
21793                     cn : [
21794                         {
21795                             tag : 'div',
21796                             class : 'carousel-prev',
21797                             cn : [
21798                                 {
21799                                     tag : 'i',
21800                                     class : 'fa fa-chevron-left'
21801                                 }
21802                             ]
21803                         },
21804                         {
21805                             tag : 'div',
21806                             class : 'carousel-next',
21807                             cn : [
21808                                 {
21809                                     tag : 'i',
21810                                     class : 'fa fa-chevron-right'
21811                                 }
21812                             ]
21813                         }
21814                     ]
21815                 });
21816             }
21817             
21818         }
21819         
21820         return cfg;
21821     },
21822     
21823     initEvents:  function()
21824     {
21825 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21826 //            this.el.on("touchstart", this.onTouchStart, this);
21827 //        }
21828         
21829         if(this.autoslide){
21830             var _this = this;
21831             
21832             this.slideFn = window.setInterval(function() {
21833                 _this.showPanelNext();
21834             }, this.timer);
21835         }
21836         
21837         if(this.showarrow){
21838             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21839             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21840         }
21841         
21842         
21843     },
21844     
21845 //    onTouchStart : function(e, el, o)
21846 //    {
21847 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21848 //            return;
21849 //        }
21850 //        
21851 //        this.showPanelNext();
21852 //    },
21853     
21854     
21855     getChildContainer : function()
21856     {
21857         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21858     },
21859     
21860     /**
21861     * register a Navigation item
21862     * @param {Roo.bootstrap.NavItem} the navitem to add
21863     */
21864     register : function(item)
21865     {
21866         this.tabs.push( item);
21867         item.navId = this.navId; // not really needed..
21868         this.addBullet();
21869     
21870     },
21871     
21872     getActivePanel : function()
21873     {
21874         var r = false;
21875         Roo.each(this.tabs, function(t) {
21876             if (t.active) {
21877                 r = t;
21878                 return false;
21879             }
21880             return null;
21881         });
21882         return r;
21883         
21884     },
21885     getPanelByName : function(n)
21886     {
21887         var r = false;
21888         Roo.each(this.tabs, function(t) {
21889             if (t.tabId == n) {
21890                 r = t;
21891                 return false;
21892             }
21893             return null;
21894         });
21895         return r;
21896     },
21897     indexOfPanel : function(p)
21898     {
21899         var r = false;
21900         Roo.each(this.tabs, function(t,i) {
21901             if (t.tabId == p.tabId) {
21902                 r = i;
21903                 return false;
21904             }
21905             return null;
21906         });
21907         return r;
21908     },
21909     /**
21910      * show a specific panel
21911      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21912      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21913      */
21914     showPanel : function (pan)
21915     {
21916         if(this.transition || typeof(pan) == 'undefined'){
21917             Roo.log("waiting for the transitionend");
21918             return false;
21919         }
21920         
21921         if (typeof(pan) == 'number') {
21922             pan = this.tabs[pan];
21923         }
21924         
21925         if (typeof(pan) == 'string') {
21926             pan = this.getPanelByName(pan);
21927         }
21928         
21929         var cur = this.getActivePanel();
21930         
21931         if(!pan || !cur){
21932             Roo.log('pan or acitve pan is undefined');
21933             return false;
21934         }
21935         
21936         if (pan.tabId == this.getActivePanel().tabId) {
21937             return true;
21938         }
21939         
21940         if (false === cur.fireEvent('beforedeactivate')) {
21941             return false;
21942         }
21943         
21944         if(this.bullets > 0 && !Roo.isTouch){
21945             this.setActiveBullet(this.indexOfPanel(pan));
21946         }
21947         
21948         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21949             
21950             //class="carousel-item carousel-item-next carousel-item-left"
21951             
21952             this.transition = true;
21953             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21954             var lr = dir == 'next' ? 'left' : 'right';
21955             pan.el.addClass(dir); // or prev
21956             pan.el.addClass('carousel-item-' + dir); // or prev
21957             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21958             cur.el.addClass(lr); // or right
21959             pan.el.addClass(lr);
21960             cur.el.addClass('carousel-item-' +lr); // or right
21961             pan.el.addClass('carousel-item-' +lr);
21962             
21963             
21964             var _this = this;
21965             cur.el.on('transitionend', function() {
21966                 Roo.log("trans end?");
21967                 
21968                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21969                 pan.setActive(true);
21970                 
21971                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21972                 cur.setActive(false);
21973                 
21974                 _this.transition = false;
21975                 
21976             }, this, { single:  true } );
21977             
21978             return true;
21979         }
21980         
21981         cur.setActive(false);
21982         pan.setActive(true);
21983         
21984         return true;
21985         
21986     },
21987     showPanelNext : function()
21988     {
21989         var i = this.indexOfPanel(this.getActivePanel());
21990         
21991         if (i >= this.tabs.length - 1 && !this.autoslide) {
21992             return;
21993         }
21994         
21995         if (i >= this.tabs.length - 1 && this.autoslide) {
21996             i = -1;
21997         }
21998         
21999         this.showPanel(this.tabs[i+1]);
22000     },
22001     
22002     showPanelPrev : function()
22003     {
22004         var i = this.indexOfPanel(this.getActivePanel());
22005         
22006         if (i  < 1 && !this.autoslide) {
22007             return;
22008         }
22009         
22010         if (i < 1 && this.autoslide) {
22011             i = this.tabs.length;
22012         }
22013         
22014         this.showPanel(this.tabs[i-1]);
22015     },
22016     
22017     
22018     addBullet: function()
22019     {
22020         if(!this.bullets || Roo.isTouch){
22021             return;
22022         }
22023         var ctr = this.el.select('.carousel-bullets',true).first();
22024         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22025         var bullet = ctr.createChild({
22026             cls : 'bullet bullet-' + i
22027         },ctr.dom.lastChild);
22028         
22029         
22030         var _this = this;
22031         
22032         bullet.on('click', (function(e, el, o, ii, t){
22033
22034             e.preventDefault();
22035
22036             this.showPanel(ii);
22037
22038             if(this.autoslide && this.slideFn){
22039                 clearInterval(this.slideFn);
22040                 this.slideFn = window.setInterval(function() {
22041                     _this.showPanelNext();
22042                 }, this.timer);
22043             }
22044
22045         }).createDelegate(this, [i, bullet], true));
22046                 
22047         
22048     },
22049      
22050     setActiveBullet : function(i)
22051     {
22052         if(Roo.isTouch){
22053             return;
22054         }
22055         
22056         Roo.each(this.el.select('.bullet', true).elements, function(el){
22057             el.removeClass('selected');
22058         });
22059
22060         var bullet = this.el.select('.bullet-' + i, true).first();
22061         
22062         if(!bullet){
22063             return;
22064         }
22065         
22066         bullet.addClass('selected');
22067     }
22068     
22069     
22070   
22071 });
22072
22073  
22074
22075  
22076  
22077 Roo.apply(Roo.bootstrap.TabGroup, {
22078     
22079     groups: {},
22080      /**
22081     * register a Navigation Group
22082     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22083     */
22084     register : function(navgrp)
22085     {
22086         this.groups[navgrp.navId] = navgrp;
22087         
22088     },
22089     /**
22090     * fetch a Navigation Group based on the navigation ID
22091     * if one does not exist , it will get created.
22092     * @param {string} the navgroup to add
22093     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22094     */
22095     get: function(navId) {
22096         if (typeof(this.groups[navId]) == 'undefined') {
22097             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22098         }
22099         return this.groups[navId] ;
22100     }
22101     
22102     
22103     
22104 });
22105
22106  /*
22107  * - LGPL
22108  *
22109  * TabPanel
22110  * 
22111  */
22112
22113 /**
22114  * @class Roo.bootstrap.TabPanel
22115  * @extends Roo.bootstrap.Component
22116  * Bootstrap TabPanel class
22117  * @cfg {Boolean} active panel active
22118  * @cfg {String} html panel content
22119  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22120  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22121  * @cfg {String} href click to link..
22122  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22123  * 
22124  * 
22125  * @constructor
22126  * Create a new TabPanel
22127  * @param {Object} config The config object
22128  */
22129
22130 Roo.bootstrap.TabPanel = function(config){
22131     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22132     this.addEvents({
22133         /**
22134              * @event changed
22135              * Fires when the active status changes
22136              * @param {Roo.bootstrap.TabPanel} this
22137              * @param {Boolean} state the new state
22138             
22139          */
22140         'changed': true,
22141         /**
22142              * @event beforedeactivate
22143              * Fires before a tab is de-activated - can be used to do validation on a form.
22144              * @param {Roo.bootstrap.TabPanel} this
22145              * @return {Boolean} false if there is an error
22146             
22147          */
22148         'beforedeactivate': true
22149      });
22150     
22151     this.tabId = this.tabId || Roo.id();
22152   
22153 };
22154
22155 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22156     
22157     active: false,
22158     html: false,
22159     tabId: false,
22160     navId : false,
22161     href : '',
22162     touchSlide : false,
22163     getAutoCreate : function(){
22164         
22165         
22166         var cfg = {
22167             tag: 'div',
22168             // item is needed for carousel - not sure if it has any effect otherwise
22169             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22170             html: this.html || ''
22171         };
22172         
22173         if(this.active){
22174             cfg.cls += ' active';
22175         }
22176         
22177         if(this.tabId){
22178             cfg.tabId = this.tabId;
22179         }
22180         
22181         
22182         
22183         return cfg;
22184     },
22185     
22186     initEvents:  function()
22187     {
22188         var p = this.parent();
22189         
22190         this.navId = this.navId || p.navId;
22191         
22192         if (typeof(this.navId) != 'undefined') {
22193             // not really needed.. but just in case.. parent should be a NavGroup.
22194             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22195             
22196             tg.register(this);
22197             
22198             var i = tg.tabs.length - 1;
22199             
22200             if(this.active && tg.bullets > 0 && i < tg.bullets){
22201                 tg.setActiveBullet(i);
22202             }
22203         }
22204         
22205         this.el.on('click', this.onClick, this);
22206         
22207         if(Roo.isTouch && this.touchSlide){
22208             this.el.on("touchstart", this.onTouchStart, this);
22209             this.el.on("touchmove", this.onTouchMove, this);
22210             this.el.on("touchend", this.onTouchEnd, this);
22211         }
22212         
22213     },
22214     
22215     onRender : function(ct, position)
22216     {
22217         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22218     },
22219     
22220     setActive : function(state)
22221     {
22222         Roo.log("panel - set active " + this.tabId + "=" + state);
22223         
22224         this.active = state;
22225         if (!state) {
22226             this.el.removeClass('active');
22227             
22228         } else  if (!this.el.hasClass('active')) {
22229             this.el.addClass('active');
22230         }
22231         
22232         this.fireEvent('changed', this, state);
22233     },
22234     
22235     onClick : function(e)
22236     {
22237         e.preventDefault();
22238         
22239         if(!this.href.length){
22240             return;
22241         }
22242         
22243         window.location.href = this.href;
22244     },
22245     
22246     startX : 0,
22247     startY : 0,
22248     endX : 0,
22249     endY : 0,
22250     swiping : false,
22251     
22252     onTouchStart : function(e)
22253     {
22254         this.swiping = false;
22255         
22256         this.startX = e.browserEvent.touches[0].clientX;
22257         this.startY = e.browserEvent.touches[0].clientY;
22258     },
22259     
22260     onTouchMove : function(e)
22261     {
22262         this.swiping = true;
22263         
22264         this.endX = e.browserEvent.touches[0].clientX;
22265         this.endY = e.browserEvent.touches[0].clientY;
22266     },
22267     
22268     onTouchEnd : function(e)
22269     {
22270         if(!this.swiping){
22271             this.onClick(e);
22272             return;
22273         }
22274         
22275         var tabGroup = this.parent();
22276         
22277         if(this.endX > this.startX){ // swiping right
22278             tabGroup.showPanelPrev();
22279             return;
22280         }
22281         
22282         if(this.startX > this.endX){ // swiping left
22283             tabGroup.showPanelNext();
22284             return;
22285         }
22286     }
22287     
22288     
22289 });
22290  
22291
22292  
22293
22294  /*
22295  * - LGPL
22296  *
22297  * DateField
22298  * 
22299  */
22300
22301 /**
22302  * @class Roo.bootstrap.DateField
22303  * @extends Roo.bootstrap.Input
22304  * Bootstrap DateField class
22305  * @cfg {Number} weekStart default 0
22306  * @cfg {String} viewMode default empty, (months|years)
22307  * @cfg {String} minViewMode default empty, (months|years)
22308  * @cfg {Number} startDate default -Infinity
22309  * @cfg {Number} endDate default Infinity
22310  * @cfg {Boolean} todayHighlight default false
22311  * @cfg {Boolean} todayBtn default false
22312  * @cfg {Boolean} calendarWeeks default false
22313  * @cfg {Object} daysOfWeekDisabled default empty
22314  * @cfg {Boolean} singleMode default false (true | false)
22315  * 
22316  * @cfg {Boolean} keyboardNavigation default true
22317  * @cfg {String} language default en
22318  * 
22319  * @constructor
22320  * Create a new DateField
22321  * @param {Object} config The config object
22322  */
22323
22324 Roo.bootstrap.DateField = function(config){
22325     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22326      this.addEvents({
22327             /**
22328              * @event show
22329              * Fires when this field show.
22330              * @param {Roo.bootstrap.DateField} this
22331              * @param {Mixed} date The date value
22332              */
22333             show : true,
22334             /**
22335              * @event show
22336              * Fires when this field hide.
22337              * @param {Roo.bootstrap.DateField} this
22338              * @param {Mixed} date The date value
22339              */
22340             hide : true,
22341             /**
22342              * @event select
22343              * Fires when select a date.
22344              * @param {Roo.bootstrap.DateField} this
22345              * @param {Mixed} date The date value
22346              */
22347             select : true,
22348             /**
22349              * @event beforeselect
22350              * Fires when before select a date.
22351              * @param {Roo.bootstrap.DateField} this
22352              * @param {Mixed} date The date value
22353              */
22354             beforeselect : true
22355         });
22356 };
22357
22358 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22359     
22360     /**
22361      * @cfg {String} format
22362      * The default date format string which can be overriden for localization support.  The format must be
22363      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22364      */
22365     format : "m/d/y",
22366     /**
22367      * @cfg {String} altFormats
22368      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22369      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22370      */
22371     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22372     
22373     weekStart : 0,
22374     
22375     viewMode : '',
22376     
22377     minViewMode : '',
22378     
22379     todayHighlight : false,
22380     
22381     todayBtn: false,
22382     
22383     language: 'en',
22384     
22385     keyboardNavigation: true,
22386     
22387     calendarWeeks: false,
22388     
22389     startDate: -Infinity,
22390     
22391     endDate: Infinity,
22392     
22393     daysOfWeekDisabled: [],
22394     
22395     _events: [],
22396     
22397     singleMode : false,
22398     
22399     UTCDate: function()
22400     {
22401         return new Date(Date.UTC.apply(Date, arguments));
22402     },
22403     
22404     UTCToday: function()
22405     {
22406         var today = new Date();
22407         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22408     },
22409     
22410     getDate: function() {
22411             var d = this.getUTCDate();
22412             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22413     },
22414     
22415     getUTCDate: function() {
22416             return this.date;
22417     },
22418     
22419     setDate: function(d) {
22420             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22421     },
22422     
22423     setUTCDate: function(d) {
22424             this.date = d;
22425             this.setValue(this.formatDate(this.date));
22426     },
22427         
22428     onRender: function(ct, position)
22429     {
22430         
22431         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22432         
22433         this.language = this.language || 'en';
22434         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22435         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22436         
22437         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22438         this.format = this.format || 'm/d/y';
22439         this.isInline = false;
22440         this.isInput = true;
22441         this.component = this.el.select('.add-on', true).first() || false;
22442         this.component = (this.component && this.component.length === 0) ? false : this.component;
22443         this.hasInput = this.component && this.inputEl().length;
22444         
22445         if (typeof(this.minViewMode === 'string')) {
22446             switch (this.minViewMode) {
22447                 case 'months':
22448                     this.minViewMode = 1;
22449                     break;
22450                 case 'years':
22451                     this.minViewMode = 2;
22452                     break;
22453                 default:
22454                     this.minViewMode = 0;
22455                     break;
22456             }
22457         }
22458         
22459         if (typeof(this.viewMode === 'string')) {
22460             switch (this.viewMode) {
22461                 case 'months':
22462                     this.viewMode = 1;
22463                     break;
22464                 case 'years':
22465                     this.viewMode = 2;
22466                     break;
22467                 default:
22468                     this.viewMode = 0;
22469                     break;
22470             }
22471         }
22472                 
22473         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22474         
22475 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22476         
22477         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22478         
22479         this.picker().on('mousedown', this.onMousedown, this);
22480         this.picker().on('click', this.onClick, this);
22481         
22482         this.picker().addClass('datepicker-dropdown');
22483         
22484         this.startViewMode = this.viewMode;
22485         
22486         if(this.singleMode){
22487             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22488                 v.setVisibilityMode(Roo.Element.DISPLAY);
22489                 v.hide();
22490             });
22491             
22492             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22493                 v.setStyle('width', '189px');
22494             });
22495         }
22496         
22497         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22498             if(!this.calendarWeeks){
22499                 v.remove();
22500                 return;
22501             }
22502             
22503             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22504             v.attr('colspan', function(i, val){
22505                 return parseInt(val) + 1;
22506             });
22507         });
22508                         
22509         
22510         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22511         
22512         this.setStartDate(this.startDate);
22513         this.setEndDate(this.endDate);
22514         
22515         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22516         
22517         this.fillDow();
22518         this.fillMonths();
22519         this.update();
22520         this.showMode();
22521         
22522         if(this.isInline) {
22523             this.showPopup();
22524         }
22525     },
22526     
22527     picker : function()
22528     {
22529         return this.pickerEl;
22530 //        return this.el.select('.datepicker', true).first();
22531     },
22532     
22533     fillDow: function()
22534     {
22535         var dowCnt = this.weekStart;
22536         
22537         var dow = {
22538             tag: 'tr',
22539             cn: [
22540                 
22541             ]
22542         };
22543         
22544         if(this.calendarWeeks){
22545             dow.cn.push({
22546                 tag: 'th',
22547                 cls: 'cw',
22548                 html: '&nbsp;'
22549             })
22550         }
22551         
22552         while (dowCnt < this.weekStart + 7) {
22553             dow.cn.push({
22554                 tag: 'th',
22555                 cls: 'dow',
22556                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22557             });
22558         }
22559         
22560         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22561     },
22562     
22563     fillMonths: function()
22564     {    
22565         var i = 0;
22566         var months = this.picker().select('>.datepicker-months td', true).first();
22567         
22568         months.dom.innerHTML = '';
22569         
22570         while (i < 12) {
22571             var month = {
22572                 tag: 'span',
22573                 cls: 'month',
22574                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22575             };
22576             
22577             months.createChild(month);
22578         }
22579         
22580     },
22581     
22582     update: function()
22583     {
22584         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;
22585         
22586         if (this.date < this.startDate) {
22587             this.viewDate = new Date(this.startDate);
22588         } else if (this.date > this.endDate) {
22589             this.viewDate = new Date(this.endDate);
22590         } else {
22591             this.viewDate = new Date(this.date);
22592         }
22593         
22594         this.fill();
22595     },
22596     
22597     fill: function() 
22598     {
22599         var d = new Date(this.viewDate),
22600                 year = d.getUTCFullYear(),
22601                 month = d.getUTCMonth(),
22602                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22603                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22604                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22605                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22606                 currentDate = this.date && this.date.valueOf(),
22607                 today = this.UTCToday();
22608         
22609         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22610         
22611 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22612         
22613 //        this.picker.select('>tfoot th.today').
22614 //                                              .text(dates[this.language].today)
22615 //                                              .toggle(this.todayBtn !== false);
22616     
22617         this.updateNavArrows();
22618         this.fillMonths();
22619                                                 
22620         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22621         
22622         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22623          
22624         prevMonth.setUTCDate(day);
22625         
22626         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22627         
22628         var nextMonth = new Date(prevMonth);
22629         
22630         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22631         
22632         nextMonth = nextMonth.valueOf();
22633         
22634         var fillMonths = false;
22635         
22636         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22637         
22638         while(prevMonth.valueOf() <= nextMonth) {
22639             var clsName = '';
22640             
22641             if (prevMonth.getUTCDay() === this.weekStart) {
22642                 if(fillMonths){
22643                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22644                 }
22645                     
22646                 fillMonths = {
22647                     tag: 'tr',
22648                     cn: []
22649                 };
22650                 
22651                 if(this.calendarWeeks){
22652                     // ISO 8601: First week contains first thursday.
22653                     // ISO also states week starts on Monday, but we can be more abstract here.
22654                     var
22655                     // Start of current week: based on weekstart/current date
22656                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22657                     // Thursday of this week
22658                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22659                     // First Thursday of year, year from thursday
22660                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22661                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22662                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22663                     
22664                     fillMonths.cn.push({
22665                         tag: 'td',
22666                         cls: 'cw',
22667                         html: calWeek
22668                     });
22669                 }
22670             }
22671             
22672             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22673                 clsName += ' old';
22674             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22675                 clsName += ' new';
22676             }
22677             if (this.todayHighlight &&
22678                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22679                 prevMonth.getUTCMonth() == today.getMonth() &&
22680                 prevMonth.getUTCDate() == today.getDate()) {
22681                 clsName += ' today';
22682             }
22683             
22684             if (currentDate && prevMonth.valueOf() === currentDate) {
22685                 clsName += ' active';
22686             }
22687             
22688             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22689                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22690                     clsName += ' disabled';
22691             }
22692             
22693             fillMonths.cn.push({
22694                 tag: 'td',
22695                 cls: 'day ' + clsName,
22696                 html: prevMonth.getDate()
22697             });
22698             
22699             prevMonth.setDate(prevMonth.getDate()+1);
22700         }
22701           
22702         var currentYear = this.date && this.date.getUTCFullYear();
22703         var currentMonth = this.date && this.date.getUTCMonth();
22704         
22705         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22706         
22707         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22708             v.removeClass('active');
22709             
22710             if(currentYear === year && k === currentMonth){
22711                 v.addClass('active');
22712             }
22713             
22714             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22715                 v.addClass('disabled');
22716             }
22717             
22718         });
22719         
22720         
22721         year = parseInt(year/10, 10) * 10;
22722         
22723         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22724         
22725         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22726         
22727         year -= 1;
22728         for (var i = -1; i < 11; i++) {
22729             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22730                 tag: 'span',
22731                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22732                 html: year
22733             });
22734             
22735             year += 1;
22736         }
22737     },
22738     
22739     showMode: function(dir) 
22740     {
22741         if (dir) {
22742             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22743         }
22744         
22745         Roo.each(this.picker().select('>div',true).elements, function(v){
22746             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22747             v.hide();
22748         });
22749         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22750     },
22751     
22752     place: function()
22753     {
22754         if(this.isInline) {
22755             return;
22756         }
22757         
22758         this.picker().removeClass(['bottom', 'top']);
22759         
22760         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22761             /*
22762              * place to the top of element!
22763              *
22764              */
22765             
22766             this.picker().addClass('top');
22767             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22768             
22769             return;
22770         }
22771         
22772         this.picker().addClass('bottom');
22773         
22774         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22775     },
22776     
22777     parseDate : function(value)
22778     {
22779         if(!value || value instanceof Date){
22780             return value;
22781         }
22782         var v = Date.parseDate(value, this.format);
22783         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22784             v = Date.parseDate(value, 'Y-m-d');
22785         }
22786         if(!v && this.altFormats){
22787             if(!this.altFormatsArray){
22788                 this.altFormatsArray = this.altFormats.split("|");
22789             }
22790             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22791                 v = Date.parseDate(value, this.altFormatsArray[i]);
22792             }
22793         }
22794         return v;
22795     },
22796     
22797     formatDate : function(date, fmt)
22798     {   
22799         return (!date || !(date instanceof Date)) ?
22800         date : date.dateFormat(fmt || this.format);
22801     },
22802     
22803     onFocus : function()
22804     {
22805         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22806         this.showPopup();
22807     },
22808     
22809     onBlur : function()
22810     {
22811         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22812         
22813         var d = this.inputEl().getValue();
22814         
22815         this.setValue(d);
22816                 
22817         this.hidePopup();
22818     },
22819     
22820     showPopup : function()
22821     {
22822         this.picker().show();
22823         this.update();
22824         this.place();
22825         
22826         this.fireEvent('showpopup', this, this.date);
22827     },
22828     
22829     hidePopup : function()
22830     {
22831         if(this.isInline) {
22832             return;
22833         }
22834         this.picker().hide();
22835         this.viewMode = this.startViewMode;
22836         this.showMode();
22837         
22838         this.fireEvent('hidepopup', this, this.date);
22839         
22840     },
22841     
22842     onMousedown: function(e)
22843     {
22844         e.stopPropagation();
22845         e.preventDefault();
22846     },
22847     
22848     keyup: function(e)
22849     {
22850         Roo.bootstrap.DateField.superclass.keyup.call(this);
22851         this.update();
22852     },
22853
22854     setValue: function(v)
22855     {
22856         if(this.fireEvent('beforeselect', this, v) !== false){
22857             var d = new Date(this.parseDate(v) ).clearTime();
22858         
22859             if(isNaN(d.getTime())){
22860                 this.date = this.viewDate = '';
22861                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22862                 return;
22863             }
22864
22865             v = this.formatDate(d);
22866
22867             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22868
22869             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22870
22871             this.update();
22872
22873             this.fireEvent('select', this, this.date);
22874         }
22875     },
22876     
22877     getValue: function()
22878     {
22879         return this.formatDate(this.date);
22880     },
22881     
22882     fireKey: function(e)
22883     {
22884         if (!this.picker().isVisible()){
22885             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22886                 this.showPopup();
22887             }
22888             return;
22889         }
22890         
22891         var dateChanged = false,
22892         dir, day, month,
22893         newDate, newViewDate;
22894         
22895         switch(e.keyCode){
22896             case 27: // escape
22897                 this.hidePopup();
22898                 e.preventDefault();
22899                 break;
22900             case 37: // left
22901             case 39: // right
22902                 if (!this.keyboardNavigation) {
22903                     break;
22904                 }
22905                 dir = e.keyCode == 37 ? -1 : 1;
22906                 
22907                 if (e.ctrlKey){
22908                     newDate = this.moveYear(this.date, dir);
22909                     newViewDate = this.moveYear(this.viewDate, dir);
22910                 } else if (e.shiftKey){
22911                     newDate = this.moveMonth(this.date, dir);
22912                     newViewDate = this.moveMonth(this.viewDate, dir);
22913                 } else {
22914                     newDate = new Date(this.date);
22915                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22916                     newViewDate = new Date(this.viewDate);
22917                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22918                 }
22919                 if (this.dateWithinRange(newDate)){
22920                     this.date = newDate;
22921                     this.viewDate = newViewDate;
22922                     this.setValue(this.formatDate(this.date));
22923 //                    this.update();
22924                     e.preventDefault();
22925                     dateChanged = true;
22926                 }
22927                 break;
22928             case 38: // up
22929             case 40: // down
22930                 if (!this.keyboardNavigation) {
22931                     break;
22932                 }
22933                 dir = e.keyCode == 38 ? -1 : 1;
22934                 if (e.ctrlKey){
22935                     newDate = this.moveYear(this.date, dir);
22936                     newViewDate = this.moveYear(this.viewDate, dir);
22937                 } else if (e.shiftKey){
22938                     newDate = this.moveMonth(this.date, dir);
22939                     newViewDate = this.moveMonth(this.viewDate, dir);
22940                 } else {
22941                     newDate = new Date(this.date);
22942                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22943                     newViewDate = new Date(this.viewDate);
22944                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22945                 }
22946                 if (this.dateWithinRange(newDate)){
22947                     this.date = newDate;
22948                     this.viewDate = newViewDate;
22949                     this.setValue(this.formatDate(this.date));
22950 //                    this.update();
22951                     e.preventDefault();
22952                     dateChanged = true;
22953                 }
22954                 break;
22955             case 13: // enter
22956                 this.setValue(this.formatDate(this.date));
22957                 this.hidePopup();
22958                 e.preventDefault();
22959                 break;
22960             case 9: // tab
22961                 this.setValue(this.formatDate(this.date));
22962                 this.hidePopup();
22963                 break;
22964             case 16: // shift
22965             case 17: // ctrl
22966             case 18: // alt
22967                 break;
22968             default :
22969                 this.hidePopup();
22970                 
22971         }
22972     },
22973     
22974     
22975     onClick: function(e) 
22976     {
22977         e.stopPropagation();
22978         e.preventDefault();
22979         
22980         var target = e.getTarget();
22981         
22982         if(target.nodeName.toLowerCase() === 'i'){
22983             target = Roo.get(target).dom.parentNode;
22984         }
22985         
22986         var nodeName = target.nodeName;
22987         var className = target.className;
22988         var html = target.innerHTML;
22989         //Roo.log(nodeName);
22990         
22991         switch(nodeName.toLowerCase()) {
22992             case 'th':
22993                 switch(className) {
22994                     case 'switch':
22995                         this.showMode(1);
22996                         break;
22997                     case 'prev':
22998                     case 'next':
22999                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23000                         switch(this.viewMode){
23001                                 case 0:
23002                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23003                                         break;
23004                                 case 1:
23005                                 case 2:
23006                                         this.viewDate = this.moveYear(this.viewDate, dir);
23007                                         break;
23008                         }
23009                         this.fill();
23010                         break;
23011                     case 'today':
23012                         var date = new Date();
23013                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23014 //                        this.fill()
23015                         this.setValue(this.formatDate(this.date));
23016                         
23017                         this.hidePopup();
23018                         break;
23019                 }
23020                 break;
23021             case 'span':
23022                 if (className.indexOf('disabled') < 0) {
23023                 if (!this.viewDate) {
23024                     this.viewDate = new Date();
23025                 }
23026                 this.viewDate.setUTCDate(1);
23027                     if (className.indexOf('month') > -1) {
23028                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23029                     } else {
23030                         var year = parseInt(html, 10) || 0;
23031                         this.viewDate.setUTCFullYear(year);
23032                         
23033                     }
23034                     
23035                     if(this.singleMode){
23036                         this.setValue(this.formatDate(this.viewDate));
23037                         this.hidePopup();
23038                         return;
23039                     }
23040                     
23041                     this.showMode(-1);
23042                     this.fill();
23043                 }
23044                 break;
23045                 
23046             case 'td':
23047                 //Roo.log(className);
23048                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23049                     var day = parseInt(html, 10) || 1;
23050                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23051                         month = (this.viewDate || new Date()).getUTCMonth();
23052
23053                     if (className.indexOf('old') > -1) {
23054                         if(month === 0 ){
23055                             month = 11;
23056                             year -= 1;
23057                         }else{
23058                             month -= 1;
23059                         }
23060                     } else if (className.indexOf('new') > -1) {
23061                         if (month == 11) {
23062                             month = 0;
23063                             year += 1;
23064                         } else {
23065                             month += 1;
23066                         }
23067                     }
23068                     //Roo.log([year,month,day]);
23069                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23070                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23071 //                    this.fill();
23072                     //Roo.log(this.formatDate(this.date));
23073                     this.setValue(this.formatDate(this.date));
23074                     this.hidePopup();
23075                 }
23076                 break;
23077         }
23078     },
23079     
23080     setStartDate: function(startDate)
23081     {
23082         this.startDate = startDate || -Infinity;
23083         if (this.startDate !== -Infinity) {
23084             this.startDate = this.parseDate(this.startDate);
23085         }
23086         this.update();
23087         this.updateNavArrows();
23088     },
23089
23090     setEndDate: function(endDate)
23091     {
23092         this.endDate = endDate || Infinity;
23093         if (this.endDate !== Infinity) {
23094             this.endDate = this.parseDate(this.endDate);
23095         }
23096         this.update();
23097         this.updateNavArrows();
23098     },
23099     
23100     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23101     {
23102         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23103         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23104             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23105         }
23106         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23107             return parseInt(d, 10);
23108         });
23109         this.update();
23110         this.updateNavArrows();
23111     },
23112     
23113     updateNavArrows: function() 
23114     {
23115         if(this.singleMode){
23116             return;
23117         }
23118         
23119         var d = new Date(this.viewDate),
23120         year = d.getUTCFullYear(),
23121         month = d.getUTCMonth();
23122         
23123         Roo.each(this.picker().select('.prev', true).elements, function(v){
23124             v.show();
23125             switch (this.viewMode) {
23126                 case 0:
23127
23128                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23129                         v.hide();
23130                     }
23131                     break;
23132                 case 1:
23133                 case 2:
23134                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23135                         v.hide();
23136                     }
23137                     break;
23138             }
23139         });
23140         
23141         Roo.each(this.picker().select('.next', true).elements, function(v){
23142             v.show();
23143             switch (this.viewMode) {
23144                 case 0:
23145
23146                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23147                         v.hide();
23148                     }
23149                     break;
23150                 case 1:
23151                 case 2:
23152                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23153                         v.hide();
23154                     }
23155                     break;
23156             }
23157         })
23158     },
23159     
23160     moveMonth: function(date, dir)
23161     {
23162         if (!dir) {
23163             return date;
23164         }
23165         var new_date = new Date(date.valueOf()),
23166         day = new_date.getUTCDate(),
23167         month = new_date.getUTCMonth(),
23168         mag = Math.abs(dir),
23169         new_month, test;
23170         dir = dir > 0 ? 1 : -1;
23171         if (mag == 1){
23172             test = dir == -1
23173             // If going back one month, make sure month is not current month
23174             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23175             ? function(){
23176                 return new_date.getUTCMonth() == month;
23177             }
23178             // If going forward one month, make sure month is as expected
23179             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23180             : function(){
23181                 return new_date.getUTCMonth() != new_month;
23182             };
23183             new_month = month + dir;
23184             new_date.setUTCMonth(new_month);
23185             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23186             if (new_month < 0 || new_month > 11) {
23187                 new_month = (new_month + 12) % 12;
23188             }
23189         } else {
23190             // For magnitudes >1, move one month at a time...
23191             for (var i=0; i<mag; i++) {
23192                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23193                 new_date = this.moveMonth(new_date, dir);
23194             }
23195             // ...then reset the day, keeping it in the new month
23196             new_month = new_date.getUTCMonth();
23197             new_date.setUTCDate(day);
23198             test = function(){
23199                 return new_month != new_date.getUTCMonth();
23200             };
23201         }
23202         // Common date-resetting loop -- if date is beyond end of month, make it
23203         // end of month
23204         while (test()){
23205             new_date.setUTCDate(--day);
23206             new_date.setUTCMonth(new_month);
23207         }
23208         return new_date;
23209     },
23210
23211     moveYear: function(date, dir)
23212     {
23213         return this.moveMonth(date, dir*12);
23214     },
23215
23216     dateWithinRange: function(date)
23217     {
23218         return date >= this.startDate && date <= this.endDate;
23219     },
23220
23221     
23222     remove: function() 
23223     {
23224         this.picker().remove();
23225     },
23226     
23227     validateValue : function(value)
23228     {
23229         if(this.getVisibilityEl().hasClass('hidden')){
23230             return true;
23231         }
23232         
23233         if(value.length < 1)  {
23234             if(this.allowBlank){
23235                 return true;
23236             }
23237             return false;
23238         }
23239         
23240         if(value.length < this.minLength){
23241             return false;
23242         }
23243         if(value.length > this.maxLength){
23244             return false;
23245         }
23246         if(this.vtype){
23247             var vt = Roo.form.VTypes;
23248             if(!vt[this.vtype](value, this)){
23249                 return false;
23250             }
23251         }
23252         if(typeof this.validator == "function"){
23253             var msg = this.validator(value);
23254             if(msg !== true){
23255                 return false;
23256             }
23257         }
23258         
23259         if(this.regex && !this.regex.test(value)){
23260             return false;
23261         }
23262         
23263         if(typeof(this.parseDate(value)) == 'undefined'){
23264             return false;
23265         }
23266         
23267         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23268             return false;
23269         }      
23270         
23271         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23272             return false;
23273         } 
23274         
23275         
23276         return true;
23277     },
23278     
23279     reset : function()
23280     {
23281         this.date = this.viewDate = '';
23282         
23283         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23284     }
23285    
23286 });
23287
23288 Roo.apply(Roo.bootstrap.DateField,  {
23289     
23290     head : {
23291         tag: 'thead',
23292         cn: [
23293         {
23294             tag: 'tr',
23295             cn: [
23296             {
23297                 tag: 'th',
23298                 cls: 'prev',
23299                 html: '<i class="fa fa-arrow-left"/>'
23300             },
23301             {
23302                 tag: 'th',
23303                 cls: 'switch',
23304                 colspan: '5'
23305             },
23306             {
23307                 tag: 'th',
23308                 cls: 'next',
23309                 html: '<i class="fa fa-arrow-right"/>'
23310             }
23311
23312             ]
23313         }
23314         ]
23315     },
23316     
23317     content : {
23318         tag: 'tbody',
23319         cn: [
23320         {
23321             tag: 'tr',
23322             cn: [
23323             {
23324                 tag: 'td',
23325                 colspan: '7'
23326             }
23327             ]
23328         }
23329         ]
23330     },
23331     
23332     footer : {
23333         tag: 'tfoot',
23334         cn: [
23335         {
23336             tag: 'tr',
23337             cn: [
23338             {
23339                 tag: 'th',
23340                 colspan: '7',
23341                 cls: 'today'
23342             }
23343                     
23344             ]
23345         }
23346         ]
23347     },
23348     
23349     dates:{
23350         en: {
23351             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23352             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23353             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23354             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23355             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23356             today: "Today"
23357         }
23358     },
23359     
23360     modes: [
23361     {
23362         clsName: 'days',
23363         navFnc: 'Month',
23364         navStep: 1
23365     },
23366     {
23367         clsName: 'months',
23368         navFnc: 'FullYear',
23369         navStep: 1
23370     },
23371     {
23372         clsName: 'years',
23373         navFnc: 'FullYear',
23374         navStep: 10
23375     }]
23376 });
23377
23378 Roo.apply(Roo.bootstrap.DateField,  {
23379   
23380     template : {
23381         tag: 'div',
23382         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23383         cn: [
23384         {
23385             tag: 'div',
23386             cls: 'datepicker-days',
23387             cn: [
23388             {
23389                 tag: 'table',
23390                 cls: 'table-condensed',
23391                 cn:[
23392                 Roo.bootstrap.DateField.head,
23393                 {
23394                     tag: 'tbody'
23395                 },
23396                 Roo.bootstrap.DateField.footer
23397                 ]
23398             }
23399             ]
23400         },
23401         {
23402             tag: 'div',
23403             cls: 'datepicker-months',
23404             cn: [
23405             {
23406                 tag: 'table',
23407                 cls: 'table-condensed',
23408                 cn:[
23409                 Roo.bootstrap.DateField.head,
23410                 Roo.bootstrap.DateField.content,
23411                 Roo.bootstrap.DateField.footer
23412                 ]
23413             }
23414             ]
23415         },
23416         {
23417             tag: 'div',
23418             cls: 'datepicker-years',
23419             cn: [
23420             {
23421                 tag: 'table',
23422                 cls: 'table-condensed',
23423                 cn:[
23424                 Roo.bootstrap.DateField.head,
23425                 Roo.bootstrap.DateField.content,
23426                 Roo.bootstrap.DateField.footer
23427                 ]
23428             }
23429             ]
23430         }
23431         ]
23432     }
23433 });
23434
23435  
23436
23437  /*
23438  * - LGPL
23439  *
23440  * TimeField
23441  * 
23442  */
23443
23444 /**
23445  * @class Roo.bootstrap.TimeField
23446  * @extends Roo.bootstrap.Input
23447  * Bootstrap DateField class
23448  * 
23449  * 
23450  * @constructor
23451  * Create a new TimeField
23452  * @param {Object} config The config object
23453  */
23454
23455 Roo.bootstrap.TimeField = function(config){
23456     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23457     this.addEvents({
23458             /**
23459              * @event show
23460              * Fires when this field show.
23461              * @param {Roo.bootstrap.DateField} thisthis
23462              * @param {Mixed} date The date value
23463              */
23464             show : true,
23465             /**
23466              * @event show
23467              * Fires when this field hide.
23468              * @param {Roo.bootstrap.DateField} this
23469              * @param {Mixed} date The date value
23470              */
23471             hide : true,
23472             /**
23473              * @event select
23474              * Fires when select a date.
23475              * @param {Roo.bootstrap.DateField} this
23476              * @param {Mixed} date The date value
23477              */
23478             select : true
23479         });
23480 };
23481
23482 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23483     
23484     /**
23485      * @cfg {String} format
23486      * The default time format string which can be overriden for localization support.  The format must be
23487      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23488      */
23489     format : "H:i",
23490
23491     getAutoCreate : function()
23492     {
23493         this.after = '<i class="fa far fa-clock"></i>';
23494         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23495         
23496          
23497     },
23498     onRender: function(ct, position)
23499     {
23500         
23501         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23502                 
23503         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23504         
23505         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23506         
23507         this.pop = this.picker().select('>.datepicker-time',true).first();
23508         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23509         
23510         this.picker().on('mousedown', this.onMousedown, this);
23511         this.picker().on('click', this.onClick, this);
23512         
23513         this.picker().addClass('datepicker-dropdown');
23514     
23515         this.fillTime();
23516         this.update();
23517             
23518         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23519         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23520         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23521         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23522         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23523         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23524
23525     },
23526     
23527     fireKey: function(e){
23528         if (!this.picker().isVisible()){
23529             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23530                 this.show();
23531             }
23532             return;
23533         }
23534
23535         e.preventDefault();
23536         
23537         switch(e.keyCode){
23538             case 27: // escape
23539                 this.hide();
23540                 break;
23541             case 37: // left
23542             case 39: // right
23543                 this.onTogglePeriod();
23544                 break;
23545             case 38: // up
23546                 this.onIncrementMinutes();
23547                 break;
23548             case 40: // down
23549                 this.onDecrementMinutes();
23550                 break;
23551             case 13: // enter
23552             case 9: // tab
23553                 this.setTime();
23554                 break;
23555         }
23556     },
23557     
23558     onClick: function(e) {
23559         e.stopPropagation();
23560         e.preventDefault();
23561     },
23562     
23563     picker : function()
23564     {
23565         return this.pickerEl;
23566     },
23567     
23568     fillTime: function()
23569     {    
23570         var time = this.pop.select('tbody', true).first();
23571         
23572         time.dom.innerHTML = '';
23573         
23574         time.createChild({
23575             tag: 'tr',
23576             cn: [
23577                 {
23578                     tag: 'td',
23579                     cn: [
23580                         {
23581                             tag: 'a',
23582                             href: '#',
23583                             cls: 'btn',
23584                             cn: [
23585                                 {
23586                                     tag: 'i',
23587                                     cls: 'hours-up fa fas fa-chevron-up'
23588                                 }
23589                             ]
23590                         } 
23591                     ]
23592                 },
23593                 {
23594                     tag: 'td',
23595                     cls: 'separator'
23596                 },
23597                 {
23598                     tag: 'td',
23599                     cn: [
23600                         {
23601                             tag: 'a',
23602                             href: '#',
23603                             cls: 'btn',
23604                             cn: [
23605                                 {
23606                                     tag: 'i',
23607                                     cls: 'minutes-up fa fas fa-chevron-up'
23608                                 }
23609                             ]
23610                         }
23611                     ]
23612                 },
23613                 {
23614                     tag: 'td',
23615                     cls: 'separator'
23616                 }
23617             ]
23618         });
23619         
23620         time.createChild({
23621             tag: 'tr',
23622             cn: [
23623                 {
23624                     tag: 'td',
23625                     cn: [
23626                         {
23627                             tag: 'span',
23628                             cls: 'timepicker-hour',
23629                             html: '00'
23630                         }  
23631                     ]
23632                 },
23633                 {
23634                     tag: 'td',
23635                     cls: 'separator',
23636                     html: ':'
23637                 },
23638                 {
23639                     tag: 'td',
23640                     cn: [
23641                         {
23642                             tag: 'span',
23643                             cls: 'timepicker-minute',
23644                             html: '00'
23645                         }  
23646                     ]
23647                 },
23648                 {
23649                     tag: 'td',
23650                     cls: 'separator'
23651                 },
23652                 {
23653                     tag: 'td',
23654                     cn: [
23655                         {
23656                             tag: 'button',
23657                             type: 'button',
23658                             cls: 'btn btn-primary period',
23659                             html: 'AM'
23660                             
23661                         }
23662                     ]
23663                 }
23664             ]
23665         });
23666         
23667         time.createChild({
23668             tag: 'tr',
23669             cn: [
23670                 {
23671                     tag: 'td',
23672                     cn: [
23673                         {
23674                             tag: 'a',
23675                             href: '#',
23676                             cls: 'btn',
23677                             cn: [
23678                                 {
23679                                     tag: 'span',
23680                                     cls: 'hours-down fa fas fa-chevron-down'
23681                                 }
23682                             ]
23683                         }
23684                     ]
23685                 },
23686                 {
23687                     tag: 'td',
23688                     cls: 'separator'
23689                 },
23690                 {
23691                     tag: 'td',
23692                     cn: [
23693                         {
23694                             tag: 'a',
23695                             href: '#',
23696                             cls: 'btn',
23697                             cn: [
23698                                 {
23699                                     tag: 'span',
23700                                     cls: 'minutes-down fa fas fa-chevron-down'
23701                                 }
23702                             ]
23703                         }
23704                     ]
23705                 },
23706                 {
23707                     tag: 'td',
23708                     cls: 'separator'
23709                 }
23710             ]
23711         });
23712         
23713     },
23714     
23715     update: function()
23716     {
23717         
23718         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23719         
23720         this.fill();
23721     },
23722     
23723     fill: function() 
23724     {
23725         var hours = this.time.getHours();
23726         var minutes = this.time.getMinutes();
23727         var period = 'AM';
23728         
23729         if(hours > 11){
23730             period = 'PM';
23731         }
23732         
23733         if(hours == 0){
23734             hours = 12;
23735         }
23736         
23737         
23738         if(hours > 12){
23739             hours = hours - 12;
23740         }
23741         
23742         if(hours < 10){
23743             hours = '0' + hours;
23744         }
23745         
23746         if(minutes < 10){
23747             minutes = '0' + minutes;
23748         }
23749         
23750         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23751         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23752         this.pop.select('button', true).first().dom.innerHTML = period;
23753         
23754     },
23755     
23756     place: function()
23757     {   
23758         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23759         
23760         var cls = ['bottom'];
23761         
23762         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23763             cls.pop();
23764             cls.push('top');
23765         }
23766         
23767         cls.push('right');
23768         
23769         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23770             cls.pop();
23771             cls.push('left');
23772         }
23773         //this.picker().setXY(20000,20000);
23774         this.picker().addClass(cls.join('-'));
23775         
23776         var _this = this;
23777         
23778         Roo.each(cls, function(c){
23779             if(c == 'bottom'){
23780                 (function() {
23781                  //  
23782                 }).defer(200);
23783                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23784                 //_this.picker().setTop(_this.inputEl().getHeight());
23785                 return;
23786             }
23787             if(c == 'top'){
23788                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23789                 
23790                 //_this.picker().setTop(0 - _this.picker().getHeight());
23791                 return;
23792             }
23793             /*
23794             if(c == 'left'){
23795                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23796                 return;
23797             }
23798             if(c == 'right'){
23799                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23800                 return;
23801             }
23802             */
23803         });
23804         
23805     },
23806   
23807     onFocus : function()
23808     {
23809         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23810         this.show();
23811     },
23812     
23813     onBlur : function()
23814     {
23815         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23816         this.hide();
23817     },
23818     
23819     show : function()
23820     {
23821         this.picker().show();
23822         this.pop.show();
23823         this.update();
23824         this.place();
23825         
23826         this.fireEvent('show', this, this.date);
23827     },
23828     
23829     hide : function()
23830     {
23831         this.picker().hide();
23832         this.pop.hide();
23833         
23834         this.fireEvent('hide', this, this.date);
23835     },
23836     
23837     setTime : function()
23838     {
23839         this.hide();
23840         this.setValue(this.time.format(this.format));
23841         
23842         this.fireEvent('select', this, this.date);
23843         
23844         
23845     },
23846     
23847     onMousedown: function(e){
23848         e.stopPropagation();
23849         e.preventDefault();
23850     },
23851     
23852     onIncrementHours: function()
23853     {
23854         Roo.log('onIncrementHours');
23855         this.time = this.time.add(Date.HOUR, 1);
23856         this.update();
23857         
23858     },
23859     
23860     onDecrementHours: function()
23861     {
23862         Roo.log('onDecrementHours');
23863         this.time = this.time.add(Date.HOUR, -1);
23864         this.update();
23865     },
23866     
23867     onIncrementMinutes: function()
23868     {
23869         Roo.log('onIncrementMinutes');
23870         this.time = this.time.add(Date.MINUTE, 1);
23871         this.update();
23872     },
23873     
23874     onDecrementMinutes: function()
23875     {
23876         Roo.log('onDecrementMinutes');
23877         this.time = this.time.add(Date.MINUTE, -1);
23878         this.update();
23879     },
23880     
23881     onTogglePeriod: function()
23882     {
23883         Roo.log('onTogglePeriod');
23884         this.time = this.time.add(Date.HOUR, 12);
23885         this.update();
23886     }
23887     
23888    
23889 });
23890  
23891
23892 Roo.apply(Roo.bootstrap.TimeField,  {
23893   
23894     template : {
23895         tag: 'div',
23896         cls: 'datepicker dropdown-menu',
23897         cn: [
23898             {
23899                 tag: 'div',
23900                 cls: 'datepicker-time',
23901                 cn: [
23902                 {
23903                     tag: 'table',
23904                     cls: 'table-condensed',
23905                     cn:[
23906                         {
23907                             tag: 'tbody',
23908                             cn: [
23909                                 {
23910                                     tag: 'tr',
23911                                     cn: [
23912                                     {
23913                                         tag: 'td',
23914                                         colspan: '7'
23915                                     }
23916                                     ]
23917                                 }
23918                             ]
23919                         },
23920                         {
23921                             tag: 'tfoot',
23922                             cn: [
23923                                 {
23924                                     tag: 'tr',
23925                                     cn: [
23926                                     {
23927                                         tag: 'th',
23928                                         colspan: '7',
23929                                         cls: '',
23930                                         cn: [
23931                                             {
23932                                                 tag: 'button',
23933                                                 cls: 'btn btn-info ok',
23934                                                 html: 'OK'
23935                                             }
23936                                         ]
23937                                     }
23938                     
23939                                     ]
23940                                 }
23941                             ]
23942                         }
23943                     ]
23944                 }
23945                 ]
23946             }
23947         ]
23948     }
23949 });
23950
23951  
23952
23953  /*
23954  * - LGPL
23955  *
23956  * MonthField
23957  * 
23958  */
23959
23960 /**
23961  * @class Roo.bootstrap.MonthField
23962  * @extends Roo.bootstrap.Input
23963  * Bootstrap MonthField class
23964  * 
23965  * @cfg {String} language default en
23966  * 
23967  * @constructor
23968  * Create a new MonthField
23969  * @param {Object} config The config object
23970  */
23971
23972 Roo.bootstrap.MonthField = function(config){
23973     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23974     
23975     this.addEvents({
23976         /**
23977          * @event show
23978          * Fires when this field show.
23979          * @param {Roo.bootstrap.MonthField} this
23980          * @param {Mixed} date The date value
23981          */
23982         show : true,
23983         /**
23984          * @event show
23985          * Fires when this field hide.
23986          * @param {Roo.bootstrap.MonthField} this
23987          * @param {Mixed} date The date value
23988          */
23989         hide : true,
23990         /**
23991          * @event select
23992          * Fires when select a date.
23993          * @param {Roo.bootstrap.MonthField} this
23994          * @param {String} oldvalue The old value
23995          * @param {String} newvalue The new value
23996          */
23997         select : true
23998     });
23999 };
24000
24001 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24002     
24003     onRender: function(ct, position)
24004     {
24005         
24006         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24007         
24008         this.language = this.language || 'en';
24009         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24010         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24011         
24012         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24013         this.isInline = false;
24014         this.isInput = true;
24015         this.component = this.el.select('.add-on', true).first() || false;
24016         this.component = (this.component && this.component.length === 0) ? false : this.component;
24017         this.hasInput = this.component && this.inputEL().length;
24018         
24019         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24020         
24021         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24022         
24023         this.picker().on('mousedown', this.onMousedown, this);
24024         this.picker().on('click', this.onClick, this);
24025         
24026         this.picker().addClass('datepicker-dropdown');
24027         
24028         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24029             v.setStyle('width', '189px');
24030         });
24031         
24032         this.fillMonths();
24033         
24034         this.update();
24035         
24036         if(this.isInline) {
24037             this.show();
24038         }
24039         
24040     },
24041     
24042     setValue: function(v, suppressEvent)
24043     {   
24044         var o = this.getValue();
24045         
24046         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24047         
24048         this.update();
24049
24050         if(suppressEvent !== true){
24051             this.fireEvent('select', this, o, v);
24052         }
24053         
24054     },
24055     
24056     getValue: function()
24057     {
24058         return this.value;
24059     },
24060     
24061     onClick: function(e) 
24062     {
24063         e.stopPropagation();
24064         e.preventDefault();
24065         
24066         var target = e.getTarget();
24067         
24068         if(target.nodeName.toLowerCase() === 'i'){
24069             target = Roo.get(target).dom.parentNode;
24070         }
24071         
24072         var nodeName = target.nodeName;
24073         var className = target.className;
24074         var html = target.innerHTML;
24075         
24076         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24077             return;
24078         }
24079         
24080         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24081         
24082         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24083         
24084         this.hide();
24085                         
24086     },
24087     
24088     picker : function()
24089     {
24090         return this.pickerEl;
24091     },
24092     
24093     fillMonths: function()
24094     {    
24095         var i = 0;
24096         var months = this.picker().select('>.datepicker-months td', true).first();
24097         
24098         months.dom.innerHTML = '';
24099         
24100         while (i < 12) {
24101             var month = {
24102                 tag: 'span',
24103                 cls: 'month',
24104                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24105             };
24106             
24107             months.createChild(month);
24108         }
24109         
24110     },
24111     
24112     update: function()
24113     {
24114         var _this = this;
24115         
24116         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24117             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24118         }
24119         
24120         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24121             e.removeClass('active');
24122             
24123             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24124                 e.addClass('active');
24125             }
24126         })
24127     },
24128     
24129     place: function()
24130     {
24131         if(this.isInline) {
24132             return;
24133         }
24134         
24135         this.picker().removeClass(['bottom', 'top']);
24136         
24137         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24138             /*
24139              * place to the top of element!
24140              *
24141              */
24142             
24143             this.picker().addClass('top');
24144             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24145             
24146             return;
24147         }
24148         
24149         this.picker().addClass('bottom');
24150         
24151         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24152     },
24153     
24154     onFocus : function()
24155     {
24156         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24157         this.show();
24158     },
24159     
24160     onBlur : function()
24161     {
24162         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24163         
24164         var d = this.inputEl().getValue();
24165         
24166         this.setValue(d);
24167                 
24168         this.hide();
24169     },
24170     
24171     show : function()
24172     {
24173         this.picker().show();
24174         this.picker().select('>.datepicker-months', true).first().show();
24175         this.update();
24176         this.place();
24177         
24178         this.fireEvent('show', this, this.date);
24179     },
24180     
24181     hide : function()
24182     {
24183         if(this.isInline) {
24184             return;
24185         }
24186         this.picker().hide();
24187         this.fireEvent('hide', this, this.date);
24188         
24189     },
24190     
24191     onMousedown: function(e)
24192     {
24193         e.stopPropagation();
24194         e.preventDefault();
24195     },
24196     
24197     keyup: function(e)
24198     {
24199         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24200         this.update();
24201     },
24202
24203     fireKey: function(e)
24204     {
24205         if (!this.picker().isVisible()){
24206             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24207                 this.show();
24208             }
24209             return;
24210         }
24211         
24212         var dir;
24213         
24214         switch(e.keyCode){
24215             case 27: // escape
24216                 this.hide();
24217                 e.preventDefault();
24218                 break;
24219             case 37: // left
24220             case 39: // right
24221                 dir = e.keyCode == 37 ? -1 : 1;
24222                 
24223                 this.vIndex = this.vIndex + dir;
24224                 
24225                 if(this.vIndex < 0){
24226                     this.vIndex = 0;
24227                 }
24228                 
24229                 if(this.vIndex > 11){
24230                     this.vIndex = 11;
24231                 }
24232                 
24233                 if(isNaN(this.vIndex)){
24234                     this.vIndex = 0;
24235                 }
24236                 
24237                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24238                 
24239                 break;
24240             case 38: // up
24241             case 40: // down
24242                 
24243                 dir = e.keyCode == 38 ? -1 : 1;
24244                 
24245                 this.vIndex = this.vIndex + dir * 4;
24246                 
24247                 if(this.vIndex < 0){
24248                     this.vIndex = 0;
24249                 }
24250                 
24251                 if(this.vIndex > 11){
24252                     this.vIndex = 11;
24253                 }
24254                 
24255                 if(isNaN(this.vIndex)){
24256                     this.vIndex = 0;
24257                 }
24258                 
24259                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24260                 break;
24261                 
24262             case 13: // enter
24263                 
24264                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24265                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24266                 }
24267                 
24268                 this.hide();
24269                 e.preventDefault();
24270                 break;
24271             case 9: // tab
24272                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24273                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24274                 }
24275                 this.hide();
24276                 break;
24277             case 16: // shift
24278             case 17: // ctrl
24279             case 18: // alt
24280                 break;
24281             default :
24282                 this.hide();
24283                 
24284         }
24285     },
24286     
24287     remove: function() 
24288     {
24289         this.picker().remove();
24290     }
24291    
24292 });
24293
24294 Roo.apply(Roo.bootstrap.MonthField,  {
24295     
24296     content : {
24297         tag: 'tbody',
24298         cn: [
24299         {
24300             tag: 'tr',
24301             cn: [
24302             {
24303                 tag: 'td',
24304                 colspan: '7'
24305             }
24306             ]
24307         }
24308         ]
24309     },
24310     
24311     dates:{
24312         en: {
24313             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24314             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24315         }
24316     }
24317 });
24318
24319 Roo.apply(Roo.bootstrap.MonthField,  {
24320   
24321     template : {
24322         tag: 'div',
24323         cls: 'datepicker dropdown-menu roo-dynamic',
24324         cn: [
24325             {
24326                 tag: 'div',
24327                 cls: 'datepicker-months',
24328                 cn: [
24329                 {
24330                     tag: 'table',
24331                     cls: 'table-condensed',
24332                     cn:[
24333                         Roo.bootstrap.DateField.content
24334                     ]
24335                 }
24336                 ]
24337             }
24338         ]
24339     }
24340 });
24341
24342  
24343
24344  
24345  /*
24346  * - LGPL
24347  *
24348  * CheckBox
24349  * 
24350  */
24351
24352 /**
24353  * @class Roo.bootstrap.CheckBox
24354  * @extends Roo.bootstrap.Input
24355  * Bootstrap CheckBox class
24356  * 
24357  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24358  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24359  * @cfg {String} boxLabel The text that appears beside the checkbox
24360  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24361  * @cfg {Boolean} checked initnal the element
24362  * @cfg {Boolean} inline inline the element (default false)
24363  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24364  * @cfg {String} tooltip label tooltip
24365  * 
24366  * @constructor
24367  * Create a new CheckBox
24368  * @param {Object} config The config object
24369  */
24370
24371 Roo.bootstrap.CheckBox = function(config){
24372     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24373    
24374     this.addEvents({
24375         /**
24376         * @event check
24377         * Fires when the element is checked or unchecked.
24378         * @param {Roo.bootstrap.CheckBox} this This input
24379         * @param {Boolean} checked The new checked value
24380         */
24381        check : true,
24382        /**
24383         * @event click
24384         * Fires when the element is click.
24385         * @param {Roo.bootstrap.CheckBox} this This input
24386         */
24387        click : true
24388     });
24389     
24390 };
24391
24392 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24393   
24394     inputType: 'checkbox',
24395     inputValue: 1,
24396     valueOff: 0,
24397     boxLabel: false,
24398     checked: false,
24399     weight : false,
24400     inline: false,
24401     tooltip : '',
24402     
24403     // checkbox success does not make any sense really.. 
24404     invalidClass : "",
24405     validClass : "",
24406     
24407     
24408     getAutoCreate : function()
24409     {
24410         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24411         
24412         var id = Roo.id();
24413         
24414         var cfg = {};
24415         
24416         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24417         
24418         if(this.inline){
24419             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24420         }
24421         
24422         var input =  {
24423             tag: 'input',
24424             id : id,
24425             type : this.inputType,
24426             value : this.inputValue,
24427             cls : 'roo-' + this.inputType, //'form-box',
24428             placeholder : this.placeholder || ''
24429             
24430         };
24431         
24432         if(this.inputType != 'radio'){
24433             var hidden =  {
24434                 tag: 'input',
24435                 type : 'hidden',
24436                 cls : 'roo-hidden-value',
24437                 value : this.checked ? this.inputValue : this.valueOff
24438             };
24439         }
24440         
24441             
24442         if (this.weight) { // Validity check?
24443             cfg.cls += " " + this.inputType + "-" + this.weight;
24444         }
24445         
24446         if (this.disabled) {
24447             input.disabled=true;
24448         }
24449         
24450         if(this.checked){
24451             input.checked = this.checked;
24452         }
24453         
24454         if (this.name) {
24455             
24456             input.name = this.name;
24457             
24458             if(this.inputType != 'radio'){
24459                 hidden.name = this.name;
24460                 input.name = '_hidden_' + this.name;
24461             }
24462         }
24463         
24464         if (this.size) {
24465             input.cls += ' input-' + this.size;
24466         }
24467         
24468         var settings=this;
24469         
24470         ['xs','sm','md','lg'].map(function(size){
24471             if (settings[size]) {
24472                 cfg.cls += ' col-' + size + '-' + settings[size];
24473             }
24474         });
24475         
24476         var inputblock = input;
24477          
24478         if (this.before || this.after) {
24479             
24480             inputblock = {
24481                 cls : 'input-group',
24482                 cn :  [] 
24483             };
24484             
24485             if (this.before) {
24486                 inputblock.cn.push({
24487                     tag :'span',
24488                     cls : 'input-group-addon',
24489                     html : this.before
24490                 });
24491             }
24492             
24493             inputblock.cn.push(input);
24494             
24495             if(this.inputType != 'radio'){
24496                 inputblock.cn.push(hidden);
24497             }
24498             
24499             if (this.after) {
24500                 inputblock.cn.push({
24501                     tag :'span',
24502                     cls : 'input-group-addon',
24503                     html : this.after
24504                 });
24505             }
24506             
24507         }
24508         var boxLabelCfg = false;
24509         
24510         if(this.boxLabel){
24511            
24512             boxLabelCfg = {
24513                 tag: 'label',
24514                 //'for': id, // box label is handled by onclick - so no for...
24515                 cls: 'box-label',
24516                 html: this.boxLabel
24517             };
24518             if(this.tooltip){
24519                 boxLabelCfg.tooltip = this.tooltip;
24520             }
24521              
24522         }
24523         
24524         
24525         if (align ==='left' && this.fieldLabel.length) {
24526 //                Roo.log("left and has label");
24527             cfg.cn = [
24528                 {
24529                     tag: 'label',
24530                     'for' :  id,
24531                     cls : 'control-label',
24532                     html : this.fieldLabel
24533                 },
24534                 {
24535                     cls : "", 
24536                     cn: [
24537                         inputblock
24538                     ]
24539                 }
24540             ];
24541             
24542             if (boxLabelCfg) {
24543                 cfg.cn[1].cn.push(boxLabelCfg);
24544             }
24545             
24546             if(this.labelWidth > 12){
24547                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24548             }
24549             
24550             if(this.labelWidth < 13 && this.labelmd == 0){
24551                 this.labelmd = this.labelWidth;
24552             }
24553             
24554             if(this.labellg > 0){
24555                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24556                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24557             }
24558             
24559             if(this.labelmd > 0){
24560                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24561                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24562             }
24563             
24564             if(this.labelsm > 0){
24565                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24566                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24567             }
24568             
24569             if(this.labelxs > 0){
24570                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24571                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24572             }
24573             
24574         } else if ( this.fieldLabel.length) {
24575 //                Roo.log(" label");
24576                 cfg.cn = [
24577                    
24578                     {
24579                         tag: this.boxLabel ? 'span' : 'label',
24580                         'for': id,
24581                         cls: 'control-label box-input-label',
24582                         //cls : 'input-group-addon',
24583                         html : this.fieldLabel
24584                     },
24585                     
24586                     inputblock
24587                     
24588                 ];
24589                 if (boxLabelCfg) {
24590                     cfg.cn.push(boxLabelCfg);
24591                 }
24592
24593         } else {
24594             
24595 //                Roo.log(" no label && no align");
24596                 cfg.cn = [  inputblock ] ;
24597                 if (boxLabelCfg) {
24598                     cfg.cn.push(boxLabelCfg);
24599                 }
24600
24601                 
24602         }
24603         
24604        
24605         
24606         if(this.inputType != 'radio'){
24607             cfg.cn.push(hidden);
24608         }
24609         
24610         return cfg;
24611         
24612     },
24613     
24614     /**
24615      * return the real input element.
24616      */
24617     inputEl: function ()
24618     {
24619         return this.el.select('input.roo-' + this.inputType,true).first();
24620     },
24621     hiddenEl: function ()
24622     {
24623         return this.el.select('input.roo-hidden-value',true).first();
24624     },
24625     
24626     labelEl: function()
24627     {
24628         return this.el.select('label.control-label',true).first();
24629     },
24630     /* depricated... */
24631     
24632     label: function()
24633     {
24634         return this.labelEl();
24635     },
24636     
24637     boxLabelEl: function()
24638     {
24639         return this.el.select('label.box-label',true).first();
24640     },
24641     
24642     initEvents : function()
24643     {
24644 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24645         
24646         this.inputEl().on('click', this.onClick,  this);
24647         
24648         if (this.boxLabel) { 
24649             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24650         }
24651         
24652         this.startValue = this.getValue();
24653         
24654         if(this.groupId){
24655             Roo.bootstrap.CheckBox.register(this);
24656         }
24657     },
24658     
24659     onClick : function(e)
24660     {   
24661         if(this.fireEvent('click', this, e) !== false){
24662             this.setChecked(!this.checked);
24663         }
24664         
24665     },
24666     
24667     setChecked : function(state,suppressEvent)
24668     {
24669         this.startValue = this.getValue();
24670
24671         if(this.inputType == 'radio'){
24672             
24673             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24674                 e.dom.checked = false;
24675             });
24676             
24677             this.inputEl().dom.checked = true;
24678             
24679             this.inputEl().dom.value = this.inputValue;
24680             
24681             if(suppressEvent !== true){
24682                 this.fireEvent('check', this, true);
24683             }
24684             
24685             this.validate();
24686             
24687             return;
24688         }
24689         
24690         this.checked = state;
24691         
24692         this.inputEl().dom.checked = state;
24693         
24694         
24695         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24696         
24697         if(suppressEvent !== true){
24698             this.fireEvent('check', this, state);
24699         }
24700         
24701         this.validate();
24702     },
24703     
24704     getValue : function()
24705     {
24706         if(this.inputType == 'radio'){
24707             return this.getGroupValue();
24708         }
24709         
24710         return this.hiddenEl().dom.value;
24711         
24712     },
24713     
24714     getGroupValue : function()
24715     {
24716         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24717             return '';
24718         }
24719         
24720         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24721     },
24722     
24723     setValue : function(v,suppressEvent)
24724     {
24725         if(this.inputType == 'radio'){
24726             this.setGroupValue(v, suppressEvent);
24727             return;
24728         }
24729         
24730         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24731         
24732         this.validate();
24733     },
24734     
24735     setGroupValue : function(v, suppressEvent)
24736     {
24737         this.startValue = this.getValue();
24738         
24739         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24740             e.dom.checked = false;
24741             
24742             if(e.dom.value == v){
24743                 e.dom.checked = true;
24744             }
24745         });
24746         
24747         if(suppressEvent !== true){
24748             this.fireEvent('check', this, true);
24749         }
24750
24751         this.validate();
24752         
24753         return;
24754     },
24755     
24756     validate : function()
24757     {
24758         if(this.getVisibilityEl().hasClass('hidden')){
24759             return true;
24760         }
24761         
24762         if(
24763                 this.disabled || 
24764                 (this.inputType == 'radio' && this.validateRadio()) ||
24765                 (this.inputType == 'checkbox' && this.validateCheckbox())
24766         ){
24767             this.markValid();
24768             return true;
24769         }
24770         
24771         this.markInvalid();
24772         return false;
24773     },
24774     
24775     validateRadio : function()
24776     {
24777         if(this.getVisibilityEl().hasClass('hidden')){
24778             return true;
24779         }
24780         
24781         if(this.allowBlank){
24782             return true;
24783         }
24784         
24785         var valid = false;
24786         
24787         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24788             if(!e.dom.checked){
24789                 return;
24790             }
24791             
24792             valid = true;
24793             
24794             return false;
24795         });
24796         
24797         return valid;
24798     },
24799     
24800     validateCheckbox : function()
24801     {
24802         if(!this.groupId){
24803             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24804             //return (this.getValue() == this.inputValue) ? true : false;
24805         }
24806         
24807         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24808         
24809         if(!group){
24810             return false;
24811         }
24812         
24813         var r = false;
24814         
24815         for(var i in group){
24816             if(group[i].el.isVisible(true)){
24817                 r = false;
24818                 break;
24819             }
24820             
24821             r = true;
24822         }
24823         
24824         for(var i in group){
24825             if(r){
24826                 break;
24827             }
24828             
24829             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24830         }
24831         
24832         return r;
24833     },
24834     
24835     /**
24836      * Mark this field as valid
24837      */
24838     markValid : function()
24839     {
24840         var _this = this;
24841         
24842         this.fireEvent('valid', this);
24843         
24844         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24845         
24846         if(this.groupId){
24847             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24848         }
24849         
24850         if(label){
24851             label.markValid();
24852         }
24853
24854         if(this.inputType == 'radio'){
24855             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24856                 var fg = e.findParent('.form-group', false, true);
24857                 if (Roo.bootstrap.version == 3) {
24858                     fg.removeClass([_this.invalidClass, _this.validClass]);
24859                     fg.addClass(_this.validClass);
24860                 } else {
24861                     fg.removeClass(['is-valid', 'is-invalid']);
24862                     fg.addClass('is-valid');
24863                 }
24864             });
24865             
24866             return;
24867         }
24868
24869         if(!this.groupId){
24870             var fg = this.el.findParent('.form-group', false, true);
24871             if (Roo.bootstrap.version == 3) {
24872                 fg.removeClass([this.invalidClass, this.validClass]);
24873                 fg.addClass(this.validClass);
24874             } else {
24875                 fg.removeClass(['is-valid', 'is-invalid']);
24876                 fg.addClass('is-valid');
24877             }
24878             return;
24879         }
24880         
24881         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24882         
24883         if(!group){
24884             return;
24885         }
24886         
24887         for(var i in group){
24888             var fg = group[i].el.findParent('.form-group', false, true);
24889             if (Roo.bootstrap.version == 3) {
24890                 fg.removeClass([this.invalidClass, this.validClass]);
24891                 fg.addClass(this.validClass);
24892             } else {
24893                 fg.removeClass(['is-valid', 'is-invalid']);
24894                 fg.addClass('is-valid');
24895             }
24896         }
24897     },
24898     
24899      /**
24900      * Mark this field as invalid
24901      * @param {String} msg The validation message
24902      */
24903     markInvalid : function(msg)
24904     {
24905         if(this.allowBlank){
24906             return;
24907         }
24908         
24909         var _this = this;
24910         
24911         this.fireEvent('invalid', this, msg);
24912         
24913         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24914         
24915         if(this.groupId){
24916             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24917         }
24918         
24919         if(label){
24920             label.markInvalid();
24921         }
24922             
24923         if(this.inputType == 'radio'){
24924             
24925             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24926                 var fg = e.findParent('.form-group', false, true);
24927                 if (Roo.bootstrap.version == 3) {
24928                     fg.removeClass([_this.invalidClass, _this.validClass]);
24929                     fg.addClass(_this.invalidClass);
24930                 } else {
24931                     fg.removeClass(['is-invalid', 'is-valid']);
24932                     fg.addClass('is-invalid');
24933                 }
24934             });
24935             
24936             return;
24937         }
24938         
24939         if(!this.groupId){
24940             var fg = this.el.findParent('.form-group', false, true);
24941             if (Roo.bootstrap.version == 3) {
24942                 fg.removeClass([_this.invalidClass, _this.validClass]);
24943                 fg.addClass(_this.invalidClass);
24944             } else {
24945                 fg.removeClass(['is-invalid', 'is-valid']);
24946                 fg.addClass('is-invalid');
24947             }
24948             return;
24949         }
24950         
24951         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24952         
24953         if(!group){
24954             return;
24955         }
24956         
24957         for(var i in group){
24958             var fg = group[i].el.findParent('.form-group', false, true);
24959             if (Roo.bootstrap.version == 3) {
24960                 fg.removeClass([_this.invalidClass, _this.validClass]);
24961                 fg.addClass(_this.invalidClass);
24962             } else {
24963                 fg.removeClass(['is-invalid', 'is-valid']);
24964                 fg.addClass('is-invalid');
24965             }
24966         }
24967         
24968     },
24969     
24970     clearInvalid : function()
24971     {
24972         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24973         
24974         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24975         
24976         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24977         
24978         if (label && label.iconEl) {
24979             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24980             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24981         }
24982     },
24983     
24984     disable : function()
24985     {
24986         if(this.inputType != 'radio'){
24987             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24988             return;
24989         }
24990         
24991         var _this = this;
24992         
24993         if(this.rendered){
24994             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24995                 _this.getActionEl().addClass(this.disabledClass);
24996                 e.dom.disabled = true;
24997             });
24998         }
24999         
25000         this.disabled = true;
25001         this.fireEvent("disable", this);
25002         return this;
25003     },
25004
25005     enable : function()
25006     {
25007         if(this.inputType != 'radio'){
25008             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25009             return;
25010         }
25011         
25012         var _this = this;
25013         
25014         if(this.rendered){
25015             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25016                 _this.getActionEl().removeClass(this.disabledClass);
25017                 e.dom.disabled = false;
25018             });
25019         }
25020         
25021         this.disabled = false;
25022         this.fireEvent("enable", this);
25023         return this;
25024     },
25025     
25026     setBoxLabel : function(v)
25027     {
25028         this.boxLabel = v;
25029         
25030         if(this.rendered){
25031             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25032         }
25033     }
25034
25035 });
25036
25037 Roo.apply(Roo.bootstrap.CheckBox, {
25038     
25039     groups: {},
25040     
25041      /**
25042     * register a CheckBox Group
25043     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25044     */
25045     register : function(checkbox)
25046     {
25047         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25048             this.groups[checkbox.groupId] = {};
25049         }
25050         
25051         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25052             return;
25053         }
25054         
25055         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25056         
25057     },
25058     /**
25059     * fetch a CheckBox Group based on the group ID
25060     * @param {string} the group ID
25061     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25062     */
25063     get: function(groupId) {
25064         if (typeof(this.groups[groupId]) == 'undefined') {
25065             return false;
25066         }
25067         
25068         return this.groups[groupId] ;
25069     }
25070     
25071     
25072 });
25073 /*
25074  * - LGPL
25075  *
25076  * RadioItem
25077  * 
25078  */
25079
25080 /**
25081  * @class Roo.bootstrap.Radio
25082  * @extends Roo.bootstrap.Component
25083  * Bootstrap Radio class
25084  * @cfg {String} boxLabel - the label associated
25085  * @cfg {String} value - the value of radio
25086  * 
25087  * @constructor
25088  * Create a new Radio
25089  * @param {Object} config The config object
25090  */
25091 Roo.bootstrap.Radio = function(config){
25092     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25093     
25094 };
25095
25096 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25097     
25098     boxLabel : '',
25099     
25100     value : '',
25101     
25102     getAutoCreate : function()
25103     {
25104         var cfg = {
25105             tag : 'div',
25106             cls : 'form-group radio',
25107             cn : [
25108                 {
25109                     tag : 'label',
25110                     cls : 'box-label',
25111                     html : this.boxLabel
25112                 }
25113             ]
25114         };
25115         
25116         return cfg;
25117     },
25118     
25119     initEvents : function() 
25120     {
25121         this.parent().register(this);
25122         
25123         this.el.on('click', this.onClick, this);
25124         
25125     },
25126     
25127     onClick : function(e)
25128     {
25129         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25130             this.setChecked(true);
25131         }
25132     },
25133     
25134     setChecked : function(state, suppressEvent)
25135     {
25136         this.parent().setValue(this.value, suppressEvent);
25137         
25138     },
25139     
25140     setBoxLabel : function(v)
25141     {
25142         this.boxLabel = v;
25143         
25144         if(this.rendered){
25145             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25146         }
25147     }
25148     
25149 });
25150  
25151
25152  /*
25153  * - LGPL
25154  *
25155  * Input
25156  * 
25157  */
25158
25159 /**
25160  * @class Roo.bootstrap.SecurePass
25161  * @extends Roo.bootstrap.Input
25162  * Bootstrap SecurePass class
25163  *
25164  * 
25165  * @constructor
25166  * Create a new SecurePass
25167  * @param {Object} config The config object
25168  */
25169  
25170 Roo.bootstrap.SecurePass = function (config) {
25171     // these go here, so the translation tool can replace them..
25172     this.errors = {
25173         PwdEmpty: "Please type a password, and then retype it to confirm.",
25174         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25175         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25176         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25177         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25178         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25179         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25180         TooWeak: "Your password is Too Weak."
25181     },
25182     this.meterLabel = "Password strength:";
25183     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25184     this.meterClass = [
25185         "roo-password-meter-tooweak", 
25186         "roo-password-meter-weak", 
25187         "roo-password-meter-medium", 
25188         "roo-password-meter-strong", 
25189         "roo-password-meter-grey"
25190     ];
25191     
25192     this.errors = {};
25193     
25194     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25195 }
25196
25197 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25198     /**
25199      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25200      * {
25201      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25202      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25203      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25204      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25205      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25206      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25207      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25208      * })
25209      */
25210     // private
25211     
25212     meterWidth: 300,
25213     errorMsg :'',    
25214     errors: false,
25215     imageRoot: '/',
25216     /**
25217      * @cfg {String/Object} Label for the strength meter (defaults to
25218      * 'Password strength:')
25219      */
25220     // private
25221     meterLabel: '',
25222     /**
25223      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25224      * ['Weak', 'Medium', 'Strong'])
25225      */
25226     // private    
25227     pwdStrengths: false,    
25228     // private
25229     strength: 0,
25230     // private
25231     _lastPwd: null,
25232     // private
25233     kCapitalLetter: 0,
25234     kSmallLetter: 1,
25235     kDigit: 2,
25236     kPunctuation: 3,
25237     
25238     insecure: false,
25239     // private
25240     initEvents: function ()
25241     {
25242         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25243
25244         if (this.el.is('input[type=password]') && Roo.isSafari) {
25245             this.el.on('keydown', this.SafariOnKeyDown, this);
25246         }
25247
25248         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25249     },
25250     // private
25251     onRender: function (ct, position)
25252     {
25253         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25254         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25255         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25256
25257         this.trigger.createChild({
25258                    cn: [
25259                     {
25260                     //id: 'PwdMeter',
25261                     tag: 'div',
25262                     cls: 'roo-password-meter-grey col-xs-12',
25263                     style: {
25264                         //width: 0,
25265                         //width: this.meterWidth + 'px'                                                
25266                         }
25267                     },
25268                     {                            
25269                          cls: 'roo-password-meter-text'                          
25270                     }
25271                 ]            
25272         });
25273
25274          
25275         if (this.hideTrigger) {
25276             this.trigger.setDisplayed(false);
25277         }
25278         this.setSize(this.width || '', this.height || '');
25279     },
25280     // private
25281     onDestroy: function ()
25282     {
25283         if (this.trigger) {
25284             this.trigger.removeAllListeners();
25285             this.trigger.remove();
25286         }
25287         if (this.wrap) {
25288             this.wrap.remove();
25289         }
25290         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25291     },
25292     // private
25293     checkStrength: function ()
25294     {
25295         var pwd = this.inputEl().getValue();
25296         if (pwd == this._lastPwd) {
25297             return;
25298         }
25299
25300         var strength;
25301         if (this.ClientSideStrongPassword(pwd)) {
25302             strength = 3;
25303         } else if (this.ClientSideMediumPassword(pwd)) {
25304             strength = 2;
25305         } else if (this.ClientSideWeakPassword(pwd)) {
25306             strength = 1;
25307         } else {
25308             strength = 0;
25309         }
25310         
25311         Roo.log('strength1: ' + strength);
25312         
25313         //var pm = this.trigger.child('div/div/div').dom;
25314         var pm = this.trigger.child('div/div');
25315         pm.removeClass(this.meterClass);
25316         pm.addClass(this.meterClass[strength]);
25317                 
25318         
25319         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25320                 
25321         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25322         
25323         this._lastPwd = pwd;
25324     },
25325     reset: function ()
25326     {
25327         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25328         
25329         this._lastPwd = '';
25330         
25331         var pm = this.trigger.child('div/div');
25332         pm.removeClass(this.meterClass);
25333         pm.addClass('roo-password-meter-grey');        
25334         
25335         
25336         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25337         
25338         pt.innerHTML = '';
25339         this.inputEl().dom.type='password';
25340     },
25341     // private
25342     validateValue: function (value)
25343     {
25344         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25345             return false;
25346         }
25347         if (value.length == 0) {
25348             if (this.allowBlank) {
25349                 this.clearInvalid();
25350                 return true;
25351             }
25352
25353             this.markInvalid(this.errors.PwdEmpty);
25354             this.errorMsg = this.errors.PwdEmpty;
25355             return false;
25356         }
25357         
25358         if(this.insecure){
25359             return true;
25360         }
25361         
25362         if (!value.match(/[\x21-\x7e]+/)) {
25363             this.markInvalid(this.errors.PwdBadChar);
25364             this.errorMsg = this.errors.PwdBadChar;
25365             return false;
25366         }
25367         if (value.length < 6) {
25368             this.markInvalid(this.errors.PwdShort);
25369             this.errorMsg = this.errors.PwdShort;
25370             return false;
25371         }
25372         if (value.length > 16) {
25373             this.markInvalid(this.errors.PwdLong);
25374             this.errorMsg = this.errors.PwdLong;
25375             return false;
25376         }
25377         var strength;
25378         if (this.ClientSideStrongPassword(value)) {
25379             strength = 3;
25380         } else if (this.ClientSideMediumPassword(value)) {
25381             strength = 2;
25382         } else if (this.ClientSideWeakPassword(value)) {
25383             strength = 1;
25384         } else {
25385             strength = 0;
25386         }
25387
25388         
25389         if (strength < 2) {
25390             //this.markInvalid(this.errors.TooWeak);
25391             this.errorMsg = this.errors.TooWeak;
25392             //return false;
25393         }
25394         
25395         
25396         console.log('strength2: ' + strength);
25397         
25398         //var pm = this.trigger.child('div/div/div').dom;
25399         
25400         var pm = this.trigger.child('div/div');
25401         pm.removeClass(this.meterClass);
25402         pm.addClass(this.meterClass[strength]);
25403                 
25404         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25405                 
25406         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25407         
25408         this.errorMsg = ''; 
25409         return true;
25410     },
25411     // private
25412     CharacterSetChecks: function (type)
25413     {
25414         this.type = type;
25415         this.fResult = false;
25416     },
25417     // private
25418     isctype: function (character, type)
25419     {
25420         switch (type) {  
25421             case this.kCapitalLetter:
25422                 if (character >= 'A' && character <= 'Z') {
25423                     return true;
25424                 }
25425                 break;
25426             
25427             case this.kSmallLetter:
25428                 if (character >= 'a' && character <= 'z') {
25429                     return true;
25430                 }
25431                 break;
25432             
25433             case this.kDigit:
25434                 if (character >= '0' && character <= '9') {
25435                     return true;
25436                 }
25437                 break;
25438             
25439             case this.kPunctuation:
25440                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25441                     return true;
25442                 }
25443                 break;
25444             
25445             default:
25446                 return false;
25447         }
25448
25449     },
25450     // private
25451     IsLongEnough: function (pwd, size)
25452     {
25453         return !(pwd == null || isNaN(size) || pwd.length < size);
25454     },
25455     // private
25456     SpansEnoughCharacterSets: function (word, nb)
25457     {
25458         if (!this.IsLongEnough(word, nb))
25459         {
25460             return false;
25461         }
25462
25463         var characterSetChecks = new Array(
25464             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25465             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25466         );
25467         
25468         for (var index = 0; index < word.length; ++index) {
25469             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25470                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25471                     characterSetChecks[nCharSet].fResult = true;
25472                     break;
25473                 }
25474             }
25475         }
25476
25477         var nCharSets = 0;
25478         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25479             if (characterSetChecks[nCharSet].fResult) {
25480                 ++nCharSets;
25481             }
25482         }
25483
25484         if (nCharSets < nb) {
25485             return false;
25486         }
25487         return true;
25488     },
25489     // private
25490     ClientSideStrongPassword: function (pwd)
25491     {
25492         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25493     },
25494     // private
25495     ClientSideMediumPassword: function (pwd)
25496     {
25497         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25498     },
25499     // private
25500     ClientSideWeakPassword: function (pwd)
25501     {
25502         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25503     }
25504           
25505 })//<script type="text/javascript">
25506
25507 /*
25508  * Based  Ext JS Library 1.1.1
25509  * Copyright(c) 2006-2007, Ext JS, LLC.
25510  * LGPL
25511  *
25512  */
25513  
25514 /**
25515  * @class Roo.HtmlEditorCore
25516  * @extends Roo.Component
25517  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25518  *
25519  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25520  */
25521
25522 Roo.HtmlEditorCore = function(config){
25523     
25524     
25525     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25526     
25527     
25528     this.addEvents({
25529         /**
25530          * @event initialize
25531          * Fires when the editor is fully initialized (including the iframe)
25532          * @param {Roo.HtmlEditorCore} this
25533          */
25534         initialize: true,
25535         /**
25536          * @event activate
25537          * Fires when the editor is first receives the focus. Any insertion must wait
25538          * until after this event.
25539          * @param {Roo.HtmlEditorCore} this
25540          */
25541         activate: true,
25542          /**
25543          * @event beforesync
25544          * Fires before the textarea is updated with content from the editor iframe. Return false
25545          * to cancel the sync.
25546          * @param {Roo.HtmlEditorCore} this
25547          * @param {String} html
25548          */
25549         beforesync: true,
25550          /**
25551          * @event beforepush
25552          * Fires before the iframe editor is updated with content from the textarea. Return false
25553          * to cancel the push.
25554          * @param {Roo.HtmlEditorCore} this
25555          * @param {String} html
25556          */
25557         beforepush: true,
25558          /**
25559          * @event sync
25560          * Fires when the textarea is updated with content from the editor iframe.
25561          * @param {Roo.HtmlEditorCore} this
25562          * @param {String} html
25563          */
25564         sync: true,
25565          /**
25566          * @event push
25567          * Fires when the iframe editor is updated with content from the textarea.
25568          * @param {Roo.HtmlEditorCore} this
25569          * @param {String} html
25570          */
25571         push: true,
25572         
25573         /**
25574          * @event editorevent
25575          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25576          * @param {Roo.HtmlEditorCore} this
25577          */
25578         editorevent: true
25579         
25580     });
25581     
25582     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25583     
25584     // defaults : white / black...
25585     this.applyBlacklists();
25586     
25587     
25588     
25589 };
25590
25591
25592 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25593
25594
25595      /**
25596      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25597      */
25598     
25599     owner : false,
25600     
25601      /**
25602      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25603      *                        Roo.resizable.
25604      */
25605     resizable : false,
25606      /**
25607      * @cfg {Number} height (in pixels)
25608      */   
25609     height: 300,
25610    /**
25611      * @cfg {Number} width (in pixels)
25612      */   
25613     width: 500,
25614     
25615     /**
25616      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25617      * 
25618      */
25619     stylesheets: false,
25620     
25621     /**
25622      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25623      */
25624     allowComments: false,
25625     // id of frame..
25626     frameId: false,
25627     
25628     // private properties
25629     validationEvent : false,
25630     deferHeight: true,
25631     initialized : false,
25632     activated : false,
25633     sourceEditMode : false,
25634     onFocus : Roo.emptyFn,
25635     iframePad:3,
25636     hideMode:'offsets',
25637     
25638     clearUp: true,
25639     
25640     // blacklist + whitelisted elements..
25641     black: false,
25642     white: false,
25643      
25644     bodyCls : '',
25645
25646     /**
25647      * Protected method that will not generally be called directly. It
25648      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25649      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25650      */
25651     getDocMarkup : function(){
25652         // body styles..
25653         var st = '';
25654         
25655         // inherit styels from page...?? 
25656         if (this.stylesheets === false) {
25657             
25658             Roo.get(document.head).select('style').each(function(node) {
25659                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25660             });
25661             
25662             Roo.get(document.head).select('link').each(function(node) { 
25663                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25664             });
25665             
25666         } else if (!this.stylesheets.length) {
25667                 // simple..
25668                 st = '<style type="text/css">' +
25669                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25670                    '</style>';
25671         } else {
25672             for (var i in this.stylesheets) { 
25673                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25674             }
25675             
25676         }
25677         
25678         st +=  '<style type="text/css">' +
25679             'IMG { cursor: pointer } ' +
25680         '</style>';
25681
25682         var cls = 'roo-htmleditor-body';
25683         
25684         if(this.bodyCls.length){
25685             cls += ' ' + this.bodyCls;
25686         }
25687         
25688         return '<html><head>' + st  +
25689             //<style type="text/css">' +
25690             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25691             //'</style>' +
25692             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25693     },
25694
25695     // private
25696     onRender : function(ct, position)
25697     {
25698         var _t = this;
25699         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25700         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25701         
25702         
25703         this.el.dom.style.border = '0 none';
25704         this.el.dom.setAttribute('tabIndex', -1);
25705         this.el.addClass('x-hidden hide');
25706         
25707         
25708         
25709         if(Roo.isIE){ // fix IE 1px bogus margin
25710             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25711         }
25712        
25713         
25714         this.frameId = Roo.id();
25715         
25716          
25717         
25718         var iframe = this.owner.wrap.createChild({
25719             tag: 'iframe',
25720             cls: 'form-control', // bootstrap..
25721             id: this.frameId,
25722             name: this.frameId,
25723             frameBorder : 'no',
25724             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25725         }, this.el
25726         );
25727         
25728         
25729         this.iframe = iframe.dom;
25730
25731          this.assignDocWin();
25732         
25733         this.doc.designMode = 'on';
25734        
25735         this.doc.open();
25736         this.doc.write(this.getDocMarkup());
25737         this.doc.close();
25738
25739         
25740         var task = { // must defer to wait for browser to be ready
25741             run : function(){
25742                 //console.log("run task?" + this.doc.readyState);
25743                 this.assignDocWin();
25744                 if(this.doc.body || this.doc.readyState == 'complete'){
25745                     try {
25746                         this.doc.designMode="on";
25747                     } catch (e) {
25748                         return;
25749                     }
25750                     Roo.TaskMgr.stop(task);
25751                     this.initEditor.defer(10, this);
25752                 }
25753             },
25754             interval : 10,
25755             duration: 10000,
25756             scope: this
25757         };
25758         Roo.TaskMgr.start(task);
25759
25760     },
25761
25762     // private
25763     onResize : function(w, h)
25764     {
25765          Roo.log('resize: ' +w + ',' + h );
25766         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25767         if(!this.iframe){
25768             return;
25769         }
25770         if(typeof w == 'number'){
25771             
25772             this.iframe.style.width = w + 'px';
25773         }
25774         if(typeof h == 'number'){
25775             
25776             this.iframe.style.height = h + 'px';
25777             if(this.doc){
25778                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25779             }
25780         }
25781         
25782     },
25783
25784     /**
25785      * Toggles the editor between standard and source edit mode.
25786      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25787      */
25788     toggleSourceEdit : function(sourceEditMode){
25789         
25790         this.sourceEditMode = sourceEditMode === true;
25791         
25792         if(this.sourceEditMode){
25793  
25794             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25795             
25796         }else{
25797             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25798             //this.iframe.className = '';
25799             this.deferFocus();
25800         }
25801         //this.setSize(this.owner.wrap.getSize());
25802         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25803     },
25804
25805     
25806   
25807
25808     /**
25809      * Protected method that will not generally be called directly. If you need/want
25810      * custom HTML cleanup, this is the method you should override.
25811      * @param {String} html The HTML to be cleaned
25812      * return {String} The cleaned HTML
25813      */
25814     cleanHtml : function(html){
25815         html = String(html);
25816         if(html.length > 5){
25817             if(Roo.isSafari){ // strip safari nonsense
25818                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25819             }
25820         }
25821         if(html == '&nbsp;'){
25822             html = '';
25823         }
25824         return html;
25825     },
25826
25827     /**
25828      * HTML Editor -> Textarea
25829      * Protected method that will not generally be called directly. Syncs the contents
25830      * of the editor iframe with the textarea.
25831      */
25832     syncValue : function(){
25833         if(this.initialized){
25834             var bd = (this.doc.body || this.doc.documentElement);
25835             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25836             var html = bd.innerHTML;
25837             if(Roo.isSafari){
25838                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25839                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25840                 if(m && m[1]){
25841                     html = '<div style="'+m[0]+'">' + html + '</div>';
25842                 }
25843             }
25844             html = this.cleanHtml(html);
25845             // fix up the special chars.. normaly like back quotes in word...
25846             // however we do not want to do this with chinese..
25847             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25848                 
25849                 var cc = match.charCodeAt();
25850
25851                 // Get the character value, handling surrogate pairs
25852                 if (match.length == 2) {
25853                     // It's a surrogate pair, calculate the Unicode code point
25854                     var high = match.charCodeAt(0) - 0xD800;
25855                     var low  = match.charCodeAt(1) - 0xDC00;
25856                     cc = (high * 0x400) + low + 0x10000;
25857                 }  else if (
25858                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25859                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25860                     (cc >= 0xf900 && cc < 0xfb00 )
25861                 ) {
25862                         return match;
25863                 }  
25864          
25865                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25866                 return "&#" + cc + ";";
25867                 
25868                 
25869             });
25870             
25871             
25872              
25873             if(this.owner.fireEvent('beforesync', this, html) !== false){
25874                 this.el.dom.value = html;
25875                 this.owner.fireEvent('sync', this, html);
25876             }
25877         }
25878     },
25879
25880     /**
25881      * Protected method that will not generally be called directly. Pushes the value of the textarea
25882      * into the iframe editor.
25883      */
25884     pushValue : function(){
25885         if(this.initialized){
25886             var v = this.el.dom.value.trim();
25887             
25888 //            if(v.length < 1){
25889 //                v = '&#160;';
25890 //            }
25891             
25892             if(this.owner.fireEvent('beforepush', this, v) !== false){
25893                 var d = (this.doc.body || this.doc.documentElement);
25894                 d.innerHTML = v;
25895                 this.cleanUpPaste();
25896                 this.el.dom.value = d.innerHTML;
25897                 this.owner.fireEvent('push', this, v);
25898             }
25899         }
25900     },
25901
25902     // private
25903     deferFocus : function(){
25904         this.focus.defer(10, this);
25905     },
25906
25907     // doc'ed in Field
25908     focus : function(){
25909         if(this.win && !this.sourceEditMode){
25910             this.win.focus();
25911         }else{
25912             this.el.focus();
25913         }
25914     },
25915     
25916     assignDocWin: function()
25917     {
25918         var iframe = this.iframe;
25919         
25920          if(Roo.isIE){
25921             this.doc = iframe.contentWindow.document;
25922             this.win = iframe.contentWindow;
25923         } else {
25924 //            if (!Roo.get(this.frameId)) {
25925 //                return;
25926 //            }
25927 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25928 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25929             
25930             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25931                 return;
25932             }
25933             
25934             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25935             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25936         }
25937     },
25938     
25939     // private
25940     initEditor : function(){
25941         //console.log("INIT EDITOR");
25942         this.assignDocWin();
25943         
25944         
25945         
25946         this.doc.designMode="on";
25947         this.doc.open();
25948         this.doc.write(this.getDocMarkup());
25949         this.doc.close();
25950         
25951         var dbody = (this.doc.body || this.doc.documentElement);
25952         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25953         // this copies styles from the containing element into thsi one..
25954         // not sure why we need all of this..
25955         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25956         
25957         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25958         //ss['background-attachment'] = 'fixed'; // w3c
25959         dbody.bgProperties = 'fixed'; // ie
25960         //Roo.DomHelper.applyStyles(dbody, ss);
25961         Roo.EventManager.on(this.doc, {
25962             //'mousedown': this.onEditorEvent,
25963             'mouseup': this.onEditorEvent,
25964             'dblclick': this.onEditorEvent,
25965             'click': this.onEditorEvent,
25966             'keyup': this.onEditorEvent,
25967             buffer:100,
25968             scope: this
25969         });
25970         if(Roo.isGecko){
25971             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25972         }
25973         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25974             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25975         }
25976         this.initialized = true;
25977
25978         this.owner.fireEvent('initialize', this);
25979         this.pushValue();
25980     },
25981
25982     // private
25983     onDestroy : function(){
25984         
25985         
25986         
25987         if(this.rendered){
25988             
25989             //for (var i =0; i < this.toolbars.length;i++) {
25990             //    // fixme - ask toolbars for heights?
25991             //    this.toolbars[i].onDestroy();
25992            // }
25993             
25994             //this.wrap.dom.innerHTML = '';
25995             //this.wrap.remove();
25996         }
25997     },
25998
25999     // private
26000     onFirstFocus : function(){
26001         
26002         this.assignDocWin();
26003         
26004         
26005         this.activated = true;
26006          
26007     
26008         if(Roo.isGecko){ // prevent silly gecko errors
26009             this.win.focus();
26010             var s = this.win.getSelection();
26011             if(!s.focusNode || s.focusNode.nodeType != 3){
26012                 var r = s.getRangeAt(0);
26013                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26014                 r.collapse(true);
26015                 this.deferFocus();
26016             }
26017             try{
26018                 this.execCmd('useCSS', true);
26019                 this.execCmd('styleWithCSS', false);
26020             }catch(e){}
26021         }
26022         this.owner.fireEvent('activate', this);
26023     },
26024
26025     // private
26026     adjustFont: function(btn){
26027         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26028         //if(Roo.isSafari){ // safari
26029         //    adjust *= 2;
26030        // }
26031         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26032         if(Roo.isSafari){ // safari
26033             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26034             v =  (v < 10) ? 10 : v;
26035             v =  (v > 48) ? 48 : v;
26036             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26037             
26038         }
26039         
26040         
26041         v = Math.max(1, v+adjust);
26042         
26043         this.execCmd('FontSize', v  );
26044     },
26045
26046     onEditorEvent : function(e)
26047     {
26048         this.owner.fireEvent('editorevent', this, e);
26049       //  this.updateToolbar();
26050         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26051     },
26052
26053     insertTag : function(tg)
26054     {
26055         // could be a bit smarter... -> wrap the current selected tRoo..
26056         if (tg.toLowerCase() == 'span' ||
26057             tg.toLowerCase() == 'code' ||
26058             tg.toLowerCase() == 'sup' ||
26059             tg.toLowerCase() == 'sub' 
26060             ) {
26061             
26062             range = this.createRange(this.getSelection());
26063             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26064             wrappingNode.appendChild(range.extractContents());
26065             range.insertNode(wrappingNode);
26066
26067             return;
26068             
26069             
26070             
26071         }
26072         this.execCmd("formatblock",   tg);
26073         
26074     },
26075     
26076     insertText : function(txt)
26077     {
26078         
26079         
26080         var range = this.createRange();
26081         range.deleteContents();
26082                //alert(Sender.getAttribute('label'));
26083                
26084         range.insertNode(this.doc.createTextNode(txt));
26085     } ,
26086     
26087      
26088
26089     /**
26090      * Executes a Midas editor command on the editor document and performs necessary focus and
26091      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26092      * @param {String} cmd The Midas command
26093      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26094      */
26095     relayCmd : function(cmd, value){
26096         this.win.focus();
26097         this.execCmd(cmd, value);
26098         this.owner.fireEvent('editorevent', this);
26099         //this.updateToolbar();
26100         this.owner.deferFocus();
26101     },
26102
26103     /**
26104      * Executes a Midas editor command directly on the editor document.
26105      * For visual commands, you should use {@link #relayCmd} instead.
26106      * <b>This should only be called after the editor is initialized.</b>
26107      * @param {String} cmd The Midas command
26108      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26109      */
26110     execCmd : function(cmd, value){
26111         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26112         this.syncValue();
26113     },
26114  
26115  
26116    
26117     /**
26118      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26119      * to insert tRoo.
26120      * @param {String} text | dom node.. 
26121      */
26122     insertAtCursor : function(text)
26123     {
26124         
26125         if(!this.activated){
26126             return;
26127         }
26128         /*
26129         if(Roo.isIE){
26130             this.win.focus();
26131             var r = this.doc.selection.createRange();
26132             if(r){
26133                 r.collapse(true);
26134                 r.pasteHTML(text);
26135                 this.syncValue();
26136                 this.deferFocus();
26137             
26138             }
26139             return;
26140         }
26141         */
26142         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26143             this.win.focus();
26144             
26145             
26146             // from jquery ui (MIT licenced)
26147             var range, node;
26148             var win = this.win;
26149             
26150             if (win.getSelection && win.getSelection().getRangeAt) {
26151                 range = win.getSelection().getRangeAt(0);
26152                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26153                 range.insertNode(node);
26154             } else if (win.document.selection && win.document.selection.createRange) {
26155                 // no firefox support
26156                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26157                 win.document.selection.createRange().pasteHTML(txt);
26158             } else {
26159                 // no firefox support
26160                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26161                 this.execCmd('InsertHTML', txt);
26162             } 
26163             
26164             this.syncValue();
26165             
26166             this.deferFocus();
26167         }
26168     },
26169  // private
26170     mozKeyPress : function(e){
26171         if(e.ctrlKey){
26172             var c = e.getCharCode(), cmd;
26173           
26174             if(c > 0){
26175                 c = String.fromCharCode(c).toLowerCase();
26176                 switch(c){
26177                     case 'b':
26178                         cmd = 'bold';
26179                         break;
26180                     case 'i':
26181                         cmd = 'italic';
26182                         break;
26183                     
26184                     case 'u':
26185                         cmd = 'underline';
26186                         break;
26187                     
26188                     case 'v':
26189                         this.cleanUpPaste.defer(100, this);
26190                         return;
26191                         
26192                 }
26193                 if(cmd){
26194                     this.win.focus();
26195                     this.execCmd(cmd);
26196                     this.deferFocus();
26197                     e.preventDefault();
26198                 }
26199                 
26200             }
26201         }
26202     },
26203
26204     // private
26205     fixKeys : function(){ // load time branching for fastest keydown performance
26206         if(Roo.isIE){
26207             return function(e){
26208                 var k = e.getKey(), r;
26209                 if(k == e.TAB){
26210                     e.stopEvent();
26211                     r = this.doc.selection.createRange();
26212                     if(r){
26213                         r.collapse(true);
26214                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26215                         this.deferFocus();
26216                     }
26217                     return;
26218                 }
26219                 
26220                 if(k == e.ENTER){
26221                     r = this.doc.selection.createRange();
26222                     if(r){
26223                         var target = r.parentElement();
26224                         if(!target || target.tagName.toLowerCase() != 'li'){
26225                             e.stopEvent();
26226                             r.pasteHTML('<br />');
26227                             r.collapse(false);
26228                             r.select();
26229                         }
26230                     }
26231                 }
26232                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26233                     this.cleanUpPaste.defer(100, this);
26234                     return;
26235                 }
26236                 
26237                 
26238             };
26239         }else if(Roo.isOpera){
26240             return function(e){
26241                 var k = e.getKey();
26242                 if(k == e.TAB){
26243                     e.stopEvent();
26244                     this.win.focus();
26245                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26246                     this.deferFocus();
26247                 }
26248                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26249                     this.cleanUpPaste.defer(100, this);
26250                     return;
26251                 }
26252                 
26253             };
26254         }else if(Roo.isSafari){
26255             return function(e){
26256                 var k = e.getKey();
26257                 
26258                 if(k == e.TAB){
26259                     e.stopEvent();
26260                     this.execCmd('InsertText','\t');
26261                     this.deferFocus();
26262                     return;
26263                 }
26264                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26265                     this.cleanUpPaste.defer(100, this);
26266                     return;
26267                 }
26268                 
26269              };
26270         }
26271     }(),
26272     
26273     getAllAncestors: function()
26274     {
26275         var p = this.getSelectedNode();
26276         var a = [];
26277         if (!p) {
26278             a.push(p); // push blank onto stack..
26279             p = this.getParentElement();
26280         }
26281         
26282         
26283         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26284             a.push(p);
26285             p = p.parentNode;
26286         }
26287         a.push(this.doc.body);
26288         return a;
26289     },
26290     lastSel : false,
26291     lastSelNode : false,
26292     
26293     
26294     getSelection : function() 
26295     {
26296         this.assignDocWin();
26297         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26298     },
26299     
26300     getSelectedNode: function() 
26301     {
26302         // this may only work on Gecko!!!
26303         
26304         // should we cache this!!!!
26305         
26306         
26307         
26308          
26309         var range = this.createRange(this.getSelection()).cloneRange();
26310         
26311         if (Roo.isIE) {
26312             var parent = range.parentElement();
26313             while (true) {
26314                 var testRange = range.duplicate();
26315                 testRange.moveToElementText(parent);
26316                 if (testRange.inRange(range)) {
26317                     break;
26318                 }
26319                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26320                     break;
26321                 }
26322                 parent = parent.parentElement;
26323             }
26324             return parent;
26325         }
26326         
26327         // is ancestor a text element.
26328         var ac =  range.commonAncestorContainer;
26329         if (ac.nodeType == 3) {
26330             ac = ac.parentNode;
26331         }
26332         
26333         var ar = ac.childNodes;
26334          
26335         var nodes = [];
26336         var other_nodes = [];
26337         var has_other_nodes = false;
26338         for (var i=0;i<ar.length;i++) {
26339             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26340                 continue;
26341             }
26342             // fullly contained node.
26343             
26344             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26345                 nodes.push(ar[i]);
26346                 continue;
26347             }
26348             
26349             // probably selected..
26350             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26351                 other_nodes.push(ar[i]);
26352                 continue;
26353             }
26354             // outer..
26355             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26356                 continue;
26357             }
26358             
26359             
26360             has_other_nodes = true;
26361         }
26362         if (!nodes.length && other_nodes.length) {
26363             nodes= other_nodes;
26364         }
26365         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26366             return false;
26367         }
26368         
26369         return nodes[0];
26370     },
26371     createRange: function(sel)
26372     {
26373         // this has strange effects when using with 
26374         // top toolbar - not sure if it's a great idea.
26375         //this.editor.contentWindow.focus();
26376         if (typeof sel != "undefined") {
26377             try {
26378                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26379             } catch(e) {
26380                 return this.doc.createRange();
26381             }
26382         } else {
26383             return this.doc.createRange();
26384         }
26385     },
26386     getParentElement: function()
26387     {
26388         
26389         this.assignDocWin();
26390         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26391         
26392         var range = this.createRange(sel);
26393          
26394         try {
26395             var p = range.commonAncestorContainer;
26396             while (p.nodeType == 3) { // text node
26397                 p = p.parentNode;
26398             }
26399             return p;
26400         } catch (e) {
26401             return null;
26402         }
26403     
26404     },
26405     /***
26406      *
26407      * Range intersection.. the hard stuff...
26408      *  '-1' = before
26409      *  '0' = hits..
26410      *  '1' = after.
26411      *         [ -- selected range --- ]
26412      *   [fail]                        [fail]
26413      *
26414      *    basically..
26415      *      if end is before start or  hits it. fail.
26416      *      if start is after end or hits it fail.
26417      *
26418      *   if either hits (but other is outside. - then it's not 
26419      *   
26420      *    
26421      **/
26422     
26423     
26424     // @see http://www.thismuchiknow.co.uk/?p=64.
26425     rangeIntersectsNode : function(range, node)
26426     {
26427         var nodeRange = node.ownerDocument.createRange();
26428         try {
26429             nodeRange.selectNode(node);
26430         } catch (e) {
26431             nodeRange.selectNodeContents(node);
26432         }
26433     
26434         var rangeStartRange = range.cloneRange();
26435         rangeStartRange.collapse(true);
26436     
26437         var rangeEndRange = range.cloneRange();
26438         rangeEndRange.collapse(false);
26439     
26440         var nodeStartRange = nodeRange.cloneRange();
26441         nodeStartRange.collapse(true);
26442     
26443         var nodeEndRange = nodeRange.cloneRange();
26444         nodeEndRange.collapse(false);
26445     
26446         return rangeStartRange.compareBoundaryPoints(
26447                  Range.START_TO_START, nodeEndRange) == -1 &&
26448                rangeEndRange.compareBoundaryPoints(
26449                  Range.START_TO_START, nodeStartRange) == 1;
26450         
26451          
26452     },
26453     rangeCompareNode : function(range, node)
26454     {
26455         var nodeRange = node.ownerDocument.createRange();
26456         try {
26457             nodeRange.selectNode(node);
26458         } catch (e) {
26459             nodeRange.selectNodeContents(node);
26460         }
26461         
26462         
26463         range.collapse(true);
26464     
26465         nodeRange.collapse(true);
26466      
26467         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26468         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26469          
26470         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26471         
26472         var nodeIsBefore   =  ss == 1;
26473         var nodeIsAfter    = ee == -1;
26474         
26475         if (nodeIsBefore && nodeIsAfter) {
26476             return 0; // outer
26477         }
26478         if (!nodeIsBefore && nodeIsAfter) {
26479             return 1; //right trailed.
26480         }
26481         
26482         if (nodeIsBefore && !nodeIsAfter) {
26483             return 2;  // left trailed.
26484         }
26485         // fully contined.
26486         return 3;
26487     },
26488
26489     // private? - in a new class?
26490     cleanUpPaste :  function()
26491     {
26492         // cleans up the whole document..
26493         Roo.log('cleanuppaste');
26494         
26495         this.cleanUpChildren(this.doc.body);
26496         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26497         if (clean != this.doc.body.innerHTML) {
26498             this.doc.body.innerHTML = clean;
26499         }
26500         
26501     },
26502     
26503     cleanWordChars : function(input) {// change the chars to hex code
26504         var he = Roo.HtmlEditorCore;
26505         
26506         var output = input;
26507         Roo.each(he.swapCodes, function(sw) { 
26508             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26509             
26510             output = output.replace(swapper, sw[1]);
26511         });
26512         
26513         return output;
26514     },
26515     
26516     
26517     cleanUpChildren : function (n)
26518     {
26519         if (!n.childNodes.length) {
26520             return;
26521         }
26522         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26523            this.cleanUpChild(n.childNodes[i]);
26524         }
26525     },
26526     
26527     
26528         
26529     
26530     cleanUpChild : function (node)
26531     {
26532         var ed = this;
26533         //console.log(node);
26534         if (node.nodeName == "#text") {
26535             // clean up silly Windows -- stuff?
26536             return; 
26537         }
26538         if (node.nodeName == "#comment") {
26539             if (!this.allowComments) {
26540                 node.parentNode.removeChild(node);
26541             }
26542             // clean up silly Windows -- stuff?
26543             return; 
26544         }
26545         var lcname = node.tagName.toLowerCase();
26546         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26547         // whitelist of tags..
26548         
26549         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26550             // remove node.
26551             node.parentNode.removeChild(node);
26552             return;
26553             
26554         }
26555         
26556         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26557         
26558         // spans with no attributes - just remove them..
26559         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26560             remove_keep_children = true;
26561         }
26562         
26563         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26564         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26565         
26566         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26567         //    remove_keep_children = true;
26568         //}
26569         
26570         if (remove_keep_children) {
26571             this.cleanUpChildren(node);
26572             // inserts everything just before this node...
26573             while (node.childNodes.length) {
26574                 var cn = node.childNodes[0];
26575                 node.removeChild(cn);
26576                 node.parentNode.insertBefore(cn, node);
26577             }
26578             node.parentNode.removeChild(node);
26579             return;
26580         }
26581         
26582         if (!node.attributes || !node.attributes.length) {
26583             
26584           
26585             
26586             
26587             this.cleanUpChildren(node);
26588             return;
26589         }
26590         
26591         function cleanAttr(n,v)
26592         {
26593             
26594             if (v.match(/^\./) || v.match(/^\//)) {
26595                 return;
26596             }
26597             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26598                 return;
26599             }
26600             if (v.match(/^#/)) {
26601                 return;
26602             }
26603             if (v.match(/^\{/)) { // allow template editing.
26604                 return;
26605             }
26606 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26607             node.removeAttribute(n);
26608             
26609         }
26610         
26611         var cwhite = this.cwhite;
26612         var cblack = this.cblack;
26613             
26614         function cleanStyle(n,v)
26615         {
26616             if (v.match(/expression/)) { //XSS?? should we even bother..
26617                 node.removeAttribute(n);
26618                 return;
26619             }
26620             
26621             var parts = v.split(/;/);
26622             var clean = [];
26623             
26624             Roo.each(parts, function(p) {
26625                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26626                 if (!p.length) {
26627                     return true;
26628                 }
26629                 var l = p.split(':').shift().replace(/\s+/g,'');
26630                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26631                 
26632                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26633 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26634                     //node.removeAttribute(n);
26635                     return true;
26636                 }
26637                 //Roo.log()
26638                 // only allow 'c whitelisted system attributes'
26639                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26640 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26641                     //node.removeAttribute(n);
26642                     return true;
26643                 }
26644                 
26645                 
26646                  
26647                 
26648                 clean.push(p);
26649                 return true;
26650             });
26651             if (clean.length) { 
26652                 node.setAttribute(n, clean.join(';'));
26653             } else {
26654                 node.removeAttribute(n);
26655             }
26656             
26657         }
26658         
26659         
26660         for (var i = node.attributes.length-1; i > -1 ; i--) {
26661             var a = node.attributes[i];
26662             //console.log(a);
26663             
26664             if (a.name.toLowerCase().substr(0,2)=='on')  {
26665                 node.removeAttribute(a.name);
26666                 continue;
26667             }
26668             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26669                 node.removeAttribute(a.name);
26670                 continue;
26671             }
26672             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26673                 cleanAttr(a.name,a.value); // fixme..
26674                 continue;
26675             }
26676             if (a.name == 'style') {
26677                 cleanStyle(a.name,a.value);
26678                 continue;
26679             }
26680             /// clean up MS crap..
26681             // tecnically this should be a list of valid class'es..
26682             
26683             
26684             if (a.name == 'class') {
26685                 if (a.value.match(/^Mso/)) {
26686                     node.removeAttribute('class');
26687                 }
26688                 
26689                 if (a.value.match(/^body$/)) {
26690                     node.removeAttribute('class');
26691                 }
26692                 continue;
26693             }
26694             
26695             // style cleanup!?
26696             // class cleanup?
26697             
26698         }
26699         
26700         
26701         this.cleanUpChildren(node);
26702         
26703         
26704     },
26705     
26706     /**
26707      * Clean up MS wordisms...
26708      */
26709     cleanWord : function(node)
26710     {
26711         if (!node) {
26712             this.cleanWord(this.doc.body);
26713             return;
26714         }
26715         
26716         if(
26717                 node.nodeName == 'SPAN' &&
26718                 !node.hasAttributes() &&
26719                 node.childNodes.length == 1 &&
26720                 node.firstChild.nodeName == "#text"  
26721         ) {
26722             var textNode = node.firstChild;
26723             node.removeChild(textNode);
26724             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26725                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26726             }
26727             node.parentNode.insertBefore(textNode, node);
26728             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26729                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26730             }
26731             node.parentNode.removeChild(node);
26732         }
26733         
26734         if (node.nodeName == "#text") {
26735             // clean up silly Windows -- stuff?
26736             return; 
26737         }
26738         if (node.nodeName == "#comment") {
26739             node.parentNode.removeChild(node);
26740             // clean up silly Windows -- stuff?
26741             return; 
26742         }
26743         
26744         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26745             node.parentNode.removeChild(node);
26746             return;
26747         }
26748         //Roo.log(node.tagName);
26749         // remove - but keep children..
26750         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26751             //Roo.log('-- removed');
26752             while (node.childNodes.length) {
26753                 var cn = node.childNodes[0];
26754                 node.removeChild(cn);
26755                 node.parentNode.insertBefore(cn, node);
26756                 // move node to parent - and clean it..
26757                 this.cleanWord(cn);
26758             }
26759             node.parentNode.removeChild(node);
26760             /// no need to iterate chidlren = it's got none..
26761             //this.iterateChildren(node, this.cleanWord);
26762             return;
26763         }
26764         // clean styles
26765         if (node.className.length) {
26766             
26767             var cn = node.className.split(/\W+/);
26768             var cna = [];
26769             Roo.each(cn, function(cls) {
26770                 if (cls.match(/Mso[a-zA-Z]+/)) {
26771                     return;
26772                 }
26773                 cna.push(cls);
26774             });
26775             node.className = cna.length ? cna.join(' ') : '';
26776             if (!cna.length) {
26777                 node.removeAttribute("class");
26778             }
26779         }
26780         
26781         if (node.hasAttribute("lang")) {
26782             node.removeAttribute("lang");
26783         }
26784         
26785         if (node.hasAttribute("style")) {
26786             
26787             var styles = node.getAttribute("style").split(";");
26788             var nstyle = [];
26789             Roo.each(styles, function(s) {
26790                 if (!s.match(/:/)) {
26791                     return;
26792                 }
26793                 var kv = s.split(":");
26794                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26795                     return;
26796                 }
26797                 // what ever is left... we allow.
26798                 nstyle.push(s);
26799             });
26800             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26801             if (!nstyle.length) {
26802                 node.removeAttribute('style');
26803             }
26804         }
26805         this.iterateChildren(node, this.cleanWord);
26806         
26807         
26808         
26809     },
26810     /**
26811      * iterateChildren of a Node, calling fn each time, using this as the scole..
26812      * @param {DomNode} node node to iterate children of.
26813      * @param {Function} fn method of this class to call on each item.
26814      */
26815     iterateChildren : function(node, fn)
26816     {
26817         if (!node.childNodes.length) {
26818                 return;
26819         }
26820         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26821            fn.call(this, node.childNodes[i])
26822         }
26823     },
26824     
26825     
26826     /**
26827      * cleanTableWidths.
26828      *
26829      * Quite often pasting from word etc.. results in tables with column and widths.
26830      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26831      *
26832      */
26833     cleanTableWidths : function(node)
26834     {
26835          
26836          
26837         if (!node) {
26838             this.cleanTableWidths(this.doc.body);
26839             return;
26840         }
26841         
26842         // ignore list...
26843         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26844             return; 
26845         }
26846         Roo.log(node.tagName);
26847         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26848             this.iterateChildren(node, this.cleanTableWidths);
26849             return;
26850         }
26851         if (node.hasAttribute('width')) {
26852             node.removeAttribute('width');
26853         }
26854         
26855          
26856         if (node.hasAttribute("style")) {
26857             // pretty basic...
26858             
26859             var styles = node.getAttribute("style").split(";");
26860             var nstyle = [];
26861             Roo.each(styles, function(s) {
26862                 if (!s.match(/:/)) {
26863                     return;
26864                 }
26865                 var kv = s.split(":");
26866                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26867                     return;
26868                 }
26869                 // what ever is left... we allow.
26870                 nstyle.push(s);
26871             });
26872             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26873             if (!nstyle.length) {
26874                 node.removeAttribute('style');
26875             }
26876         }
26877         
26878         this.iterateChildren(node, this.cleanTableWidths);
26879         
26880         
26881     },
26882     
26883     
26884     
26885     
26886     domToHTML : function(currentElement, depth, nopadtext) {
26887         
26888         depth = depth || 0;
26889         nopadtext = nopadtext || false;
26890     
26891         if (!currentElement) {
26892             return this.domToHTML(this.doc.body);
26893         }
26894         
26895         //Roo.log(currentElement);
26896         var j;
26897         var allText = false;
26898         var nodeName = currentElement.nodeName;
26899         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26900         
26901         if  (nodeName == '#text') {
26902             
26903             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26904         }
26905         
26906         
26907         var ret = '';
26908         if (nodeName != 'BODY') {
26909              
26910             var i = 0;
26911             // Prints the node tagName, such as <A>, <IMG>, etc
26912             if (tagName) {
26913                 var attr = [];
26914                 for(i = 0; i < currentElement.attributes.length;i++) {
26915                     // quoting?
26916                     var aname = currentElement.attributes.item(i).name;
26917                     if (!currentElement.attributes.item(i).value.length) {
26918                         continue;
26919                     }
26920                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26921                 }
26922                 
26923                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26924             } 
26925             else {
26926                 
26927                 // eack
26928             }
26929         } else {
26930             tagName = false;
26931         }
26932         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26933             return ret;
26934         }
26935         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26936             nopadtext = true;
26937         }
26938         
26939         
26940         // Traverse the tree
26941         i = 0;
26942         var currentElementChild = currentElement.childNodes.item(i);
26943         var allText = true;
26944         var innerHTML  = '';
26945         lastnode = '';
26946         while (currentElementChild) {
26947             // Formatting code (indent the tree so it looks nice on the screen)
26948             var nopad = nopadtext;
26949             if (lastnode == 'SPAN') {
26950                 nopad  = true;
26951             }
26952             // text
26953             if  (currentElementChild.nodeName == '#text') {
26954                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26955                 toadd = nopadtext ? toadd : toadd.trim();
26956                 if (!nopad && toadd.length > 80) {
26957                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26958                 }
26959                 innerHTML  += toadd;
26960                 
26961                 i++;
26962                 currentElementChild = currentElement.childNodes.item(i);
26963                 lastNode = '';
26964                 continue;
26965             }
26966             allText = false;
26967             
26968             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26969                 
26970             // Recursively traverse the tree structure of the child node
26971             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26972             lastnode = currentElementChild.nodeName;
26973             i++;
26974             currentElementChild=currentElement.childNodes.item(i);
26975         }
26976         
26977         ret += innerHTML;
26978         
26979         if (!allText) {
26980                 // The remaining code is mostly for formatting the tree
26981             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26982         }
26983         
26984         
26985         if (tagName) {
26986             ret+= "</"+tagName+">";
26987         }
26988         return ret;
26989         
26990     },
26991         
26992     applyBlacklists : function()
26993     {
26994         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26995         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26996         
26997         this.white = [];
26998         this.black = [];
26999         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27000             if (b.indexOf(tag) > -1) {
27001                 return;
27002             }
27003             this.white.push(tag);
27004             
27005         }, this);
27006         
27007         Roo.each(w, function(tag) {
27008             if (b.indexOf(tag) > -1) {
27009                 return;
27010             }
27011             if (this.white.indexOf(tag) > -1) {
27012                 return;
27013             }
27014             this.white.push(tag);
27015             
27016         }, this);
27017         
27018         
27019         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27020             if (w.indexOf(tag) > -1) {
27021                 return;
27022             }
27023             this.black.push(tag);
27024             
27025         }, this);
27026         
27027         Roo.each(b, function(tag) {
27028             if (w.indexOf(tag) > -1) {
27029                 return;
27030             }
27031             if (this.black.indexOf(tag) > -1) {
27032                 return;
27033             }
27034             this.black.push(tag);
27035             
27036         }, this);
27037         
27038         
27039         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27040         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27041         
27042         this.cwhite = [];
27043         this.cblack = [];
27044         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27045             if (b.indexOf(tag) > -1) {
27046                 return;
27047             }
27048             this.cwhite.push(tag);
27049             
27050         }, this);
27051         
27052         Roo.each(w, function(tag) {
27053             if (b.indexOf(tag) > -1) {
27054                 return;
27055             }
27056             if (this.cwhite.indexOf(tag) > -1) {
27057                 return;
27058             }
27059             this.cwhite.push(tag);
27060             
27061         }, this);
27062         
27063         
27064         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27065             if (w.indexOf(tag) > -1) {
27066                 return;
27067             }
27068             this.cblack.push(tag);
27069             
27070         }, this);
27071         
27072         Roo.each(b, function(tag) {
27073             if (w.indexOf(tag) > -1) {
27074                 return;
27075             }
27076             if (this.cblack.indexOf(tag) > -1) {
27077                 return;
27078             }
27079             this.cblack.push(tag);
27080             
27081         }, this);
27082     },
27083     
27084     setStylesheets : function(stylesheets)
27085     {
27086         if(typeof(stylesheets) == 'string'){
27087             Roo.get(this.iframe.contentDocument.head).createChild({
27088                 tag : 'link',
27089                 rel : 'stylesheet',
27090                 type : 'text/css',
27091                 href : stylesheets
27092             });
27093             
27094             return;
27095         }
27096         var _this = this;
27097      
27098         Roo.each(stylesheets, function(s) {
27099             if(!s.length){
27100                 return;
27101             }
27102             
27103             Roo.get(_this.iframe.contentDocument.head).createChild({
27104                 tag : 'link',
27105                 rel : 'stylesheet',
27106                 type : 'text/css',
27107                 href : s
27108             });
27109         });
27110
27111         
27112     },
27113     
27114     removeStylesheets : function()
27115     {
27116         var _this = this;
27117         
27118         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27119             s.remove();
27120         });
27121     },
27122     
27123     setStyle : function(style)
27124     {
27125         Roo.get(this.iframe.contentDocument.head).createChild({
27126             tag : 'style',
27127             type : 'text/css',
27128             html : style
27129         });
27130
27131         return;
27132     }
27133     
27134     // hide stuff that is not compatible
27135     /**
27136      * @event blur
27137      * @hide
27138      */
27139     /**
27140      * @event change
27141      * @hide
27142      */
27143     /**
27144      * @event focus
27145      * @hide
27146      */
27147     /**
27148      * @event specialkey
27149      * @hide
27150      */
27151     /**
27152      * @cfg {String} fieldClass @hide
27153      */
27154     /**
27155      * @cfg {String} focusClass @hide
27156      */
27157     /**
27158      * @cfg {String} autoCreate @hide
27159      */
27160     /**
27161      * @cfg {String} inputType @hide
27162      */
27163     /**
27164      * @cfg {String} invalidClass @hide
27165      */
27166     /**
27167      * @cfg {String} invalidText @hide
27168      */
27169     /**
27170      * @cfg {String} msgFx @hide
27171      */
27172     /**
27173      * @cfg {String} validateOnBlur @hide
27174      */
27175 });
27176
27177 Roo.HtmlEditorCore.white = [
27178         'area', 'br', 'img', 'input', 'hr', 'wbr',
27179         
27180        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27181        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27182        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27183        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27184        'table',   'ul',         'xmp', 
27185        
27186        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27187       'thead',   'tr', 
27188      
27189       'dir', 'menu', 'ol', 'ul', 'dl',
27190        
27191       'embed',  'object'
27192 ];
27193
27194
27195 Roo.HtmlEditorCore.black = [
27196     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27197         'applet', // 
27198         'base',   'basefont', 'bgsound', 'blink',  'body', 
27199         'frame',  'frameset', 'head',    'html',   'ilayer', 
27200         'iframe', 'layer',  'link',     'meta',    'object',   
27201         'script', 'style' ,'title',  'xml' // clean later..
27202 ];
27203 Roo.HtmlEditorCore.clean = [
27204     'script', 'style', 'title', 'xml'
27205 ];
27206 Roo.HtmlEditorCore.remove = [
27207     'font'
27208 ];
27209 // attributes..
27210
27211 Roo.HtmlEditorCore.ablack = [
27212     'on'
27213 ];
27214     
27215 Roo.HtmlEditorCore.aclean = [ 
27216     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27217 ];
27218
27219 // protocols..
27220 Roo.HtmlEditorCore.pwhite= [
27221         'http',  'https',  'mailto'
27222 ];
27223
27224 // white listed style attributes.
27225 Roo.HtmlEditorCore.cwhite= [
27226       //  'text-align', /// default is to allow most things..
27227       
27228          
27229 //        'font-size'//??
27230 ];
27231
27232 // black listed style attributes.
27233 Roo.HtmlEditorCore.cblack= [
27234       //  'font-size' -- this can be set by the project 
27235 ];
27236
27237
27238 Roo.HtmlEditorCore.swapCodes   =[ 
27239     [    8211, "&#8211;" ], 
27240     [    8212, "&#8212;" ], 
27241     [    8216,  "'" ],  
27242     [    8217, "'" ],  
27243     [    8220, '"' ],  
27244     [    8221, '"' ],  
27245     [    8226, "*" ],  
27246     [    8230, "..." ]
27247 ]; 
27248
27249     /*
27250  * - LGPL
27251  *
27252  * HtmlEditor
27253  * 
27254  */
27255
27256 /**
27257  * @class Roo.bootstrap.HtmlEditor
27258  * @extends Roo.bootstrap.TextArea
27259  * Bootstrap HtmlEditor class
27260
27261  * @constructor
27262  * Create a new HtmlEditor
27263  * @param {Object} config The config object
27264  */
27265
27266 Roo.bootstrap.HtmlEditor = function(config){
27267     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27268     if (!this.toolbars) {
27269         this.toolbars = [];
27270     }
27271     
27272     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27273     this.addEvents({
27274             /**
27275              * @event initialize
27276              * Fires when the editor is fully initialized (including the iframe)
27277              * @param {HtmlEditor} this
27278              */
27279             initialize: true,
27280             /**
27281              * @event activate
27282              * Fires when the editor is first receives the focus. Any insertion must wait
27283              * until after this event.
27284              * @param {HtmlEditor} this
27285              */
27286             activate: true,
27287              /**
27288              * @event beforesync
27289              * Fires before the textarea is updated with content from the editor iframe. Return false
27290              * to cancel the sync.
27291              * @param {HtmlEditor} this
27292              * @param {String} html
27293              */
27294             beforesync: true,
27295              /**
27296              * @event beforepush
27297              * Fires before the iframe editor is updated with content from the textarea. Return false
27298              * to cancel the push.
27299              * @param {HtmlEditor} this
27300              * @param {String} html
27301              */
27302             beforepush: true,
27303              /**
27304              * @event sync
27305              * Fires when the textarea is updated with content from the editor iframe.
27306              * @param {HtmlEditor} this
27307              * @param {String} html
27308              */
27309             sync: true,
27310              /**
27311              * @event push
27312              * Fires when the iframe editor is updated with content from the textarea.
27313              * @param {HtmlEditor} this
27314              * @param {String} html
27315              */
27316             push: true,
27317              /**
27318              * @event editmodechange
27319              * Fires when the editor switches edit modes
27320              * @param {HtmlEditor} this
27321              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27322              */
27323             editmodechange: true,
27324             /**
27325              * @event editorevent
27326              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27327              * @param {HtmlEditor} this
27328              */
27329             editorevent: true,
27330             /**
27331              * @event firstfocus
27332              * Fires when on first focus - needed by toolbars..
27333              * @param {HtmlEditor} this
27334              */
27335             firstfocus: true,
27336             /**
27337              * @event autosave
27338              * Auto save the htmlEditor value as a file into Events
27339              * @param {HtmlEditor} this
27340              */
27341             autosave: true,
27342             /**
27343              * @event savedpreview
27344              * preview the saved version of htmlEditor
27345              * @param {HtmlEditor} this
27346              */
27347             savedpreview: true
27348         });
27349 };
27350
27351
27352 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27353     
27354     
27355       /**
27356      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27357      */
27358     toolbars : false,
27359     
27360      /**
27361     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27362     */
27363     btns : [],
27364    
27365      /**
27366      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27367      *                        Roo.resizable.
27368      */
27369     resizable : false,
27370      /**
27371      * @cfg {Number} height (in pixels)
27372      */   
27373     height: 300,
27374    /**
27375      * @cfg {Number} width (in pixels)
27376      */   
27377     width: false,
27378     
27379     /**
27380      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27381      * 
27382      */
27383     stylesheets: false,
27384     
27385     // id of frame..
27386     frameId: false,
27387     
27388     // private properties
27389     validationEvent : false,
27390     deferHeight: true,
27391     initialized : false,
27392     activated : false,
27393     
27394     onFocus : Roo.emptyFn,
27395     iframePad:3,
27396     hideMode:'offsets',
27397     
27398     tbContainer : false,
27399     
27400     bodyCls : '',
27401     
27402     toolbarContainer :function() {
27403         return this.wrap.select('.x-html-editor-tb',true).first();
27404     },
27405
27406     /**
27407      * Protected method that will not generally be called directly. It
27408      * is called when the editor creates its toolbar. Override this method if you need to
27409      * add custom toolbar buttons.
27410      * @param {HtmlEditor} editor
27411      */
27412     createToolbar : function(){
27413         Roo.log('renewing');
27414         Roo.log("create toolbars");
27415         
27416         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27417         this.toolbars[0].render(this.toolbarContainer());
27418         
27419         return;
27420         
27421 //        if (!editor.toolbars || !editor.toolbars.length) {
27422 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27423 //        }
27424 //        
27425 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27426 //            editor.toolbars[i] = Roo.factory(
27427 //                    typeof(editor.toolbars[i]) == 'string' ?
27428 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27429 //                Roo.bootstrap.HtmlEditor);
27430 //            editor.toolbars[i].init(editor);
27431 //        }
27432     },
27433
27434      
27435     // private
27436     onRender : function(ct, position)
27437     {
27438        // Roo.log("Call onRender: " + this.xtype);
27439         var _t = this;
27440         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27441       
27442         this.wrap = this.inputEl().wrap({
27443             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27444         });
27445         
27446         this.editorcore.onRender(ct, position);
27447          
27448         if (this.resizable) {
27449             this.resizeEl = new Roo.Resizable(this.wrap, {
27450                 pinned : true,
27451                 wrap: true,
27452                 dynamic : true,
27453                 minHeight : this.height,
27454                 height: this.height,
27455                 handles : this.resizable,
27456                 width: this.width,
27457                 listeners : {
27458                     resize : function(r, w, h) {
27459                         _t.onResize(w,h); // -something
27460                     }
27461                 }
27462             });
27463             
27464         }
27465         this.createToolbar(this);
27466        
27467         
27468         if(!this.width && this.resizable){
27469             this.setSize(this.wrap.getSize());
27470         }
27471         if (this.resizeEl) {
27472             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27473             // should trigger onReize..
27474         }
27475         
27476     },
27477
27478     // private
27479     onResize : function(w, h)
27480     {
27481         Roo.log('resize: ' +w + ',' + h );
27482         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27483         var ew = false;
27484         var eh = false;
27485         
27486         if(this.inputEl() ){
27487             if(typeof w == 'number'){
27488                 var aw = w - this.wrap.getFrameWidth('lr');
27489                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27490                 ew = aw;
27491             }
27492             if(typeof h == 'number'){
27493                  var tbh = -11;  // fixme it needs to tool bar size!
27494                 for (var i =0; i < this.toolbars.length;i++) {
27495                     // fixme - ask toolbars for heights?
27496                     tbh += this.toolbars[i].el.getHeight();
27497                     //if (this.toolbars[i].footer) {
27498                     //    tbh += this.toolbars[i].footer.el.getHeight();
27499                     //}
27500                 }
27501               
27502                 
27503                 
27504                 
27505                 
27506                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27507                 ah -= 5; // knock a few pixes off for look..
27508                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27509                 var eh = ah;
27510             }
27511         }
27512         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27513         this.editorcore.onResize(ew,eh);
27514         
27515     },
27516
27517     /**
27518      * Toggles the editor between standard and source edit mode.
27519      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27520      */
27521     toggleSourceEdit : function(sourceEditMode)
27522     {
27523         this.editorcore.toggleSourceEdit(sourceEditMode);
27524         
27525         if(this.editorcore.sourceEditMode){
27526             Roo.log('editor - showing textarea');
27527             
27528 //            Roo.log('in');
27529 //            Roo.log(this.syncValue());
27530             this.syncValue();
27531             this.inputEl().removeClass(['hide', 'x-hidden']);
27532             this.inputEl().dom.removeAttribute('tabIndex');
27533             this.inputEl().focus();
27534         }else{
27535             Roo.log('editor - hiding textarea');
27536 //            Roo.log('out')
27537 //            Roo.log(this.pushValue()); 
27538             this.pushValue();
27539             
27540             this.inputEl().addClass(['hide', 'x-hidden']);
27541             this.inputEl().dom.setAttribute('tabIndex', -1);
27542             //this.deferFocus();
27543         }
27544          
27545         if(this.resizable){
27546             this.setSize(this.wrap.getSize());
27547         }
27548         
27549         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27550     },
27551  
27552     // private (for BoxComponent)
27553     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27554
27555     // private (for BoxComponent)
27556     getResizeEl : function(){
27557         return this.wrap;
27558     },
27559
27560     // private (for BoxComponent)
27561     getPositionEl : function(){
27562         return this.wrap;
27563     },
27564
27565     // private
27566     initEvents : function(){
27567         this.originalValue = this.getValue();
27568     },
27569
27570 //    /**
27571 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27572 //     * @method
27573 //     */
27574 //    markInvalid : Roo.emptyFn,
27575 //    /**
27576 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27577 //     * @method
27578 //     */
27579 //    clearInvalid : Roo.emptyFn,
27580
27581     setValue : function(v){
27582         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27583         this.editorcore.pushValue();
27584     },
27585
27586      
27587     // private
27588     deferFocus : function(){
27589         this.focus.defer(10, this);
27590     },
27591
27592     // doc'ed in Field
27593     focus : function(){
27594         this.editorcore.focus();
27595         
27596     },
27597       
27598
27599     // private
27600     onDestroy : function(){
27601         
27602         
27603         
27604         if(this.rendered){
27605             
27606             for (var i =0; i < this.toolbars.length;i++) {
27607                 // fixme - ask toolbars for heights?
27608                 this.toolbars[i].onDestroy();
27609             }
27610             
27611             this.wrap.dom.innerHTML = '';
27612             this.wrap.remove();
27613         }
27614     },
27615
27616     // private
27617     onFirstFocus : function(){
27618         //Roo.log("onFirstFocus");
27619         this.editorcore.onFirstFocus();
27620          for (var i =0; i < this.toolbars.length;i++) {
27621             this.toolbars[i].onFirstFocus();
27622         }
27623         
27624     },
27625     
27626     // private
27627     syncValue : function()
27628     {   
27629         this.editorcore.syncValue();
27630     },
27631     
27632     pushValue : function()
27633     {   
27634         this.editorcore.pushValue();
27635     }
27636      
27637     
27638     // hide stuff that is not compatible
27639     /**
27640      * @event blur
27641      * @hide
27642      */
27643     /**
27644      * @event change
27645      * @hide
27646      */
27647     /**
27648      * @event focus
27649      * @hide
27650      */
27651     /**
27652      * @event specialkey
27653      * @hide
27654      */
27655     /**
27656      * @cfg {String} fieldClass @hide
27657      */
27658     /**
27659      * @cfg {String} focusClass @hide
27660      */
27661     /**
27662      * @cfg {String} autoCreate @hide
27663      */
27664     /**
27665      * @cfg {String} inputType @hide
27666      */
27667      
27668     /**
27669      * @cfg {String} invalidText @hide
27670      */
27671     /**
27672      * @cfg {String} msgFx @hide
27673      */
27674     /**
27675      * @cfg {String} validateOnBlur @hide
27676      */
27677 });
27678  
27679     
27680    
27681    
27682    
27683       
27684 Roo.namespace('Roo.bootstrap.htmleditor');
27685 /**
27686  * @class Roo.bootstrap.HtmlEditorToolbar1
27687  * Basic Toolbar
27688  * 
27689  * @example
27690  * Usage:
27691  *
27692  new Roo.bootstrap.HtmlEditor({
27693     ....
27694     toolbars : [
27695         new Roo.bootstrap.HtmlEditorToolbar1({
27696             disable : { fonts: 1 , format: 1, ..., ... , ...],
27697             btns : [ .... ]
27698         })
27699     }
27700      
27701  * 
27702  * @cfg {Object} disable List of elements to disable..
27703  * @cfg {Array} btns List of additional buttons.
27704  * 
27705  * 
27706  * NEEDS Extra CSS? 
27707  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27708  */
27709  
27710 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27711 {
27712     
27713     Roo.apply(this, config);
27714     
27715     // default disabled, based on 'good practice'..
27716     this.disable = this.disable || {};
27717     Roo.applyIf(this.disable, {
27718         fontSize : true,
27719         colors : true,
27720         specialElements : true
27721     });
27722     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27723     
27724     this.editor = config.editor;
27725     this.editorcore = config.editor.editorcore;
27726     
27727     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27728     
27729     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27730     // dont call parent... till later.
27731 }
27732 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27733      
27734     bar : true,
27735     
27736     editor : false,
27737     editorcore : false,
27738     
27739     
27740     formats : [
27741         "p" ,  
27742         "h1","h2","h3","h4","h5","h6", 
27743         "pre", "code", 
27744         "abbr", "acronym", "address", "cite", "samp", "var",
27745         'div','span'
27746     ],
27747     
27748     onRender : function(ct, position)
27749     {
27750        // Roo.log("Call onRender: " + this.xtype);
27751         
27752        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27753        Roo.log(this.el);
27754        this.el.dom.style.marginBottom = '0';
27755        var _this = this;
27756        var editorcore = this.editorcore;
27757        var editor= this.editor;
27758        
27759        var children = [];
27760        var btn = function(id,cmd , toggle, handler, html){
27761        
27762             var  event = toggle ? 'toggle' : 'click';
27763        
27764             var a = {
27765                 size : 'sm',
27766                 xtype: 'Button',
27767                 xns: Roo.bootstrap,
27768                 //glyphicon : id,
27769                 fa: id,
27770                 cmd : id || cmd,
27771                 enableToggle:toggle !== false,
27772                 html : html || '',
27773                 pressed : toggle ? false : null,
27774                 listeners : {}
27775             };
27776             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27777                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27778             };
27779             children.push(a);
27780             return a;
27781        }
27782        
27783     //    var cb_box = function...
27784         
27785         var style = {
27786                 xtype: 'Button',
27787                 size : 'sm',
27788                 xns: Roo.bootstrap,
27789                 fa : 'font',
27790                 //html : 'submit'
27791                 menu : {
27792                     xtype: 'Menu',
27793                     xns: Roo.bootstrap,
27794                     items:  []
27795                 }
27796         };
27797         Roo.each(this.formats, function(f) {
27798             style.menu.items.push({
27799                 xtype :'MenuItem',
27800                 xns: Roo.bootstrap,
27801                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27802                 tagname : f,
27803                 listeners : {
27804                     click : function()
27805                     {
27806                         editorcore.insertTag(this.tagname);
27807                         editor.focus();
27808                     }
27809                 }
27810                 
27811             });
27812         });
27813         children.push(style);   
27814         
27815         btn('bold',false,true);
27816         btn('italic',false,true);
27817         btn('align-left', 'justifyleft',true);
27818         btn('align-center', 'justifycenter',true);
27819         btn('align-right' , 'justifyright',true);
27820         btn('link', false, false, function(btn) {
27821             //Roo.log("create link?");
27822             var url = prompt(this.createLinkText, this.defaultLinkValue);
27823             if(url && url != 'http:/'+'/'){
27824                 this.editorcore.relayCmd('createlink', url);
27825             }
27826         }),
27827         btn('list','insertunorderedlist',true);
27828         btn('pencil', false,true, function(btn){
27829                 Roo.log(this);
27830                 this.toggleSourceEdit(btn.pressed);
27831         });
27832         
27833         if (this.editor.btns.length > 0) {
27834             for (var i = 0; i<this.editor.btns.length; i++) {
27835                 children.push(this.editor.btns[i]);
27836             }
27837         }
27838         
27839         /*
27840         var cog = {
27841                 xtype: 'Button',
27842                 size : 'sm',
27843                 xns: Roo.bootstrap,
27844                 glyphicon : 'cog',
27845                 //html : 'submit'
27846                 menu : {
27847                     xtype: 'Menu',
27848                     xns: Roo.bootstrap,
27849                     items:  []
27850                 }
27851         };
27852         
27853         cog.menu.items.push({
27854             xtype :'MenuItem',
27855             xns: Roo.bootstrap,
27856             html : Clean styles,
27857             tagname : f,
27858             listeners : {
27859                 click : function()
27860                 {
27861                     editorcore.insertTag(this.tagname);
27862                     editor.focus();
27863                 }
27864             }
27865             
27866         });
27867        */
27868         
27869          
27870        this.xtype = 'NavSimplebar';
27871         
27872         for(var i=0;i< children.length;i++) {
27873             
27874             this.buttons.add(this.addxtypeChild(children[i]));
27875             
27876         }
27877         
27878         editor.on('editorevent', this.updateToolbar, this);
27879     },
27880     onBtnClick : function(id)
27881     {
27882        this.editorcore.relayCmd(id);
27883        this.editorcore.focus();
27884     },
27885     
27886     /**
27887      * Protected method that will not generally be called directly. It triggers
27888      * a toolbar update by reading the markup state of the current selection in the editor.
27889      */
27890     updateToolbar: function(){
27891
27892         if(!this.editorcore.activated){
27893             this.editor.onFirstFocus(); // is this neeed?
27894             return;
27895         }
27896
27897         var btns = this.buttons; 
27898         var doc = this.editorcore.doc;
27899         btns.get('bold').setActive(doc.queryCommandState('bold'));
27900         btns.get('italic').setActive(doc.queryCommandState('italic'));
27901         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27902         
27903         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27904         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27905         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27906         
27907         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27908         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27909          /*
27910         
27911         var ans = this.editorcore.getAllAncestors();
27912         if (this.formatCombo) {
27913             
27914             
27915             var store = this.formatCombo.store;
27916             this.formatCombo.setValue("");
27917             for (var i =0; i < ans.length;i++) {
27918                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27919                     // select it..
27920                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27921                     break;
27922                 }
27923             }
27924         }
27925         
27926         
27927         
27928         // hides menus... - so this cant be on a menu...
27929         Roo.bootstrap.MenuMgr.hideAll();
27930         */
27931         Roo.bootstrap.MenuMgr.hideAll();
27932         //this.editorsyncValue();
27933     },
27934     onFirstFocus: function() {
27935         this.buttons.each(function(item){
27936            item.enable();
27937         });
27938     },
27939     toggleSourceEdit : function(sourceEditMode){
27940         
27941           
27942         if(sourceEditMode){
27943             Roo.log("disabling buttons");
27944            this.buttons.each( function(item){
27945                 if(item.cmd != 'pencil'){
27946                     item.disable();
27947                 }
27948             });
27949           
27950         }else{
27951             Roo.log("enabling buttons");
27952             if(this.editorcore.initialized){
27953                 this.buttons.each( function(item){
27954                     item.enable();
27955                 });
27956             }
27957             
27958         }
27959         Roo.log("calling toggole on editor");
27960         // tell the editor that it's been pressed..
27961         this.editor.toggleSourceEdit(sourceEditMode);
27962        
27963     }
27964 });
27965
27966
27967
27968
27969  
27970 /*
27971  * - LGPL
27972  */
27973
27974 /**
27975  * @class Roo.bootstrap.Markdown
27976  * @extends Roo.bootstrap.TextArea
27977  * Bootstrap Showdown editable area
27978  * @cfg {string} content
27979  * 
27980  * @constructor
27981  * Create a new Showdown
27982  */
27983
27984 Roo.bootstrap.Markdown = function(config){
27985     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27986    
27987 };
27988
27989 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27990     
27991     editing :false,
27992     
27993     initEvents : function()
27994     {
27995         
27996         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27997         this.markdownEl = this.el.createChild({
27998             cls : 'roo-markdown-area'
27999         });
28000         this.inputEl().addClass('d-none');
28001         if (this.getValue() == '') {
28002             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28003             
28004         } else {
28005             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28006         }
28007         this.markdownEl.on('click', this.toggleTextEdit, this);
28008         this.on('blur', this.toggleTextEdit, this);
28009         this.on('specialkey', this.resizeTextArea, this);
28010     },
28011     
28012     toggleTextEdit : function()
28013     {
28014         var sh = this.markdownEl.getHeight();
28015         this.inputEl().addClass('d-none');
28016         this.markdownEl.addClass('d-none');
28017         if (!this.editing) {
28018             // show editor?
28019             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28020             this.inputEl().removeClass('d-none');
28021             this.inputEl().focus();
28022             this.editing = true;
28023             return;
28024         }
28025         // show showdown...
28026         this.updateMarkdown();
28027         this.markdownEl.removeClass('d-none');
28028         this.editing = false;
28029         return;
28030     },
28031     updateMarkdown : function()
28032     {
28033         if (this.getValue() == '') {
28034             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28035             return;
28036         }
28037  
28038         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28039     },
28040     
28041     resizeTextArea: function () {
28042         
28043         var sh = 100;
28044         Roo.log([sh, this.getValue().split("\n").length * 30]);
28045         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28046     },
28047     setValue : function(val)
28048     {
28049         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28050         if (!this.editing) {
28051             this.updateMarkdown();
28052         }
28053         
28054     },
28055     focus : function()
28056     {
28057         if (!this.editing) {
28058             this.toggleTextEdit();
28059         }
28060         
28061     }
28062
28063
28064 });/*
28065  * Based on:
28066  * Ext JS Library 1.1.1
28067  * Copyright(c) 2006-2007, Ext JS, LLC.
28068  *
28069  * Originally Released Under LGPL - original licence link has changed is not relivant.
28070  *
28071  * Fork - LGPL
28072  * <script type="text/javascript">
28073  */
28074  
28075 /**
28076  * @class Roo.bootstrap.PagingToolbar
28077  * @extends Roo.bootstrap.NavSimplebar
28078  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28079  * @constructor
28080  * Create a new PagingToolbar
28081  * @param {Object} config The config object
28082  * @param {Roo.data.Store} store
28083  */
28084 Roo.bootstrap.PagingToolbar = function(config)
28085 {
28086     // old args format still supported... - xtype is prefered..
28087         // created from xtype...
28088     
28089     this.ds = config.dataSource;
28090     
28091     if (config.store && !this.ds) {
28092         this.store= Roo.factory(config.store, Roo.data);
28093         this.ds = this.store;
28094         this.ds.xmodule = this.xmodule || false;
28095     }
28096     
28097     this.toolbarItems = [];
28098     if (config.items) {
28099         this.toolbarItems = config.items;
28100     }
28101     
28102     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28103     
28104     this.cursor = 0;
28105     
28106     if (this.ds) { 
28107         this.bind(this.ds);
28108     }
28109     
28110     if (Roo.bootstrap.version == 4) {
28111         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28112     } else {
28113         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28114     }
28115     
28116 };
28117
28118 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28119     /**
28120      * @cfg {Roo.data.Store} dataSource
28121      * The underlying data store providing the paged data
28122      */
28123     /**
28124      * @cfg {String/HTMLElement/Element} container
28125      * container The id or element that will contain the toolbar
28126      */
28127     /**
28128      * @cfg {Boolean} displayInfo
28129      * True to display the displayMsg (defaults to false)
28130      */
28131     /**
28132      * @cfg {Number} pageSize
28133      * The number of records to display per page (defaults to 20)
28134      */
28135     pageSize: 20,
28136     /**
28137      * @cfg {String} displayMsg
28138      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28139      */
28140     displayMsg : 'Displaying {0} - {1} of {2}',
28141     /**
28142      * @cfg {String} emptyMsg
28143      * The message to display when no records are found (defaults to "No data to display")
28144      */
28145     emptyMsg : 'No data to display',
28146     /**
28147      * Customizable piece of the default paging text (defaults to "Page")
28148      * @type String
28149      */
28150     beforePageText : "Page",
28151     /**
28152      * Customizable piece of the default paging text (defaults to "of %0")
28153      * @type String
28154      */
28155     afterPageText : "of {0}",
28156     /**
28157      * Customizable piece of the default paging text (defaults to "First Page")
28158      * @type String
28159      */
28160     firstText : "First Page",
28161     /**
28162      * Customizable piece of the default paging text (defaults to "Previous Page")
28163      * @type String
28164      */
28165     prevText : "Previous Page",
28166     /**
28167      * Customizable piece of the default paging text (defaults to "Next Page")
28168      * @type String
28169      */
28170     nextText : "Next Page",
28171     /**
28172      * Customizable piece of the default paging text (defaults to "Last Page")
28173      * @type String
28174      */
28175     lastText : "Last Page",
28176     /**
28177      * Customizable piece of the default paging text (defaults to "Refresh")
28178      * @type String
28179      */
28180     refreshText : "Refresh",
28181
28182     buttons : false,
28183     // private
28184     onRender : function(ct, position) 
28185     {
28186         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28187         this.navgroup.parentId = this.id;
28188         this.navgroup.onRender(this.el, null);
28189         // add the buttons to the navgroup
28190         
28191         if(this.displayInfo){
28192             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28193             this.displayEl = this.el.select('.x-paging-info', true).first();
28194 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28195 //            this.displayEl = navel.el.select('span',true).first();
28196         }
28197         
28198         var _this = this;
28199         
28200         if(this.buttons){
28201             Roo.each(_this.buttons, function(e){ // this might need to use render????
28202                Roo.factory(e).render(_this.el);
28203             });
28204         }
28205             
28206         Roo.each(_this.toolbarItems, function(e) {
28207             _this.navgroup.addItem(e);
28208         });
28209         
28210         
28211         this.first = this.navgroup.addItem({
28212             tooltip: this.firstText,
28213             cls: "prev btn-outline-secondary",
28214             html : ' <i class="fa fa-step-backward"></i>',
28215             disabled: true,
28216             preventDefault: true,
28217             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28218         });
28219         
28220         this.prev =  this.navgroup.addItem({
28221             tooltip: this.prevText,
28222             cls: "prev btn-outline-secondary",
28223             html : ' <i class="fa fa-backward"></i>',
28224             disabled: true,
28225             preventDefault: true,
28226             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28227         });
28228     //this.addSeparator();
28229         
28230         
28231         var field = this.navgroup.addItem( {
28232             tagtype : 'span',
28233             cls : 'x-paging-position  btn-outline-secondary',
28234              disabled: true,
28235             html : this.beforePageText  +
28236                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28237                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28238          } ); //?? escaped?
28239         
28240         this.field = field.el.select('input', true).first();
28241         this.field.on("keydown", this.onPagingKeydown, this);
28242         this.field.on("focus", function(){this.dom.select();});
28243     
28244     
28245         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28246         //this.field.setHeight(18);
28247         //this.addSeparator();
28248         this.next = this.navgroup.addItem({
28249             tooltip: this.nextText,
28250             cls: "next btn-outline-secondary",
28251             html : ' <i class="fa fa-forward"></i>',
28252             disabled: true,
28253             preventDefault: true,
28254             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28255         });
28256         this.last = this.navgroup.addItem({
28257             tooltip: this.lastText,
28258             html : ' <i class="fa fa-step-forward"></i>',
28259             cls: "next btn-outline-secondary",
28260             disabled: true,
28261             preventDefault: true,
28262             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28263         });
28264     //this.addSeparator();
28265         this.loading = this.navgroup.addItem({
28266             tooltip: this.refreshText,
28267             cls: "btn-outline-secondary",
28268             html : ' <i class="fa fa-refresh"></i>',
28269             preventDefault: true,
28270             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28271         });
28272         
28273     },
28274
28275     // private
28276     updateInfo : function(){
28277         if(this.displayEl){
28278             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28279             var msg = count == 0 ?
28280                 this.emptyMsg :
28281                 String.format(
28282                     this.displayMsg,
28283                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28284                 );
28285             this.displayEl.update(msg);
28286         }
28287     },
28288
28289     // private
28290     onLoad : function(ds, r, o)
28291     {
28292         this.cursor = o.params && o.params.start ? o.params.start : 0;
28293         
28294         var d = this.getPageData(),
28295             ap = d.activePage,
28296             ps = d.pages;
28297         
28298         
28299         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28300         this.field.dom.value = ap;
28301         this.first.setDisabled(ap == 1);
28302         this.prev.setDisabled(ap == 1);
28303         this.next.setDisabled(ap == ps);
28304         this.last.setDisabled(ap == ps);
28305         this.loading.enable();
28306         this.updateInfo();
28307     },
28308
28309     // private
28310     getPageData : function(){
28311         var total = this.ds.getTotalCount();
28312         return {
28313             total : total,
28314             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28315             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28316         };
28317     },
28318
28319     // private
28320     onLoadError : function(){
28321         this.loading.enable();
28322     },
28323
28324     // private
28325     onPagingKeydown : function(e){
28326         var k = e.getKey();
28327         var d = this.getPageData();
28328         if(k == e.RETURN){
28329             var v = this.field.dom.value, pageNum;
28330             if(!v || isNaN(pageNum = parseInt(v, 10))){
28331                 this.field.dom.value = d.activePage;
28332                 return;
28333             }
28334             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28335             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28336             e.stopEvent();
28337         }
28338         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))
28339         {
28340           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28341           this.field.dom.value = pageNum;
28342           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28343           e.stopEvent();
28344         }
28345         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28346         {
28347           var v = this.field.dom.value, pageNum; 
28348           var increment = (e.shiftKey) ? 10 : 1;
28349           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28350                 increment *= -1;
28351           }
28352           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28353             this.field.dom.value = d.activePage;
28354             return;
28355           }
28356           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28357           {
28358             this.field.dom.value = parseInt(v, 10) + increment;
28359             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28360             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28361           }
28362           e.stopEvent();
28363         }
28364     },
28365
28366     // private
28367     beforeLoad : function(){
28368         if(this.loading){
28369             this.loading.disable();
28370         }
28371     },
28372
28373     // private
28374     onClick : function(which){
28375         
28376         var ds = this.ds;
28377         if (!ds) {
28378             return;
28379         }
28380         
28381         switch(which){
28382             case "first":
28383                 ds.load({params:{start: 0, limit: this.pageSize}});
28384             break;
28385             case "prev":
28386                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28387             break;
28388             case "next":
28389                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28390             break;
28391             case "last":
28392                 var total = ds.getTotalCount();
28393                 var extra = total % this.pageSize;
28394                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28395                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28396             break;
28397             case "refresh":
28398                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28399             break;
28400         }
28401     },
28402
28403     /**
28404      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28405      * @param {Roo.data.Store} store The data store to unbind
28406      */
28407     unbind : function(ds){
28408         ds.un("beforeload", this.beforeLoad, this);
28409         ds.un("load", this.onLoad, this);
28410         ds.un("loadexception", this.onLoadError, this);
28411         ds.un("remove", this.updateInfo, this);
28412         ds.un("add", this.updateInfo, this);
28413         this.ds = undefined;
28414     },
28415
28416     /**
28417      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28418      * @param {Roo.data.Store} store The data store to bind
28419      */
28420     bind : function(ds){
28421         ds.on("beforeload", this.beforeLoad, this);
28422         ds.on("load", this.onLoad, this);
28423         ds.on("loadexception", this.onLoadError, this);
28424         ds.on("remove", this.updateInfo, this);
28425         ds.on("add", this.updateInfo, this);
28426         this.ds = ds;
28427     }
28428 });/*
28429  * - LGPL
28430  *
28431  * element
28432  * 
28433  */
28434
28435 /**
28436  * @class Roo.bootstrap.MessageBar
28437  * @extends Roo.bootstrap.Component
28438  * Bootstrap MessageBar class
28439  * @cfg {String} html contents of the MessageBar
28440  * @cfg {String} weight (info | success | warning | danger) default info
28441  * @cfg {String} beforeClass insert the bar before the given class
28442  * @cfg {Boolean} closable (true | false) default false
28443  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28444  * 
28445  * @constructor
28446  * Create a new Element
28447  * @param {Object} config The config object
28448  */
28449
28450 Roo.bootstrap.MessageBar = function(config){
28451     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28452 };
28453
28454 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28455     
28456     html: '',
28457     weight: 'info',
28458     closable: false,
28459     fixed: false,
28460     beforeClass: 'bootstrap-sticky-wrap',
28461     
28462     getAutoCreate : function(){
28463         
28464         var cfg = {
28465             tag: 'div',
28466             cls: 'alert alert-dismissable alert-' + this.weight,
28467             cn: [
28468                 {
28469                     tag: 'span',
28470                     cls: 'message',
28471                     html: this.html || ''
28472                 }
28473             ]
28474         };
28475         
28476         if(this.fixed){
28477             cfg.cls += ' alert-messages-fixed';
28478         }
28479         
28480         if(this.closable){
28481             cfg.cn.push({
28482                 tag: 'button',
28483                 cls: 'close',
28484                 html: 'x'
28485             });
28486         }
28487         
28488         return cfg;
28489     },
28490     
28491     onRender : function(ct, position)
28492     {
28493         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28494         
28495         if(!this.el){
28496             var cfg = Roo.apply({},  this.getAutoCreate());
28497             cfg.id = Roo.id();
28498             
28499             if (this.cls) {
28500                 cfg.cls += ' ' + this.cls;
28501             }
28502             if (this.style) {
28503                 cfg.style = this.style;
28504             }
28505             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28506             
28507             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28508         }
28509         
28510         this.el.select('>button.close').on('click', this.hide, this);
28511         
28512     },
28513     
28514     show : function()
28515     {
28516         if (!this.rendered) {
28517             this.render();
28518         }
28519         
28520         this.el.show();
28521         
28522         this.fireEvent('show', this);
28523         
28524     },
28525     
28526     hide : function()
28527     {
28528         if (!this.rendered) {
28529             this.render();
28530         }
28531         
28532         this.el.hide();
28533         
28534         this.fireEvent('hide', this);
28535     },
28536     
28537     update : function()
28538     {
28539 //        var e = this.el.dom.firstChild;
28540 //        
28541 //        if(this.closable){
28542 //            e = e.nextSibling;
28543 //        }
28544 //        
28545 //        e.data = this.html || '';
28546
28547         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28548     }
28549    
28550 });
28551
28552  
28553
28554      /*
28555  * - LGPL
28556  *
28557  * Graph
28558  * 
28559  */
28560
28561
28562 /**
28563  * @class Roo.bootstrap.Graph
28564  * @extends Roo.bootstrap.Component
28565  * Bootstrap Graph class
28566 > Prameters
28567  -sm {number} sm 4
28568  -md {number} md 5
28569  @cfg {String} graphtype  bar | vbar | pie
28570  @cfg {number} g_x coodinator | centre x (pie)
28571  @cfg {number} g_y coodinator | centre y (pie)
28572  @cfg {number} g_r radius (pie)
28573  @cfg {number} g_height height of the chart (respected by all elements in the set)
28574  @cfg {number} g_width width of the chart (respected by all elements in the set)
28575  @cfg {Object} title The title of the chart
28576     
28577  -{Array}  values
28578  -opts (object) options for the chart 
28579      o {
28580      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28581      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28582      o vgutter (number)
28583      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.
28584      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28585      o to
28586      o stretch (boolean)
28587      o }
28588  -opts (object) options for the pie
28589      o{
28590      o cut
28591      o startAngle (number)
28592      o endAngle (number)
28593      } 
28594  *
28595  * @constructor
28596  * Create a new Input
28597  * @param {Object} config The config object
28598  */
28599
28600 Roo.bootstrap.Graph = function(config){
28601     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28602     
28603     this.addEvents({
28604         // img events
28605         /**
28606          * @event click
28607          * The img click event for the img.
28608          * @param {Roo.EventObject} e
28609          */
28610         "click" : true
28611     });
28612 };
28613
28614 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28615     
28616     sm: 4,
28617     md: 5,
28618     graphtype: 'bar',
28619     g_height: 250,
28620     g_width: 400,
28621     g_x: 50,
28622     g_y: 50,
28623     g_r: 30,
28624     opts:{
28625         //g_colors: this.colors,
28626         g_type: 'soft',
28627         g_gutter: '20%'
28628
28629     },
28630     title : false,
28631
28632     getAutoCreate : function(){
28633         
28634         var cfg = {
28635             tag: 'div',
28636             html : null
28637         };
28638         
28639         
28640         return  cfg;
28641     },
28642
28643     onRender : function(ct,position){
28644         
28645         
28646         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28647         
28648         if (typeof(Raphael) == 'undefined') {
28649             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28650             return;
28651         }
28652         
28653         this.raphael = Raphael(this.el.dom);
28654         
28655                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28656                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28657                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28658                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28659                 /*
28660                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28661                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28662                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28663                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28664                 
28665                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28666                 r.barchart(330, 10, 300, 220, data1);
28667                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28668                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28669                 */
28670                 
28671                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28672                 // r.barchart(30, 30, 560, 250,  xdata, {
28673                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28674                 //     axis : "0 0 1 1",
28675                 //     axisxlabels :  xdata
28676                 //     //yvalues : cols,
28677                    
28678                 // });
28679 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28680 //        
28681 //        this.load(null,xdata,{
28682 //                axis : "0 0 1 1",
28683 //                axisxlabels :  xdata
28684 //                });
28685
28686     },
28687
28688     load : function(graphtype,xdata,opts)
28689     {
28690         this.raphael.clear();
28691         if(!graphtype) {
28692             graphtype = this.graphtype;
28693         }
28694         if(!opts){
28695             opts = this.opts;
28696         }
28697         var r = this.raphael,
28698             fin = function () {
28699                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28700             },
28701             fout = function () {
28702                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28703             },
28704             pfin = function() {
28705                 this.sector.stop();
28706                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28707
28708                 if (this.label) {
28709                     this.label[0].stop();
28710                     this.label[0].attr({ r: 7.5 });
28711                     this.label[1].attr({ "font-weight": 800 });
28712                 }
28713             },
28714             pfout = function() {
28715                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28716
28717                 if (this.label) {
28718                     this.label[0].animate({ r: 5 }, 500, "bounce");
28719                     this.label[1].attr({ "font-weight": 400 });
28720                 }
28721             };
28722
28723         switch(graphtype){
28724             case 'bar':
28725                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28726                 break;
28727             case 'hbar':
28728                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28729                 break;
28730             case 'pie':
28731 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28732 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28733 //            
28734                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28735                 
28736                 break;
28737
28738         }
28739         
28740         if(this.title){
28741             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28742         }
28743         
28744     },
28745     
28746     setTitle: function(o)
28747     {
28748         this.title = o;
28749     },
28750     
28751     initEvents: function() {
28752         
28753         if(!this.href){
28754             this.el.on('click', this.onClick, this);
28755         }
28756     },
28757     
28758     onClick : function(e)
28759     {
28760         Roo.log('img onclick');
28761         this.fireEvent('click', this, e);
28762     }
28763    
28764 });
28765
28766  
28767 /*
28768  * - LGPL
28769  *
28770  * numberBox
28771  * 
28772  */
28773 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28774
28775 /**
28776  * @class Roo.bootstrap.dash.NumberBox
28777  * @extends Roo.bootstrap.Component
28778  * Bootstrap NumberBox class
28779  * @cfg {String} headline Box headline
28780  * @cfg {String} content Box content
28781  * @cfg {String} icon Box icon
28782  * @cfg {String} footer Footer text
28783  * @cfg {String} fhref Footer href
28784  * 
28785  * @constructor
28786  * Create a new NumberBox
28787  * @param {Object} config The config object
28788  */
28789
28790
28791 Roo.bootstrap.dash.NumberBox = function(config){
28792     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28793     
28794 };
28795
28796 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28797     
28798     headline : '',
28799     content : '',
28800     icon : '',
28801     footer : '',
28802     fhref : '',
28803     ficon : '',
28804     
28805     getAutoCreate : function(){
28806         
28807         var cfg = {
28808             tag : 'div',
28809             cls : 'small-box ',
28810             cn : [
28811                 {
28812                     tag : 'div',
28813                     cls : 'inner',
28814                     cn :[
28815                         {
28816                             tag : 'h3',
28817                             cls : 'roo-headline',
28818                             html : this.headline
28819                         },
28820                         {
28821                             tag : 'p',
28822                             cls : 'roo-content',
28823                             html : this.content
28824                         }
28825                     ]
28826                 }
28827             ]
28828         };
28829         
28830         if(this.icon){
28831             cfg.cn.push({
28832                 tag : 'div',
28833                 cls : 'icon',
28834                 cn :[
28835                     {
28836                         tag : 'i',
28837                         cls : 'ion ' + this.icon
28838                     }
28839                 ]
28840             });
28841         }
28842         
28843         if(this.footer){
28844             var footer = {
28845                 tag : 'a',
28846                 cls : 'small-box-footer',
28847                 href : this.fhref || '#',
28848                 html : this.footer
28849             };
28850             
28851             cfg.cn.push(footer);
28852             
28853         }
28854         
28855         return  cfg;
28856     },
28857
28858     onRender : function(ct,position){
28859         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28860
28861
28862        
28863                 
28864     },
28865
28866     setHeadline: function (value)
28867     {
28868         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28869     },
28870     
28871     setFooter: function (value, href)
28872     {
28873         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28874         
28875         if(href){
28876             this.el.select('a.small-box-footer',true).first().attr('href', href);
28877         }
28878         
28879     },
28880
28881     setContent: function (value)
28882     {
28883         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28884     },
28885
28886     initEvents: function() 
28887     {   
28888         
28889     }
28890     
28891 });
28892
28893  
28894 /*
28895  * - LGPL
28896  *
28897  * TabBox
28898  * 
28899  */
28900 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28901
28902 /**
28903  * @class Roo.bootstrap.dash.TabBox
28904  * @extends Roo.bootstrap.Component
28905  * Bootstrap TabBox class
28906  * @cfg {String} title Title of the TabBox
28907  * @cfg {String} icon Icon of the TabBox
28908  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28909  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28910  * 
28911  * @constructor
28912  * Create a new TabBox
28913  * @param {Object} config The config object
28914  */
28915
28916
28917 Roo.bootstrap.dash.TabBox = function(config){
28918     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28919     this.addEvents({
28920         // raw events
28921         /**
28922          * @event addpane
28923          * When a pane is added
28924          * @param {Roo.bootstrap.dash.TabPane} pane
28925          */
28926         "addpane" : true,
28927         /**
28928          * @event activatepane
28929          * When a pane is activated
28930          * @param {Roo.bootstrap.dash.TabPane} pane
28931          */
28932         "activatepane" : true
28933         
28934          
28935     });
28936     
28937     this.panes = [];
28938 };
28939
28940 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28941
28942     title : '',
28943     icon : false,
28944     showtabs : true,
28945     tabScrollable : false,
28946     
28947     getChildContainer : function()
28948     {
28949         return this.el.select('.tab-content', true).first();
28950     },
28951     
28952     getAutoCreate : function(){
28953         
28954         var header = {
28955             tag: 'li',
28956             cls: 'pull-left header',
28957             html: this.title,
28958             cn : []
28959         };
28960         
28961         if(this.icon){
28962             header.cn.push({
28963                 tag: 'i',
28964                 cls: 'fa ' + this.icon
28965             });
28966         }
28967         
28968         var h = {
28969             tag: 'ul',
28970             cls: 'nav nav-tabs pull-right',
28971             cn: [
28972                 header
28973             ]
28974         };
28975         
28976         if(this.tabScrollable){
28977             h = {
28978                 tag: 'div',
28979                 cls: 'tab-header',
28980                 cn: [
28981                     {
28982                         tag: 'ul',
28983                         cls: 'nav nav-tabs pull-right',
28984                         cn: [
28985                             header
28986                         ]
28987                     }
28988                 ]
28989             };
28990         }
28991         
28992         var cfg = {
28993             tag: 'div',
28994             cls: 'nav-tabs-custom',
28995             cn: [
28996                 h,
28997                 {
28998                     tag: 'div',
28999                     cls: 'tab-content no-padding',
29000                     cn: []
29001                 }
29002             ]
29003         };
29004
29005         return  cfg;
29006     },
29007     initEvents : function()
29008     {
29009         //Roo.log('add add pane handler');
29010         this.on('addpane', this.onAddPane, this);
29011     },
29012      /**
29013      * Updates the box title
29014      * @param {String} html to set the title to.
29015      */
29016     setTitle : function(value)
29017     {
29018         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29019     },
29020     onAddPane : function(pane)
29021     {
29022         this.panes.push(pane);
29023         //Roo.log('addpane');
29024         //Roo.log(pane);
29025         // tabs are rendere left to right..
29026         if(!this.showtabs){
29027             return;
29028         }
29029         
29030         var ctr = this.el.select('.nav-tabs', true).first();
29031          
29032          
29033         var existing = ctr.select('.nav-tab',true);
29034         var qty = existing.getCount();;
29035         
29036         
29037         var tab = ctr.createChild({
29038             tag : 'li',
29039             cls : 'nav-tab' + (qty ? '' : ' active'),
29040             cn : [
29041                 {
29042                     tag : 'a',
29043                     href:'#',
29044                     html : pane.title
29045                 }
29046             ]
29047         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29048         pane.tab = tab;
29049         
29050         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29051         if (!qty) {
29052             pane.el.addClass('active');
29053         }
29054         
29055                 
29056     },
29057     onTabClick : function(ev,un,ob,pane)
29058     {
29059         //Roo.log('tab - prev default');
29060         ev.preventDefault();
29061         
29062         
29063         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29064         pane.tab.addClass('active');
29065         //Roo.log(pane.title);
29066         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29067         // technically we should have a deactivate event.. but maybe add later.
29068         // and it should not de-activate the selected tab...
29069         this.fireEvent('activatepane', pane);
29070         pane.el.addClass('active');
29071         pane.fireEvent('activate');
29072         
29073         
29074     },
29075     
29076     getActivePane : function()
29077     {
29078         var r = false;
29079         Roo.each(this.panes, function(p) {
29080             if(p.el.hasClass('active')){
29081                 r = p;
29082                 return false;
29083             }
29084             
29085             return;
29086         });
29087         
29088         return r;
29089     }
29090     
29091     
29092 });
29093
29094  
29095 /*
29096  * - LGPL
29097  *
29098  * Tab pane
29099  * 
29100  */
29101 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29102 /**
29103  * @class Roo.bootstrap.TabPane
29104  * @extends Roo.bootstrap.Component
29105  * Bootstrap TabPane class
29106  * @cfg {Boolean} active (false | true) Default false
29107  * @cfg {String} title title of panel
29108
29109  * 
29110  * @constructor
29111  * Create a new TabPane
29112  * @param {Object} config The config object
29113  */
29114
29115 Roo.bootstrap.dash.TabPane = function(config){
29116     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29117     
29118     this.addEvents({
29119         // raw events
29120         /**
29121          * @event activate
29122          * When a pane is activated
29123          * @param {Roo.bootstrap.dash.TabPane} pane
29124          */
29125         "activate" : true
29126          
29127     });
29128 };
29129
29130 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29131     
29132     active : false,
29133     title : '',
29134     
29135     // the tabBox that this is attached to.
29136     tab : false,
29137      
29138     getAutoCreate : function() 
29139     {
29140         var cfg = {
29141             tag: 'div',
29142             cls: 'tab-pane'
29143         };
29144         
29145         if(this.active){
29146             cfg.cls += ' active';
29147         }
29148         
29149         return cfg;
29150     },
29151     initEvents  : function()
29152     {
29153         //Roo.log('trigger add pane handler');
29154         this.parent().fireEvent('addpane', this)
29155     },
29156     
29157      /**
29158      * Updates the tab title 
29159      * @param {String} html to set the title to.
29160      */
29161     setTitle: function(str)
29162     {
29163         if (!this.tab) {
29164             return;
29165         }
29166         this.title = str;
29167         this.tab.select('a', true).first().dom.innerHTML = str;
29168         
29169     }
29170     
29171     
29172     
29173 });
29174
29175  
29176
29177
29178  /*
29179  * - LGPL
29180  *
29181  * menu
29182  * 
29183  */
29184 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29185
29186 /**
29187  * @class Roo.bootstrap.menu.Menu
29188  * @extends Roo.bootstrap.Component
29189  * Bootstrap Menu class - container for Menu
29190  * @cfg {String} html Text of the menu
29191  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29192  * @cfg {String} icon Font awesome icon
29193  * @cfg {String} pos Menu align to (top | bottom) default bottom
29194  * 
29195  * 
29196  * @constructor
29197  * Create a new Menu
29198  * @param {Object} config The config object
29199  */
29200
29201
29202 Roo.bootstrap.menu.Menu = function(config){
29203     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29204     
29205     this.addEvents({
29206         /**
29207          * @event beforeshow
29208          * Fires before this menu is displayed
29209          * @param {Roo.bootstrap.menu.Menu} this
29210          */
29211         beforeshow : true,
29212         /**
29213          * @event beforehide
29214          * Fires before this menu is hidden
29215          * @param {Roo.bootstrap.menu.Menu} this
29216          */
29217         beforehide : true,
29218         /**
29219          * @event show
29220          * Fires after this menu is displayed
29221          * @param {Roo.bootstrap.menu.Menu} this
29222          */
29223         show : true,
29224         /**
29225          * @event hide
29226          * Fires after this menu is hidden
29227          * @param {Roo.bootstrap.menu.Menu} this
29228          */
29229         hide : true,
29230         /**
29231          * @event click
29232          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29233          * @param {Roo.bootstrap.menu.Menu} this
29234          * @param {Roo.EventObject} e
29235          */
29236         click : true
29237     });
29238     
29239 };
29240
29241 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29242     
29243     submenu : false,
29244     html : '',
29245     weight : 'default',
29246     icon : false,
29247     pos : 'bottom',
29248     
29249     
29250     getChildContainer : function() {
29251         if(this.isSubMenu){
29252             return this.el;
29253         }
29254         
29255         return this.el.select('ul.dropdown-menu', true).first();  
29256     },
29257     
29258     getAutoCreate : function()
29259     {
29260         var text = [
29261             {
29262                 tag : 'span',
29263                 cls : 'roo-menu-text',
29264                 html : this.html
29265             }
29266         ];
29267         
29268         if(this.icon){
29269             text.unshift({
29270                 tag : 'i',
29271                 cls : 'fa ' + this.icon
29272             })
29273         }
29274         
29275         
29276         var cfg = {
29277             tag : 'div',
29278             cls : 'btn-group',
29279             cn : [
29280                 {
29281                     tag : 'button',
29282                     cls : 'dropdown-button btn btn-' + this.weight,
29283                     cn : text
29284                 },
29285                 {
29286                     tag : 'button',
29287                     cls : 'dropdown-toggle btn btn-' + this.weight,
29288                     cn : [
29289                         {
29290                             tag : 'span',
29291                             cls : 'caret'
29292                         }
29293                     ]
29294                 },
29295                 {
29296                     tag : 'ul',
29297                     cls : 'dropdown-menu'
29298                 }
29299             ]
29300             
29301         };
29302         
29303         if(this.pos == 'top'){
29304             cfg.cls += ' dropup';
29305         }
29306         
29307         if(this.isSubMenu){
29308             cfg = {
29309                 tag : 'ul',
29310                 cls : 'dropdown-menu'
29311             }
29312         }
29313         
29314         return cfg;
29315     },
29316     
29317     onRender : function(ct, position)
29318     {
29319         this.isSubMenu = ct.hasClass('dropdown-submenu');
29320         
29321         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29322     },
29323     
29324     initEvents : function() 
29325     {
29326         if(this.isSubMenu){
29327             return;
29328         }
29329         
29330         this.hidden = true;
29331         
29332         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29333         this.triggerEl.on('click', this.onTriggerPress, this);
29334         
29335         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29336         this.buttonEl.on('click', this.onClick, this);
29337         
29338     },
29339     
29340     list : function()
29341     {
29342         if(this.isSubMenu){
29343             return this.el;
29344         }
29345         
29346         return this.el.select('ul.dropdown-menu', true).first();
29347     },
29348     
29349     onClick : function(e)
29350     {
29351         this.fireEvent("click", this, e);
29352     },
29353     
29354     onTriggerPress  : function(e)
29355     {   
29356         if (this.isVisible()) {
29357             this.hide();
29358         } else {
29359             this.show();
29360         }
29361     },
29362     
29363     isVisible : function(){
29364         return !this.hidden;
29365     },
29366     
29367     show : function()
29368     {
29369         this.fireEvent("beforeshow", this);
29370         
29371         this.hidden = false;
29372         this.el.addClass('open');
29373         
29374         Roo.get(document).on("mouseup", this.onMouseUp, this);
29375         
29376         this.fireEvent("show", this);
29377         
29378         
29379     },
29380     
29381     hide : function()
29382     {
29383         this.fireEvent("beforehide", this);
29384         
29385         this.hidden = true;
29386         this.el.removeClass('open');
29387         
29388         Roo.get(document).un("mouseup", this.onMouseUp);
29389         
29390         this.fireEvent("hide", this);
29391     },
29392     
29393     onMouseUp : function()
29394     {
29395         this.hide();
29396     }
29397     
29398 });
29399
29400  
29401  /*
29402  * - LGPL
29403  *
29404  * menu item
29405  * 
29406  */
29407 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29408
29409 /**
29410  * @class Roo.bootstrap.menu.Item
29411  * @extends Roo.bootstrap.Component
29412  * Bootstrap MenuItem class
29413  * @cfg {Boolean} submenu (true | false) default false
29414  * @cfg {String} html text of the item
29415  * @cfg {String} href the link
29416  * @cfg {Boolean} disable (true | false) default false
29417  * @cfg {Boolean} preventDefault (true | false) default true
29418  * @cfg {String} icon Font awesome icon
29419  * @cfg {String} pos Submenu align to (left | right) default right 
29420  * 
29421  * 
29422  * @constructor
29423  * Create a new Item
29424  * @param {Object} config The config object
29425  */
29426
29427
29428 Roo.bootstrap.menu.Item = function(config){
29429     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29430     this.addEvents({
29431         /**
29432          * @event mouseover
29433          * Fires when the mouse is hovering over this menu
29434          * @param {Roo.bootstrap.menu.Item} this
29435          * @param {Roo.EventObject} e
29436          */
29437         mouseover : true,
29438         /**
29439          * @event mouseout
29440          * Fires when the mouse exits this menu
29441          * @param {Roo.bootstrap.menu.Item} this
29442          * @param {Roo.EventObject} e
29443          */
29444         mouseout : true,
29445         // raw events
29446         /**
29447          * @event click
29448          * The raw click event for the entire grid.
29449          * @param {Roo.EventObject} e
29450          */
29451         click : true
29452     });
29453 };
29454
29455 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29456     
29457     submenu : false,
29458     href : '',
29459     html : '',
29460     preventDefault: true,
29461     disable : false,
29462     icon : false,
29463     pos : 'right',
29464     
29465     getAutoCreate : function()
29466     {
29467         var text = [
29468             {
29469                 tag : 'span',
29470                 cls : 'roo-menu-item-text',
29471                 html : this.html
29472             }
29473         ];
29474         
29475         if(this.icon){
29476             text.unshift({
29477                 tag : 'i',
29478                 cls : 'fa ' + this.icon
29479             })
29480         }
29481         
29482         var cfg = {
29483             tag : 'li',
29484             cn : [
29485                 {
29486                     tag : 'a',
29487                     href : this.href || '#',
29488                     cn : text
29489                 }
29490             ]
29491         };
29492         
29493         if(this.disable){
29494             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29495         }
29496         
29497         if(this.submenu){
29498             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29499             
29500             if(this.pos == 'left'){
29501                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29502             }
29503         }
29504         
29505         return cfg;
29506     },
29507     
29508     initEvents : function() 
29509     {
29510         this.el.on('mouseover', this.onMouseOver, this);
29511         this.el.on('mouseout', this.onMouseOut, this);
29512         
29513         this.el.select('a', true).first().on('click', this.onClick, this);
29514         
29515     },
29516     
29517     onClick : function(e)
29518     {
29519         if(this.preventDefault){
29520             e.preventDefault();
29521         }
29522         
29523         this.fireEvent("click", this, e);
29524     },
29525     
29526     onMouseOver : function(e)
29527     {
29528         if(this.submenu && this.pos == 'left'){
29529             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29530         }
29531         
29532         this.fireEvent("mouseover", this, e);
29533     },
29534     
29535     onMouseOut : function(e)
29536     {
29537         this.fireEvent("mouseout", this, e);
29538     }
29539 });
29540
29541  
29542
29543  /*
29544  * - LGPL
29545  *
29546  * menu separator
29547  * 
29548  */
29549 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29550
29551 /**
29552  * @class Roo.bootstrap.menu.Separator
29553  * @extends Roo.bootstrap.Component
29554  * Bootstrap Separator class
29555  * 
29556  * @constructor
29557  * Create a new Separator
29558  * @param {Object} config The config object
29559  */
29560
29561
29562 Roo.bootstrap.menu.Separator = function(config){
29563     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29564 };
29565
29566 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29567     
29568     getAutoCreate : function(){
29569         var cfg = {
29570             tag : 'li',
29571             cls: 'dropdown-divider divider'
29572         };
29573         
29574         return cfg;
29575     }
29576    
29577 });
29578
29579  
29580
29581  /*
29582  * - LGPL
29583  *
29584  * Tooltip
29585  * 
29586  */
29587
29588 /**
29589  * @class Roo.bootstrap.Tooltip
29590  * Bootstrap Tooltip class
29591  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29592  * to determine which dom element triggers the tooltip.
29593  * 
29594  * It needs to add support for additional attributes like tooltip-position
29595  * 
29596  * @constructor
29597  * Create a new Toolti
29598  * @param {Object} config The config object
29599  */
29600
29601 Roo.bootstrap.Tooltip = function(config){
29602     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29603     
29604     this.alignment = Roo.bootstrap.Tooltip.alignment;
29605     
29606     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29607         this.alignment = config.alignment;
29608     }
29609     
29610 };
29611
29612 Roo.apply(Roo.bootstrap.Tooltip, {
29613     /**
29614      * @function init initialize tooltip monitoring.
29615      * @static
29616      */
29617     currentEl : false,
29618     currentTip : false,
29619     currentRegion : false,
29620     
29621     //  init : delay?
29622     
29623     init : function()
29624     {
29625         Roo.get(document).on('mouseover', this.enter ,this);
29626         Roo.get(document).on('mouseout', this.leave, this);
29627          
29628         
29629         this.currentTip = new Roo.bootstrap.Tooltip();
29630     },
29631     
29632     enter : function(ev)
29633     {
29634         var dom = ev.getTarget();
29635         
29636         //Roo.log(['enter',dom]);
29637         var el = Roo.fly(dom);
29638         if (this.currentEl) {
29639             //Roo.log(dom);
29640             //Roo.log(this.currentEl);
29641             //Roo.log(this.currentEl.contains(dom));
29642             if (this.currentEl == el) {
29643                 return;
29644             }
29645             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29646                 return;
29647             }
29648
29649         }
29650         
29651         if (this.currentTip.el) {
29652             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29653         }    
29654         //Roo.log(ev);
29655         
29656         if(!el || el.dom == document){
29657             return;
29658         }
29659         
29660         var bindEl = el; 
29661         var pel = false;
29662         if (!el.attr('tooltip')) {
29663             pel = el.findParent("[tooltip]");
29664             if (pel) {
29665                 bindEl = Roo.get(pel);
29666             }
29667         }
29668         
29669        
29670         
29671         // you can not look for children, as if el is the body.. then everythign is the child..
29672         if (!pel && !el.attr('tooltip')) { //
29673             if (!el.select("[tooltip]").elements.length) {
29674                 return;
29675             }
29676             // is the mouse over this child...?
29677             bindEl = el.select("[tooltip]").first();
29678             var xy = ev.getXY();
29679             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29680                 //Roo.log("not in region.");
29681                 return;
29682             }
29683             //Roo.log("child element over..");
29684             
29685         }
29686         this.currentEl = el;
29687         this.currentTip.bind(bindEl);
29688         this.currentRegion = Roo.lib.Region.getRegion(dom);
29689         this.currentTip.enter();
29690         
29691     },
29692     leave : function(ev)
29693     {
29694         var dom = ev.getTarget();
29695         //Roo.log(['leave',dom]);
29696         if (!this.currentEl) {
29697             return;
29698         }
29699         
29700         
29701         if (dom != this.currentEl.dom) {
29702             return;
29703         }
29704         var xy = ev.getXY();
29705         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29706             return;
29707         }
29708         // only activate leave if mouse cursor is outside... bounding box..
29709         
29710         
29711         
29712         
29713         if (this.currentTip) {
29714             this.currentTip.leave();
29715         }
29716         //Roo.log('clear currentEl');
29717         this.currentEl = false;
29718         
29719         
29720     },
29721     alignment : {
29722         'left' : ['r-l', [-2,0], 'right'],
29723         'right' : ['l-r', [2,0], 'left'],
29724         'bottom' : ['t-b', [0,2], 'top'],
29725         'top' : [ 'b-t', [0,-2], 'bottom']
29726     }
29727     
29728 });
29729
29730
29731 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29732     
29733     
29734     bindEl : false,
29735     
29736     delay : null, // can be { show : 300 , hide: 500}
29737     
29738     timeout : null,
29739     
29740     hoverState : null, //???
29741     
29742     placement : 'bottom', 
29743     
29744     alignment : false,
29745     
29746     getAutoCreate : function(){
29747     
29748         var cfg = {
29749            cls : 'tooltip',   
29750            role : 'tooltip',
29751            cn : [
29752                 {
29753                     cls : 'tooltip-arrow arrow'
29754                 },
29755                 {
29756                     cls : 'tooltip-inner'
29757                 }
29758            ]
29759         };
29760         
29761         return cfg;
29762     },
29763     bind : function(el)
29764     {
29765         this.bindEl = el;
29766     },
29767     
29768     initEvents : function()
29769     {
29770         this.arrowEl = this.el.select('.arrow', true).first();
29771         this.innerEl = this.el.select('.tooltip-inner', true).first();
29772     },
29773     
29774     enter : function () {
29775        
29776         if (this.timeout != null) {
29777             clearTimeout(this.timeout);
29778         }
29779         
29780         this.hoverState = 'in';
29781          //Roo.log("enter - show");
29782         if (!this.delay || !this.delay.show) {
29783             this.show();
29784             return;
29785         }
29786         var _t = this;
29787         this.timeout = setTimeout(function () {
29788             if (_t.hoverState == 'in') {
29789                 _t.show();
29790             }
29791         }, this.delay.show);
29792     },
29793     leave : function()
29794     {
29795         clearTimeout(this.timeout);
29796     
29797         this.hoverState = 'out';
29798          if (!this.delay || !this.delay.hide) {
29799             this.hide();
29800             return;
29801         }
29802        
29803         var _t = this;
29804         this.timeout = setTimeout(function () {
29805             //Roo.log("leave - timeout");
29806             
29807             if (_t.hoverState == 'out') {
29808                 _t.hide();
29809                 Roo.bootstrap.Tooltip.currentEl = false;
29810             }
29811         }, delay);
29812     },
29813     
29814     show : function (msg)
29815     {
29816         if (!this.el) {
29817             this.render(document.body);
29818         }
29819         // set content.
29820         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29821         
29822         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29823         
29824         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29825         
29826         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29827                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29828         
29829         var placement = typeof this.placement == 'function' ?
29830             this.placement.call(this, this.el, on_el) :
29831             this.placement;
29832             
29833         var autoToken = /\s?auto?\s?/i;
29834         var autoPlace = autoToken.test(placement);
29835         if (autoPlace) {
29836             placement = placement.replace(autoToken, '') || 'top';
29837         }
29838         
29839         //this.el.detach()
29840         //this.el.setXY([0,0]);
29841         this.el.show();
29842         //this.el.dom.style.display='block';
29843         
29844         //this.el.appendTo(on_el);
29845         
29846         var p = this.getPosition();
29847         var box = this.el.getBox();
29848         
29849         if (autoPlace) {
29850             // fixme..
29851         }
29852         
29853         var align = this.alignment[placement];
29854         
29855         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29856         
29857         if(placement == 'top' || placement == 'bottom'){
29858             if(xy[0] < 0){
29859                 placement = 'right';
29860             }
29861             
29862             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29863                 placement = 'left';
29864             }
29865             
29866             var scroll = Roo.select('body', true).first().getScroll();
29867             
29868             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29869                 placement = 'top';
29870             }
29871             
29872             align = this.alignment[placement];
29873             
29874             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29875             
29876         }
29877         
29878         var elems = document.getElementsByTagName('div');
29879         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29880         for (var i = 0; i < elems.length; i++) {
29881           var zindex = Number.parseInt(
29882                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29883                 10
29884           );
29885           if (zindex > highest) {
29886             highest = zindex;
29887           }
29888         }
29889         
29890         
29891         
29892         this.el.dom.style.zIndex = highest;
29893         
29894         this.el.alignTo(this.bindEl, align[0],align[1]);
29895         //var arrow = this.el.select('.arrow',true).first();
29896         //arrow.set(align[2], 
29897         
29898         this.el.addClass(placement);
29899         this.el.addClass("bs-tooltip-"+ placement);
29900         
29901         this.el.addClass('in fade show');
29902         
29903         this.hoverState = null;
29904         
29905         if (this.el.hasClass('fade')) {
29906             // fade it?
29907         }
29908         
29909         
29910         
29911         
29912         
29913     },
29914     hide : function()
29915     {
29916          
29917         if (!this.el) {
29918             return;
29919         }
29920         //this.el.setXY([0,0]);
29921         this.el.removeClass(['show', 'in']);
29922         //this.el.hide();
29923         
29924     }
29925     
29926 });
29927  
29928
29929  /*
29930  * - LGPL
29931  *
29932  * Location Picker
29933  * 
29934  */
29935
29936 /**
29937  * @class Roo.bootstrap.LocationPicker
29938  * @extends Roo.bootstrap.Component
29939  * Bootstrap LocationPicker class
29940  * @cfg {Number} latitude Position when init default 0
29941  * @cfg {Number} longitude Position when init default 0
29942  * @cfg {Number} zoom default 15
29943  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29944  * @cfg {Boolean} mapTypeControl default false
29945  * @cfg {Boolean} disableDoubleClickZoom default false
29946  * @cfg {Boolean} scrollwheel default true
29947  * @cfg {Boolean} streetViewControl default false
29948  * @cfg {Number} radius default 0
29949  * @cfg {String} locationName
29950  * @cfg {Boolean} draggable default true
29951  * @cfg {Boolean} enableAutocomplete default false
29952  * @cfg {Boolean} enableReverseGeocode default true
29953  * @cfg {String} markerTitle
29954  * 
29955  * @constructor
29956  * Create a new LocationPicker
29957  * @param {Object} config The config object
29958  */
29959
29960
29961 Roo.bootstrap.LocationPicker = function(config){
29962     
29963     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29964     
29965     this.addEvents({
29966         /**
29967          * @event initial
29968          * Fires when the picker initialized.
29969          * @param {Roo.bootstrap.LocationPicker} this
29970          * @param {Google Location} location
29971          */
29972         initial : true,
29973         /**
29974          * @event positionchanged
29975          * Fires when the picker position changed.
29976          * @param {Roo.bootstrap.LocationPicker} this
29977          * @param {Google Location} location
29978          */
29979         positionchanged : true,
29980         /**
29981          * @event resize
29982          * Fires when the map resize.
29983          * @param {Roo.bootstrap.LocationPicker} this
29984          */
29985         resize : true,
29986         /**
29987          * @event show
29988          * Fires when the map show.
29989          * @param {Roo.bootstrap.LocationPicker} this
29990          */
29991         show : true,
29992         /**
29993          * @event hide
29994          * Fires when the map hide.
29995          * @param {Roo.bootstrap.LocationPicker} this
29996          */
29997         hide : true,
29998         /**
29999          * @event mapClick
30000          * Fires when click the map.
30001          * @param {Roo.bootstrap.LocationPicker} this
30002          * @param {Map event} e
30003          */
30004         mapClick : true,
30005         /**
30006          * @event mapRightClick
30007          * Fires when right click the map.
30008          * @param {Roo.bootstrap.LocationPicker} this
30009          * @param {Map event} e
30010          */
30011         mapRightClick : true,
30012         /**
30013          * @event markerClick
30014          * Fires when click the marker.
30015          * @param {Roo.bootstrap.LocationPicker} this
30016          * @param {Map event} e
30017          */
30018         markerClick : true,
30019         /**
30020          * @event markerRightClick
30021          * Fires when right click the marker.
30022          * @param {Roo.bootstrap.LocationPicker} this
30023          * @param {Map event} e
30024          */
30025         markerRightClick : true,
30026         /**
30027          * @event OverlayViewDraw
30028          * Fires when OverlayView Draw
30029          * @param {Roo.bootstrap.LocationPicker} this
30030          */
30031         OverlayViewDraw : true,
30032         /**
30033          * @event OverlayViewOnAdd
30034          * Fires when OverlayView Draw
30035          * @param {Roo.bootstrap.LocationPicker} this
30036          */
30037         OverlayViewOnAdd : true,
30038         /**
30039          * @event OverlayViewOnRemove
30040          * Fires when OverlayView Draw
30041          * @param {Roo.bootstrap.LocationPicker} this
30042          */
30043         OverlayViewOnRemove : true,
30044         /**
30045          * @event OverlayViewShow
30046          * Fires when OverlayView Draw
30047          * @param {Roo.bootstrap.LocationPicker} this
30048          * @param {Pixel} cpx
30049          */
30050         OverlayViewShow : true,
30051         /**
30052          * @event OverlayViewHide
30053          * Fires when OverlayView Draw
30054          * @param {Roo.bootstrap.LocationPicker} this
30055          */
30056         OverlayViewHide : true,
30057         /**
30058          * @event loadexception
30059          * Fires when load google lib failed.
30060          * @param {Roo.bootstrap.LocationPicker} this
30061          */
30062         loadexception : true
30063     });
30064         
30065 };
30066
30067 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30068     
30069     gMapContext: false,
30070     
30071     latitude: 0,
30072     longitude: 0,
30073     zoom: 15,
30074     mapTypeId: false,
30075     mapTypeControl: false,
30076     disableDoubleClickZoom: false,
30077     scrollwheel: true,
30078     streetViewControl: false,
30079     radius: 0,
30080     locationName: '',
30081     draggable: true,
30082     enableAutocomplete: false,
30083     enableReverseGeocode: true,
30084     markerTitle: '',
30085     
30086     getAutoCreate: function()
30087     {
30088
30089         var cfg = {
30090             tag: 'div',
30091             cls: 'roo-location-picker'
30092         };
30093         
30094         return cfg
30095     },
30096     
30097     initEvents: function(ct, position)
30098     {       
30099         if(!this.el.getWidth() || this.isApplied()){
30100             return;
30101         }
30102         
30103         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30104         
30105         this.initial();
30106     },
30107     
30108     initial: function()
30109     {
30110         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30111             this.fireEvent('loadexception', this);
30112             return;
30113         }
30114         
30115         if(!this.mapTypeId){
30116             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30117         }
30118         
30119         this.gMapContext = this.GMapContext();
30120         
30121         this.initOverlayView();
30122         
30123         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30124         
30125         var _this = this;
30126                 
30127         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30128             _this.setPosition(_this.gMapContext.marker.position);
30129         });
30130         
30131         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30132             _this.fireEvent('mapClick', this, event);
30133             
30134         });
30135
30136         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30137             _this.fireEvent('mapRightClick', this, event);
30138             
30139         });
30140         
30141         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30142             _this.fireEvent('markerClick', this, event);
30143             
30144         });
30145
30146         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30147             _this.fireEvent('markerRightClick', this, event);
30148             
30149         });
30150         
30151         this.setPosition(this.gMapContext.location);
30152         
30153         this.fireEvent('initial', this, this.gMapContext.location);
30154     },
30155     
30156     initOverlayView: function()
30157     {
30158         var _this = this;
30159         
30160         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30161             
30162             draw: function()
30163             {
30164                 _this.fireEvent('OverlayViewDraw', _this);
30165             },
30166             
30167             onAdd: function()
30168             {
30169                 _this.fireEvent('OverlayViewOnAdd', _this);
30170             },
30171             
30172             onRemove: function()
30173             {
30174                 _this.fireEvent('OverlayViewOnRemove', _this);
30175             },
30176             
30177             show: function(cpx)
30178             {
30179                 _this.fireEvent('OverlayViewShow', _this, cpx);
30180             },
30181             
30182             hide: function()
30183             {
30184                 _this.fireEvent('OverlayViewHide', _this);
30185             }
30186             
30187         });
30188     },
30189     
30190     fromLatLngToContainerPixel: function(event)
30191     {
30192         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30193     },
30194     
30195     isApplied: function() 
30196     {
30197         return this.getGmapContext() == false ? false : true;
30198     },
30199     
30200     getGmapContext: function() 
30201     {
30202         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30203     },
30204     
30205     GMapContext: function() 
30206     {
30207         var position = new google.maps.LatLng(this.latitude, this.longitude);
30208         
30209         var _map = new google.maps.Map(this.el.dom, {
30210             center: position,
30211             zoom: this.zoom,
30212             mapTypeId: this.mapTypeId,
30213             mapTypeControl: this.mapTypeControl,
30214             disableDoubleClickZoom: this.disableDoubleClickZoom,
30215             scrollwheel: this.scrollwheel,
30216             streetViewControl: this.streetViewControl,
30217             locationName: this.locationName,
30218             draggable: this.draggable,
30219             enableAutocomplete: this.enableAutocomplete,
30220             enableReverseGeocode: this.enableReverseGeocode
30221         });
30222         
30223         var _marker = new google.maps.Marker({
30224             position: position,
30225             map: _map,
30226             title: this.markerTitle,
30227             draggable: this.draggable
30228         });
30229         
30230         return {
30231             map: _map,
30232             marker: _marker,
30233             circle: null,
30234             location: position,
30235             radius: this.radius,
30236             locationName: this.locationName,
30237             addressComponents: {
30238                 formatted_address: null,
30239                 addressLine1: null,
30240                 addressLine2: null,
30241                 streetName: null,
30242                 streetNumber: null,
30243                 city: null,
30244                 district: null,
30245                 state: null,
30246                 stateOrProvince: null
30247             },
30248             settings: this,
30249             domContainer: this.el.dom,
30250             geodecoder: new google.maps.Geocoder()
30251         };
30252     },
30253     
30254     drawCircle: function(center, radius, options) 
30255     {
30256         if (this.gMapContext.circle != null) {
30257             this.gMapContext.circle.setMap(null);
30258         }
30259         if (radius > 0) {
30260             radius *= 1;
30261             options = Roo.apply({}, options, {
30262                 strokeColor: "#0000FF",
30263                 strokeOpacity: .35,
30264                 strokeWeight: 2,
30265                 fillColor: "#0000FF",
30266                 fillOpacity: .2
30267             });
30268             
30269             options.map = this.gMapContext.map;
30270             options.radius = radius;
30271             options.center = center;
30272             this.gMapContext.circle = new google.maps.Circle(options);
30273             return this.gMapContext.circle;
30274         }
30275         
30276         return null;
30277     },
30278     
30279     setPosition: function(location) 
30280     {
30281         this.gMapContext.location = location;
30282         this.gMapContext.marker.setPosition(location);
30283         this.gMapContext.map.panTo(location);
30284         this.drawCircle(location, this.gMapContext.radius, {});
30285         
30286         var _this = this;
30287         
30288         if (this.gMapContext.settings.enableReverseGeocode) {
30289             this.gMapContext.geodecoder.geocode({
30290                 latLng: this.gMapContext.location
30291             }, function(results, status) {
30292                 
30293                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30294                     _this.gMapContext.locationName = results[0].formatted_address;
30295                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30296                     
30297                     _this.fireEvent('positionchanged', this, location);
30298                 }
30299             });
30300             
30301             return;
30302         }
30303         
30304         this.fireEvent('positionchanged', this, location);
30305     },
30306     
30307     resize: function()
30308     {
30309         google.maps.event.trigger(this.gMapContext.map, "resize");
30310         
30311         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30312         
30313         this.fireEvent('resize', this);
30314     },
30315     
30316     setPositionByLatLng: function(latitude, longitude)
30317     {
30318         this.setPosition(new google.maps.LatLng(latitude, longitude));
30319     },
30320     
30321     getCurrentPosition: function() 
30322     {
30323         return {
30324             latitude: this.gMapContext.location.lat(),
30325             longitude: this.gMapContext.location.lng()
30326         };
30327     },
30328     
30329     getAddressName: function() 
30330     {
30331         return this.gMapContext.locationName;
30332     },
30333     
30334     getAddressComponents: function() 
30335     {
30336         return this.gMapContext.addressComponents;
30337     },
30338     
30339     address_component_from_google_geocode: function(address_components) 
30340     {
30341         var result = {};
30342         
30343         for (var i = 0; i < address_components.length; i++) {
30344             var component = address_components[i];
30345             if (component.types.indexOf("postal_code") >= 0) {
30346                 result.postalCode = component.short_name;
30347             } else if (component.types.indexOf("street_number") >= 0) {
30348                 result.streetNumber = component.short_name;
30349             } else if (component.types.indexOf("route") >= 0) {
30350                 result.streetName = component.short_name;
30351             } else if (component.types.indexOf("neighborhood") >= 0) {
30352                 result.city = component.short_name;
30353             } else if (component.types.indexOf("locality") >= 0) {
30354                 result.city = component.short_name;
30355             } else if (component.types.indexOf("sublocality") >= 0) {
30356                 result.district = component.short_name;
30357             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30358                 result.stateOrProvince = component.short_name;
30359             } else if (component.types.indexOf("country") >= 0) {
30360                 result.country = component.short_name;
30361             }
30362         }
30363         
30364         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30365         result.addressLine2 = "";
30366         return result;
30367     },
30368     
30369     setZoomLevel: function(zoom)
30370     {
30371         this.gMapContext.map.setZoom(zoom);
30372     },
30373     
30374     show: function()
30375     {
30376         if(!this.el){
30377             return;
30378         }
30379         
30380         this.el.show();
30381         
30382         this.resize();
30383         
30384         this.fireEvent('show', this);
30385     },
30386     
30387     hide: function()
30388     {
30389         if(!this.el){
30390             return;
30391         }
30392         
30393         this.el.hide();
30394         
30395         this.fireEvent('hide', this);
30396     }
30397     
30398 });
30399
30400 Roo.apply(Roo.bootstrap.LocationPicker, {
30401     
30402     OverlayView : function(map, options)
30403     {
30404         options = options || {};
30405         
30406         this.setMap(map);
30407     }
30408     
30409     
30410 });/**
30411  * @class Roo.bootstrap.Alert
30412  * @extends Roo.bootstrap.Component
30413  * Bootstrap Alert class - shows an alert area box
30414  * eg
30415  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30416   Enter a valid email address
30417 </div>
30418  * @licence LGPL
30419  * @cfg {String} title The title of alert
30420  * @cfg {String} html The content of alert
30421  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30422  * @cfg {String} fa font-awesomeicon
30423  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30424  * @cfg {Boolean} close true to show a x closer
30425  * 
30426  * 
30427  * @constructor
30428  * Create a new alert
30429  * @param {Object} config The config object
30430  */
30431
30432
30433 Roo.bootstrap.Alert = function(config){
30434     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30435     
30436 };
30437
30438 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30439     
30440     title: '',
30441     html: '',
30442     weight: false,
30443     fa: false,
30444     faicon: false, // BC
30445     close : false,
30446     
30447     
30448     getAutoCreate : function()
30449     {
30450         
30451         var cfg = {
30452             tag : 'div',
30453             cls : 'alert',
30454             cn : [
30455                 {
30456                     tag: 'button',
30457                     type :  "button",
30458                     cls: "close",
30459                     html : '×',
30460                     style : this.close ? '' : 'display:none'
30461                 },
30462                 {
30463                     tag : 'i',
30464                     cls : 'roo-alert-icon'
30465                     
30466                 },
30467                 {
30468                     tag : 'b',
30469                     cls : 'roo-alert-title',
30470                     html : this.title
30471                 },
30472                 {
30473                     tag : 'span',
30474                     cls : 'roo-alert-text',
30475                     html : this.html
30476                 }
30477             ]
30478         };
30479         
30480         if(this.faicon){
30481             cfg.cn[0].cls += ' fa ' + this.faicon;
30482         }
30483         if(this.fa){
30484             cfg.cn[0].cls += ' fa ' + this.fa;
30485         }
30486         
30487         if(this.weight){
30488             cfg.cls += ' alert-' + this.weight;
30489         }
30490         
30491         return cfg;
30492     },
30493     
30494     initEvents: function() 
30495     {
30496         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30497         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30498         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30499         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30500         if (this.seconds > 0) {
30501             this.hide.defer(this.seconds, this);
30502         }
30503     },
30504     /**
30505      * Set the Title Message HTML
30506      * @param {String} html
30507      */
30508     setTitle : function(str)
30509     {
30510         this.titleEl.dom.innerHTML = str;
30511     },
30512      
30513      /**
30514      * Set the Body Message HTML
30515      * @param {String} html
30516      */
30517     setHtml : function(str)
30518     {
30519         this.htmlEl.dom.innerHTML = str;
30520     },
30521     /**
30522      * Set the Weight of the alert
30523      * @param {String} (success|info|warning|danger) weight
30524      */
30525     
30526     setWeight : function(weight)
30527     {
30528         if(this.weight){
30529             this.el.removeClass('alert-' + this.weight);
30530         }
30531         
30532         this.weight = weight;
30533         
30534         this.el.addClass('alert-' + this.weight);
30535     },
30536       /**
30537      * Set the Icon of the alert
30538      * @param {String} see fontawsome names (name without the 'fa-' bit)
30539      */
30540     setIcon : function(icon)
30541     {
30542         if(this.faicon){
30543             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30544         }
30545         
30546         this.faicon = icon;
30547         
30548         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30549     },
30550     /**
30551      * Hide the Alert
30552      */
30553     hide: function() 
30554     {
30555         this.el.hide();   
30556     },
30557     /**
30558      * Show the Alert
30559      */
30560     show: function() 
30561     {  
30562         this.el.show();   
30563     }
30564     
30565 });
30566
30567  
30568 /*
30569 * Licence: LGPL
30570 */
30571
30572 /**
30573  * @class Roo.bootstrap.UploadCropbox
30574  * @extends Roo.bootstrap.Component
30575  * Bootstrap UploadCropbox class
30576  * @cfg {String} emptyText show when image has been loaded
30577  * @cfg {String} rotateNotify show when image too small to rotate
30578  * @cfg {Number} errorTimeout default 3000
30579  * @cfg {Number} minWidth default 300
30580  * @cfg {Number} minHeight default 300
30581  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30582  * @cfg {Boolean} isDocument (true|false) default false
30583  * @cfg {String} url action url
30584  * @cfg {String} paramName default 'imageUpload'
30585  * @cfg {String} method default POST
30586  * @cfg {Boolean} loadMask (true|false) default true
30587  * @cfg {Boolean} loadingText default 'Loading...'
30588  * 
30589  * @constructor
30590  * Create a new UploadCropbox
30591  * @param {Object} config The config object
30592  */
30593
30594 Roo.bootstrap.UploadCropbox = function(config){
30595     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30596     
30597     this.addEvents({
30598         /**
30599          * @event beforeselectfile
30600          * Fire before select file
30601          * @param {Roo.bootstrap.UploadCropbox} this
30602          */
30603         "beforeselectfile" : true,
30604         /**
30605          * @event initial
30606          * Fire after initEvent
30607          * @param {Roo.bootstrap.UploadCropbox} this
30608          */
30609         "initial" : true,
30610         /**
30611          * @event crop
30612          * Fire after initEvent
30613          * @param {Roo.bootstrap.UploadCropbox} this
30614          * @param {String} data
30615          */
30616         "crop" : true,
30617         /**
30618          * @event prepare
30619          * Fire when preparing the file data
30620          * @param {Roo.bootstrap.UploadCropbox} this
30621          * @param {Object} file
30622          */
30623         "prepare" : true,
30624         /**
30625          * @event exception
30626          * Fire when get exception
30627          * @param {Roo.bootstrap.UploadCropbox} this
30628          * @param {XMLHttpRequest} xhr
30629          */
30630         "exception" : true,
30631         /**
30632          * @event beforeloadcanvas
30633          * Fire before load the canvas
30634          * @param {Roo.bootstrap.UploadCropbox} this
30635          * @param {String} src
30636          */
30637         "beforeloadcanvas" : true,
30638         /**
30639          * @event trash
30640          * Fire when trash image
30641          * @param {Roo.bootstrap.UploadCropbox} this
30642          */
30643         "trash" : true,
30644         /**
30645          * @event download
30646          * Fire when download the image
30647          * @param {Roo.bootstrap.UploadCropbox} this
30648          */
30649         "download" : true,
30650         /**
30651          * @event footerbuttonclick
30652          * Fire when footerbuttonclick
30653          * @param {Roo.bootstrap.UploadCropbox} this
30654          * @param {String} type
30655          */
30656         "footerbuttonclick" : true,
30657         /**
30658          * @event resize
30659          * Fire when resize
30660          * @param {Roo.bootstrap.UploadCropbox} this
30661          */
30662         "resize" : true,
30663         /**
30664          * @event rotate
30665          * Fire when rotate the image
30666          * @param {Roo.bootstrap.UploadCropbox} this
30667          * @param {String} pos
30668          */
30669         "rotate" : true,
30670         /**
30671          * @event inspect
30672          * Fire when inspect the file
30673          * @param {Roo.bootstrap.UploadCropbox} this
30674          * @param {Object} file
30675          */
30676         "inspect" : true,
30677         /**
30678          * @event upload
30679          * Fire when xhr upload the file
30680          * @param {Roo.bootstrap.UploadCropbox} this
30681          * @param {Object} data
30682          */
30683         "upload" : true,
30684         /**
30685          * @event arrange
30686          * Fire when arrange the file data
30687          * @param {Roo.bootstrap.UploadCropbox} this
30688          * @param {Object} formData
30689          */
30690         "arrange" : true
30691     });
30692     
30693     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30694 };
30695
30696 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30697     
30698     emptyText : 'Click to upload image',
30699     rotateNotify : 'Image is too small to rotate',
30700     errorTimeout : 3000,
30701     scale : 0,
30702     baseScale : 1,
30703     rotate : 0,
30704     dragable : false,
30705     pinching : false,
30706     mouseX : 0,
30707     mouseY : 0,
30708     cropData : false,
30709     minWidth : 300,
30710     minHeight : 300,
30711     file : false,
30712     exif : {},
30713     baseRotate : 1,
30714     cropType : 'image/jpeg',
30715     buttons : false,
30716     canvasLoaded : false,
30717     isDocument : false,
30718     method : 'POST',
30719     paramName : 'imageUpload',
30720     loadMask : true,
30721     loadingText : 'Loading...',
30722     maskEl : false,
30723     
30724     getAutoCreate : function()
30725     {
30726         var cfg = {
30727             tag : 'div',
30728             cls : 'roo-upload-cropbox',
30729             cn : [
30730                 {
30731                     tag : 'input',
30732                     cls : 'roo-upload-cropbox-selector',
30733                     type : 'file'
30734                 },
30735                 {
30736                     tag : 'div',
30737                     cls : 'roo-upload-cropbox-body',
30738                     style : 'cursor:pointer',
30739                     cn : [
30740                         {
30741                             tag : 'div',
30742                             cls : 'roo-upload-cropbox-preview'
30743                         },
30744                         {
30745                             tag : 'div',
30746                             cls : 'roo-upload-cropbox-thumb'
30747                         },
30748                         {
30749                             tag : 'div',
30750                             cls : 'roo-upload-cropbox-empty-notify',
30751                             html : this.emptyText
30752                         },
30753                         {
30754                             tag : 'div',
30755                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30756                             html : this.rotateNotify
30757                         }
30758                     ]
30759                 },
30760                 {
30761                     tag : 'div',
30762                     cls : 'roo-upload-cropbox-footer',
30763                     cn : {
30764                         tag : 'div',
30765                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30766                         cn : []
30767                     }
30768                 }
30769             ]
30770         };
30771         
30772         return cfg;
30773     },
30774     
30775     onRender : function(ct, position)
30776     {
30777         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30778         
30779         if (this.buttons.length) {
30780             
30781             Roo.each(this.buttons, function(bb) {
30782                 
30783                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30784                 
30785                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30786                 
30787             }, this);
30788         }
30789         
30790         if(this.loadMask){
30791             this.maskEl = this.el;
30792         }
30793     },
30794     
30795     initEvents : function()
30796     {
30797         this.urlAPI = (window.createObjectURL && window) || 
30798                                 (window.URL && URL.revokeObjectURL && URL) || 
30799                                 (window.webkitURL && webkitURL);
30800                         
30801         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30802         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30803         
30804         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30805         this.selectorEl.hide();
30806         
30807         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30808         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30809         
30810         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30811         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30812         this.thumbEl.hide();
30813         
30814         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30815         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30816         
30817         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30818         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30819         this.errorEl.hide();
30820         
30821         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30822         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30823         this.footerEl.hide();
30824         
30825         this.setThumbBoxSize();
30826         
30827         this.bind();
30828         
30829         this.resize();
30830         
30831         this.fireEvent('initial', this);
30832     },
30833
30834     bind : function()
30835     {
30836         var _this = this;
30837         
30838         window.addEventListener("resize", function() { _this.resize(); } );
30839         
30840         this.bodyEl.on('click', this.beforeSelectFile, this);
30841         
30842         if(Roo.isTouch){
30843             this.bodyEl.on('touchstart', this.onTouchStart, this);
30844             this.bodyEl.on('touchmove', this.onTouchMove, this);
30845             this.bodyEl.on('touchend', this.onTouchEnd, this);
30846         }
30847         
30848         if(!Roo.isTouch){
30849             this.bodyEl.on('mousedown', this.onMouseDown, this);
30850             this.bodyEl.on('mousemove', this.onMouseMove, this);
30851             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30852             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30853             Roo.get(document).on('mouseup', this.onMouseUp, this);
30854         }
30855         
30856         this.selectorEl.on('change', this.onFileSelected, this);
30857     },
30858     
30859     reset : function()
30860     {    
30861         this.scale = 0;
30862         this.baseScale = 1;
30863         this.rotate = 0;
30864         this.baseRotate = 1;
30865         this.dragable = false;
30866         this.pinching = false;
30867         this.mouseX = 0;
30868         this.mouseY = 0;
30869         this.cropData = false;
30870         this.notifyEl.dom.innerHTML = this.emptyText;
30871         
30872         this.selectorEl.dom.value = '';
30873         
30874     },
30875     
30876     resize : function()
30877     {
30878         if(this.fireEvent('resize', this) != false){
30879             this.setThumbBoxPosition();
30880             this.setCanvasPosition();
30881         }
30882     },
30883     
30884     onFooterButtonClick : function(e, el, o, type)
30885     {
30886         switch (type) {
30887             case 'rotate-left' :
30888                 this.onRotateLeft(e);
30889                 break;
30890             case 'rotate-right' :
30891                 this.onRotateRight(e);
30892                 break;
30893             case 'picture' :
30894                 this.beforeSelectFile(e);
30895                 break;
30896             case 'trash' :
30897                 this.trash(e);
30898                 break;
30899             case 'crop' :
30900                 this.crop(e);
30901                 break;
30902             case 'download' :
30903                 this.download(e);
30904                 break;
30905             default :
30906                 break;
30907         }
30908         
30909         this.fireEvent('footerbuttonclick', this, type);
30910     },
30911     
30912     beforeSelectFile : function(e)
30913     {
30914         e.preventDefault();
30915         
30916         if(this.fireEvent('beforeselectfile', this) != false){
30917             this.selectorEl.dom.click();
30918         }
30919     },
30920     
30921     onFileSelected : function(e)
30922     {
30923         e.preventDefault();
30924         
30925         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30926             return;
30927         }
30928         
30929         var file = this.selectorEl.dom.files[0];
30930         
30931         if(this.fireEvent('inspect', this, file) != false){
30932             this.prepare(file);
30933         }
30934         
30935     },
30936     
30937     trash : function(e)
30938     {
30939         this.fireEvent('trash', this);
30940     },
30941     
30942     download : function(e)
30943     {
30944         this.fireEvent('download', this);
30945     },
30946     
30947     loadCanvas : function(src)
30948     {   
30949         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30950             
30951             this.reset();
30952             
30953             this.imageEl = document.createElement('img');
30954             
30955             var _this = this;
30956             
30957             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30958             
30959             this.imageEl.src = src;
30960         }
30961     },
30962     
30963     onLoadCanvas : function()
30964     {   
30965         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30966         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30967         
30968         this.bodyEl.un('click', this.beforeSelectFile, this);
30969         
30970         this.notifyEl.hide();
30971         this.thumbEl.show();
30972         this.footerEl.show();
30973         
30974         this.baseRotateLevel();
30975         
30976         if(this.isDocument){
30977             this.setThumbBoxSize();
30978         }
30979         
30980         this.setThumbBoxPosition();
30981         
30982         this.baseScaleLevel();
30983         
30984         this.draw();
30985         
30986         this.resize();
30987         
30988         this.canvasLoaded = true;
30989         
30990         if(this.loadMask){
30991             this.maskEl.unmask();
30992         }
30993         
30994     },
30995     
30996     setCanvasPosition : function()
30997     {   
30998         if(!this.canvasEl){
30999             return;
31000         }
31001         
31002         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31003         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31004         
31005         this.previewEl.setLeft(pw);
31006         this.previewEl.setTop(ph);
31007         
31008     },
31009     
31010     onMouseDown : function(e)
31011     {   
31012         e.stopEvent();
31013         
31014         this.dragable = true;
31015         this.pinching = false;
31016         
31017         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31018             this.dragable = false;
31019             return;
31020         }
31021         
31022         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31023         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31024         
31025     },
31026     
31027     onMouseMove : function(e)
31028     {   
31029         e.stopEvent();
31030         
31031         if(!this.canvasLoaded){
31032             return;
31033         }
31034         
31035         if (!this.dragable){
31036             return;
31037         }
31038         
31039         var minX = Math.ceil(this.thumbEl.getLeft(true));
31040         var minY = Math.ceil(this.thumbEl.getTop(true));
31041         
31042         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31043         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31044         
31045         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31046         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31047         
31048         x = x - this.mouseX;
31049         y = y - this.mouseY;
31050         
31051         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31052         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31053         
31054         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31055         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31056         
31057         this.previewEl.setLeft(bgX);
31058         this.previewEl.setTop(bgY);
31059         
31060         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31061         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31062     },
31063     
31064     onMouseUp : function(e)
31065     {   
31066         e.stopEvent();
31067         
31068         this.dragable = false;
31069     },
31070     
31071     onMouseWheel : function(e)
31072     {   
31073         e.stopEvent();
31074         
31075         this.startScale = this.scale;
31076         
31077         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31078         
31079         if(!this.zoomable()){
31080             this.scale = this.startScale;
31081             return;
31082         }
31083         
31084         this.draw();
31085         
31086         return;
31087     },
31088     
31089     zoomable : function()
31090     {
31091         var minScale = this.thumbEl.getWidth() / this.minWidth;
31092         
31093         if(this.minWidth < this.minHeight){
31094             minScale = this.thumbEl.getHeight() / this.minHeight;
31095         }
31096         
31097         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31098         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31099         
31100         if(
31101                 this.isDocument &&
31102                 (this.rotate == 0 || this.rotate == 180) && 
31103                 (
31104                     width > this.imageEl.OriginWidth || 
31105                     height > this.imageEl.OriginHeight ||
31106                     (width < this.minWidth && height < this.minHeight)
31107                 )
31108         ){
31109             return false;
31110         }
31111         
31112         if(
31113                 this.isDocument &&
31114                 (this.rotate == 90 || this.rotate == 270) && 
31115                 (
31116                     width > this.imageEl.OriginWidth || 
31117                     height > this.imageEl.OriginHeight ||
31118                     (width < this.minHeight && height < this.minWidth)
31119                 )
31120         ){
31121             return false;
31122         }
31123         
31124         if(
31125                 !this.isDocument &&
31126                 (this.rotate == 0 || this.rotate == 180) && 
31127                 (
31128                     width < this.minWidth || 
31129                     width > this.imageEl.OriginWidth || 
31130                     height < this.minHeight || 
31131                     height > this.imageEl.OriginHeight
31132                 )
31133         ){
31134             return false;
31135         }
31136         
31137         if(
31138                 !this.isDocument &&
31139                 (this.rotate == 90 || this.rotate == 270) && 
31140                 (
31141                     width < this.minHeight || 
31142                     width > this.imageEl.OriginWidth || 
31143                     height < this.minWidth || 
31144                     height > this.imageEl.OriginHeight
31145                 )
31146         ){
31147             return false;
31148         }
31149         
31150         return true;
31151         
31152     },
31153     
31154     onRotateLeft : function(e)
31155     {   
31156         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31157             
31158             var minScale = this.thumbEl.getWidth() / this.minWidth;
31159             
31160             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31161             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31162             
31163             this.startScale = this.scale;
31164             
31165             while (this.getScaleLevel() < minScale){
31166             
31167                 this.scale = this.scale + 1;
31168                 
31169                 if(!this.zoomable()){
31170                     break;
31171                 }
31172                 
31173                 if(
31174                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31175                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31176                 ){
31177                     continue;
31178                 }
31179                 
31180                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31181
31182                 this.draw();
31183                 
31184                 return;
31185             }
31186             
31187             this.scale = this.startScale;
31188             
31189             this.onRotateFail();
31190             
31191             return false;
31192         }
31193         
31194         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31195
31196         if(this.isDocument){
31197             this.setThumbBoxSize();
31198             this.setThumbBoxPosition();
31199             this.setCanvasPosition();
31200         }
31201         
31202         this.draw();
31203         
31204         this.fireEvent('rotate', this, 'left');
31205         
31206     },
31207     
31208     onRotateRight : function(e)
31209     {
31210         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31211             
31212             var minScale = this.thumbEl.getWidth() / this.minWidth;
31213         
31214             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31215             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31216             
31217             this.startScale = this.scale;
31218             
31219             while (this.getScaleLevel() < minScale){
31220             
31221                 this.scale = this.scale + 1;
31222                 
31223                 if(!this.zoomable()){
31224                     break;
31225                 }
31226                 
31227                 if(
31228                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31229                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31230                 ){
31231                     continue;
31232                 }
31233                 
31234                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31235
31236                 this.draw();
31237                 
31238                 return;
31239             }
31240             
31241             this.scale = this.startScale;
31242             
31243             this.onRotateFail();
31244             
31245             return false;
31246         }
31247         
31248         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31249
31250         if(this.isDocument){
31251             this.setThumbBoxSize();
31252             this.setThumbBoxPosition();
31253             this.setCanvasPosition();
31254         }
31255         
31256         this.draw();
31257         
31258         this.fireEvent('rotate', this, 'right');
31259     },
31260     
31261     onRotateFail : function()
31262     {
31263         this.errorEl.show(true);
31264         
31265         var _this = this;
31266         
31267         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31268     },
31269     
31270     draw : function()
31271     {
31272         this.previewEl.dom.innerHTML = '';
31273         
31274         var canvasEl = document.createElement("canvas");
31275         
31276         var contextEl = canvasEl.getContext("2d");
31277         
31278         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31279         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31280         var center = this.imageEl.OriginWidth / 2;
31281         
31282         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31283             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31284             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31285             center = this.imageEl.OriginHeight / 2;
31286         }
31287         
31288         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31289         
31290         contextEl.translate(center, center);
31291         contextEl.rotate(this.rotate * Math.PI / 180);
31292
31293         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31294         
31295         this.canvasEl = document.createElement("canvas");
31296         
31297         this.contextEl = this.canvasEl.getContext("2d");
31298         
31299         switch (this.rotate) {
31300             case 0 :
31301                 
31302                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31303                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31304                 
31305                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31306                 
31307                 break;
31308             case 90 : 
31309                 
31310                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31311                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31312                 
31313                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31314                     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);
31315                     break;
31316                 }
31317                 
31318                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31319                 
31320                 break;
31321             case 180 :
31322                 
31323                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31324                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31325                 
31326                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31327                     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);
31328                     break;
31329                 }
31330                 
31331                 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);
31332                 
31333                 break;
31334             case 270 :
31335                 
31336                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31337                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31338         
31339                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31340                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31341                     break;
31342                 }
31343                 
31344                 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);
31345                 
31346                 break;
31347             default : 
31348                 break;
31349         }
31350         
31351         this.previewEl.appendChild(this.canvasEl);
31352         
31353         this.setCanvasPosition();
31354     },
31355     
31356     crop : function()
31357     {
31358         if(!this.canvasLoaded){
31359             return;
31360         }
31361         
31362         var imageCanvas = document.createElement("canvas");
31363         
31364         var imageContext = imageCanvas.getContext("2d");
31365         
31366         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31367         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31368         
31369         var center = imageCanvas.width / 2;
31370         
31371         imageContext.translate(center, center);
31372         
31373         imageContext.rotate(this.rotate * Math.PI / 180);
31374         
31375         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31376         
31377         var canvas = document.createElement("canvas");
31378         
31379         var context = canvas.getContext("2d");
31380                 
31381         canvas.width = this.minWidth;
31382         canvas.height = this.minHeight;
31383
31384         switch (this.rotate) {
31385             case 0 :
31386                 
31387                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31388                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31389                 
31390                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31391                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31392                 
31393                 var targetWidth = this.minWidth - 2 * x;
31394                 var targetHeight = this.minHeight - 2 * y;
31395                 
31396                 var scale = 1;
31397                 
31398                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31399                     scale = targetWidth / width;
31400                 }
31401                 
31402                 if(x > 0 && y == 0){
31403                     scale = targetHeight / height;
31404                 }
31405                 
31406                 if(x > 0 && y > 0){
31407                     scale = targetWidth / width;
31408                     
31409                     if(width < height){
31410                         scale = targetHeight / height;
31411                     }
31412                 }
31413                 
31414                 context.scale(scale, scale);
31415                 
31416                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31417                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31418
31419                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31420                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31421
31422                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31423                 
31424                 break;
31425             case 90 : 
31426                 
31427                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31428                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31429                 
31430                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31431                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31432                 
31433                 var targetWidth = this.minWidth - 2 * x;
31434                 var targetHeight = this.minHeight - 2 * y;
31435                 
31436                 var scale = 1;
31437                 
31438                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31439                     scale = targetWidth / width;
31440                 }
31441                 
31442                 if(x > 0 && y == 0){
31443                     scale = targetHeight / height;
31444                 }
31445                 
31446                 if(x > 0 && y > 0){
31447                     scale = targetWidth / width;
31448                     
31449                     if(width < height){
31450                         scale = targetHeight / height;
31451                     }
31452                 }
31453                 
31454                 context.scale(scale, scale);
31455                 
31456                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31457                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31458
31459                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31460                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31461                 
31462                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31463                 
31464                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31465                 
31466                 break;
31467             case 180 :
31468                 
31469                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31470                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31471                 
31472                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31473                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31474                 
31475                 var targetWidth = this.minWidth - 2 * x;
31476                 var targetHeight = this.minHeight - 2 * y;
31477                 
31478                 var scale = 1;
31479                 
31480                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31481                     scale = targetWidth / width;
31482                 }
31483                 
31484                 if(x > 0 && y == 0){
31485                     scale = targetHeight / height;
31486                 }
31487                 
31488                 if(x > 0 && y > 0){
31489                     scale = targetWidth / width;
31490                     
31491                     if(width < height){
31492                         scale = targetHeight / height;
31493                     }
31494                 }
31495                 
31496                 context.scale(scale, scale);
31497                 
31498                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31499                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31500
31501                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31502                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31503
31504                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31505                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31506                 
31507                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31508                 
31509                 break;
31510             case 270 :
31511                 
31512                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31513                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31514                 
31515                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31516                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31517                 
31518                 var targetWidth = this.minWidth - 2 * x;
31519                 var targetHeight = this.minHeight - 2 * y;
31520                 
31521                 var scale = 1;
31522                 
31523                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31524                     scale = targetWidth / width;
31525                 }
31526                 
31527                 if(x > 0 && y == 0){
31528                     scale = targetHeight / height;
31529                 }
31530                 
31531                 if(x > 0 && y > 0){
31532                     scale = targetWidth / width;
31533                     
31534                     if(width < height){
31535                         scale = targetHeight / height;
31536                     }
31537                 }
31538                 
31539                 context.scale(scale, scale);
31540                 
31541                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31542                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31543
31544                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31545                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31546                 
31547                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31548                 
31549                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31550                 
31551                 break;
31552             default : 
31553                 break;
31554         }
31555         
31556         this.cropData = canvas.toDataURL(this.cropType);
31557         
31558         if(this.fireEvent('crop', this, this.cropData) !== false){
31559             this.process(this.file, this.cropData);
31560         }
31561         
31562         return;
31563         
31564     },
31565     
31566     setThumbBoxSize : function()
31567     {
31568         var width, height;
31569         
31570         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31571             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31572             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31573             
31574             this.minWidth = width;
31575             this.minHeight = height;
31576             
31577             if(this.rotate == 90 || this.rotate == 270){
31578                 this.minWidth = height;
31579                 this.minHeight = width;
31580             }
31581         }
31582         
31583         height = 300;
31584         width = Math.ceil(this.minWidth * height / this.minHeight);
31585         
31586         if(this.minWidth > this.minHeight){
31587             width = 300;
31588             height = Math.ceil(this.minHeight * width / this.minWidth);
31589         }
31590         
31591         this.thumbEl.setStyle({
31592             width : width + 'px',
31593             height : height + 'px'
31594         });
31595
31596         return;
31597             
31598     },
31599     
31600     setThumbBoxPosition : function()
31601     {
31602         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31603         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31604         
31605         this.thumbEl.setLeft(x);
31606         this.thumbEl.setTop(y);
31607         
31608     },
31609     
31610     baseRotateLevel : function()
31611     {
31612         this.baseRotate = 1;
31613         
31614         if(
31615                 typeof(this.exif) != 'undefined' &&
31616                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31617                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31618         ){
31619             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31620         }
31621         
31622         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31623         
31624     },
31625     
31626     baseScaleLevel : function()
31627     {
31628         var width, height;
31629         
31630         if(this.isDocument){
31631             
31632             if(this.baseRotate == 6 || this.baseRotate == 8){
31633             
31634                 height = this.thumbEl.getHeight();
31635                 this.baseScale = height / this.imageEl.OriginWidth;
31636
31637                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31638                     width = this.thumbEl.getWidth();
31639                     this.baseScale = width / this.imageEl.OriginHeight;
31640                 }
31641
31642                 return;
31643             }
31644
31645             height = this.thumbEl.getHeight();
31646             this.baseScale = height / this.imageEl.OriginHeight;
31647
31648             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31649                 width = this.thumbEl.getWidth();
31650                 this.baseScale = width / this.imageEl.OriginWidth;
31651             }
31652
31653             return;
31654         }
31655         
31656         if(this.baseRotate == 6 || this.baseRotate == 8){
31657             
31658             width = this.thumbEl.getHeight();
31659             this.baseScale = width / this.imageEl.OriginHeight;
31660             
31661             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31662                 height = this.thumbEl.getWidth();
31663                 this.baseScale = height / this.imageEl.OriginHeight;
31664             }
31665             
31666             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31667                 height = this.thumbEl.getWidth();
31668                 this.baseScale = height / this.imageEl.OriginHeight;
31669                 
31670                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31671                     width = this.thumbEl.getHeight();
31672                     this.baseScale = width / this.imageEl.OriginWidth;
31673                 }
31674             }
31675             
31676             return;
31677         }
31678         
31679         width = this.thumbEl.getWidth();
31680         this.baseScale = width / this.imageEl.OriginWidth;
31681         
31682         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31683             height = this.thumbEl.getHeight();
31684             this.baseScale = height / this.imageEl.OriginHeight;
31685         }
31686         
31687         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31688             
31689             height = this.thumbEl.getHeight();
31690             this.baseScale = height / this.imageEl.OriginHeight;
31691             
31692             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31693                 width = this.thumbEl.getWidth();
31694                 this.baseScale = width / this.imageEl.OriginWidth;
31695             }
31696             
31697         }
31698         
31699         return;
31700     },
31701     
31702     getScaleLevel : function()
31703     {
31704         return this.baseScale * Math.pow(1.1, this.scale);
31705     },
31706     
31707     onTouchStart : function(e)
31708     {
31709         if(!this.canvasLoaded){
31710             this.beforeSelectFile(e);
31711             return;
31712         }
31713         
31714         var touches = e.browserEvent.touches;
31715         
31716         if(!touches){
31717             return;
31718         }
31719         
31720         if(touches.length == 1){
31721             this.onMouseDown(e);
31722             return;
31723         }
31724         
31725         if(touches.length != 2){
31726             return;
31727         }
31728         
31729         var coords = [];
31730         
31731         for(var i = 0, finger; finger = touches[i]; i++){
31732             coords.push(finger.pageX, finger.pageY);
31733         }
31734         
31735         var x = Math.pow(coords[0] - coords[2], 2);
31736         var y = Math.pow(coords[1] - coords[3], 2);
31737         
31738         this.startDistance = Math.sqrt(x + y);
31739         
31740         this.startScale = this.scale;
31741         
31742         this.pinching = true;
31743         this.dragable = false;
31744         
31745     },
31746     
31747     onTouchMove : function(e)
31748     {
31749         if(!this.pinching && !this.dragable){
31750             return;
31751         }
31752         
31753         var touches = e.browserEvent.touches;
31754         
31755         if(!touches){
31756             return;
31757         }
31758         
31759         if(this.dragable){
31760             this.onMouseMove(e);
31761             return;
31762         }
31763         
31764         var coords = [];
31765         
31766         for(var i = 0, finger; finger = touches[i]; i++){
31767             coords.push(finger.pageX, finger.pageY);
31768         }
31769         
31770         var x = Math.pow(coords[0] - coords[2], 2);
31771         var y = Math.pow(coords[1] - coords[3], 2);
31772         
31773         this.endDistance = Math.sqrt(x + y);
31774         
31775         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31776         
31777         if(!this.zoomable()){
31778             this.scale = this.startScale;
31779             return;
31780         }
31781         
31782         this.draw();
31783         
31784     },
31785     
31786     onTouchEnd : function(e)
31787     {
31788         this.pinching = false;
31789         this.dragable = false;
31790         
31791     },
31792     
31793     process : function(file, crop)
31794     {
31795         if(this.loadMask){
31796             this.maskEl.mask(this.loadingText);
31797         }
31798         
31799         this.xhr = new XMLHttpRequest();
31800         
31801         file.xhr = this.xhr;
31802
31803         this.xhr.open(this.method, this.url, true);
31804         
31805         var headers = {
31806             "Accept": "application/json",
31807             "Cache-Control": "no-cache",
31808             "X-Requested-With": "XMLHttpRequest"
31809         };
31810         
31811         for (var headerName in headers) {
31812             var headerValue = headers[headerName];
31813             if (headerValue) {
31814                 this.xhr.setRequestHeader(headerName, headerValue);
31815             }
31816         }
31817         
31818         var _this = this;
31819         
31820         this.xhr.onload = function()
31821         {
31822             _this.xhrOnLoad(_this.xhr);
31823         }
31824         
31825         this.xhr.onerror = function()
31826         {
31827             _this.xhrOnError(_this.xhr);
31828         }
31829         
31830         var formData = new FormData();
31831
31832         formData.append('returnHTML', 'NO');
31833         
31834         if(crop){
31835             formData.append('crop', crop);
31836         }
31837         
31838         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31839             formData.append(this.paramName, file, file.name);
31840         }
31841         
31842         if(typeof(file.filename) != 'undefined'){
31843             formData.append('filename', file.filename);
31844         }
31845         
31846         if(typeof(file.mimetype) != 'undefined'){
31847             formData.append('mimetype', file.mimetype);
31848         }
31849         
31850         if(this.fireEvent('arrange', this, formData) != false){
31851             this.xhr.send(formData);
31852         };
31853     },
31854     
31855     xhrOnLoad : function(xhr)
31856     {
31857         if(this.loadMask){
31858             this.maskEl.unmask();
31859         }
31860         
31861         if (xhr.readyState !== 4) {
31862             this.fireEvent('exception', this, xhr);
31863             return;
31864         }
31865
31866         var response = Roo.decode(xhr.responseText);
31867         
31868         if(!response.success){
31869             this.fireEvent('exception', this, xhr);
31870             return;
31871         }
31872         
31873         var response = Roo.decode(xhr.responseText);
31874         
31875         this.fireEvent('upload', this, response);
31876         
31877     },
31878     
31879     xhrOnError : function()
31880     {
31881         if(this.loadMask){
31882             this.maskEl.unmask();
31883         }
31884         
31885         Roo.log('xhr on error');
31886         
31887         var response = Roo.decode(xhr.responseText);
31888           
31889         Roo.log(response);
31890         
31891     },
31892     
31893     prepare : function(file)
31894     {   
31895         if(this.loadMask){
31896             this.maskEl.mask(this.loadingText);
31897         }
31898         
31899         this.file = false;
31900         this.exif = {};
31901         
31902         if(typeof(file) === 'string'){
31903             this.loadCanvas(file);
31904             return;
31905         }
31906         
31907         if(!file || !this.urlAPI){
31908             return;
31909         }
31910         
31911         this.file = file;
31912         this.cropType = file.type;
31913         
31914         var _this = this;
31915         
31916         if(this.fireEvent('prepare', this, this.file) != false){
31917             
31918             var reader = new FileReader();
31919             
31920             reader.onload = function (e) {
31921                 if (e.target.error) {
31922                     Roo.log(e.target.error);
31923                     return;
31924                 }
31925                 
31926                 var buffer = e.target.result,
31927                     dataView = new DataView(buffer),
31928                     offset = 2,
31929                     maxOffset = dataView.byteLength - 4,
31930                     markerBytes,
31931                     markerLength;
31932                 
31933                 if (dataView.getUint16(0) === 0xffd8) {
31934                     while (offset < maxOffset) {
31935                         markerBytes = dataView.getUint16(offset);
31936                         
31937                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31938                             markerLength = dataView.getUint16(offset + 2) + 2;
31939                             if (offset + markerLength > dataView.byteLength) {
31940                                 Roo.log('Invalid meta data: Invalid segment size.');
31941                                 break;
31942                             }
31943                             
31944                             if(markerBytes == 0xffe1){
31945                                 _this.parseExifData(
31946                                     dataView,
31947                                     offset,
31948                                     markerLength
31949                                 );
31950                             }
31951                             
31952                             offset += markerLength;
31953                             
31954                             continue;
31955                         }
31956                         
31957                         break;
31958                     }
31959                     
31960                 }
31961                 
31962                 var url = _this.urlAPI.createObjectURL(_this.file);
31963                 
31964                 _this.loadCanvas(url);
31965                 
31966                 return;
31967             }
31968             
31969             reader.readAsArrayBuffer(this.file);
31970             
31971         }
31972         
31973     },
31974     
31975     parseExifData : function(dataView, offset, length)
31976     {
31977         var tiffOffset = offset + 10,
31978             littleEndian,
31979             dirOffset;
31980     
31981         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31982             // No Exif data, might be XMP data instead
31983             return;
31984         }
31985         
31986         // Check for the ASCII code for "Exif" (0x45786966):
31987         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31988             // No Exif data, might be XMP data instead
31989             return;
31990         }
31991         if (tiffOffset + 8 > dataView.byteLength) {
31992             Roo.log('Invalid Exif data: Invalid segment size.');
31993             return;
31994         }
31995         // Check for the two null bytes:
31996         if (dataView.getUint16(offset + 8) !== 0x0000) {
31997             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31998             return;
31999         }
32000         // Check the byte alignment:
32001         switch (dataView.getUint16(tiffOffset)) {
32002         case 0x4949:
32003             littleEndian = true;
32004             break;
32005         case 0x4D4D:
32006             littleEndian = false;
32007             break;
32008         default:
32009             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32010             return;
32011         }
32012         // Check for the TIFF tag marker (0x002A):
32013         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32014             Roo.log('Invalid Exif data: Missing TIFF marker.');
32015             return;
32016         }
32017         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32018         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32019         
32020         this.parseExifTags(
32021             dataView,
32022             tiffOffset,
32023             tiffOffset + dirOffset,
32024             littleEndian
32025         );
32026     },
32027     
32028     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32029     {
32030         var tagsNumber,
32031             dirEndOffset,
32032             i;
32033         if (dirOffset + 6 > dataView.byteLength) {
32034             Roo.log('Invalid Exif data: Invalid directory offset.');
32035             return;
32036         }
32037         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32038         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32039         if (dirEndOffset + 4 > dataView.byteLength) {
32040             Roo.log('Invalid Exif data: Invalid directory size.');
32041             return;
32042         }
32043         for (i = 0; i < tagsNumber; i += 1) {
32044             this.parseExifTag(
32045                 dataView,
32046                 tiffOffset,
32047                 dirOffset + 2 + 12 * i, // tag offset
32048                 littleEndian
32049             );
32050         }
32051         // Return the offset to the next directory:
32052         return dataView.getUint32(dirEndOffset, littleEndian);
32053     },
32054     
32055     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32056     {
32057         var tag = dataView.getUint16(offset, littleEndian);
32058         
32059         this.exif[tag] = this.getExifValue(
32060             dataView,
32061             tiffOffset,
32062             offset,
32063             dataView.getUint16(offset + 2, littleEndian), // tag type
32064             dataView.getUint32(offset + 4, littleEndian), // tag length
32065             littleEndian
32066         );
32067     },
32068     
32069     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32070     {
32071         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32072             tagSize,
32073             dataOffset,
32074             values,
32075             i,
32076             str,
32077             c;
32078     
32079         if (!tagType) {
32080             Roo.log('Invalid Exif data: Invalid tag type.');
32081             return;
32082         }
32083         
32084         tagSize = tagType.size * length;
32085         // Determine if the value is contained in the dataOffset bytes,
32086         // or if the value at the dataOffset is a pointer to the actual data:
32087         dataOffset = tagSize > 4 ?
32088                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32089         if (dataOffset + tagSize > dataView.byteLength) {
32090             Roo.log('Invalid Exif data: Invalid data offset.');
32091             return;
32092         }
32093         if (length === 1) {
32094             return tagType.getValue(dataView, dataOffset, littleEndian);
32095         }
32096         values = [];
32097         for (i = 0; i < length; i += 1) {
32098             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32099         }
32100         
32101         if (tagType.ascii) {
32102             str = '';
32103             // Concatenate the chars:
32104             for (i = 0; i < values.length; i += 1) {
32105                 c = values[i];
32106                 // Ignore the terminating NULL byte(s):
32107                 if (c === '\u0000') {
32108                     break;
32109                 }
32110                 str += c;
32111             }
32112             return str;
32113         }
32114         return values;
32115     }
32116     
32117 });
32118
32119 Roo.apply(Roo.bootstrap.UploadCropbox, {
32120     tags : {
32121         'Orientation': 0x0112
32122     },
32123     
32124     Orientation: {
32125             1: 0, //'top-left',
32126 //            2: 'top-right',
32127             3: 180, //'bottom-right',
32128 //            4: 'bottom-left',
32129 //            5: 'left-top',
32130             6: 90, //'right-top',
32131 //            7: 'right-bottom',
32132             8: 270 //'left-bottom'
32133     },
32134     
32135     exifTagTypes : {
32136         // byte, 8-bit unsigned int:
32137         1: {
32138             getValue: function (dataView, dataOffset) {
32139                 return dataView.getUint8(dataOffset);
32140             },
32141             size: 1
32142         },
32143         // ascii, 8-bit byte:
32144         2: {
32145             getValue: function (dataView, dataOffset) {
32146                 return String.fromCharCode(dataView.getUint8(dataOffset));
32147             },
32148             size: 1,
32149             ascii: true
32150         },
32151         // short, 16 bit int:
32152         3: {
32153             getValue: function (dataView, dataOffset, littleEndian) {
32154                 return dataView.getUint16(dataOffset, littleEndian);
32155             },
32156             size: 2
32157         },
32158         // long, 32 bit int:
32159         4: {
32160             getValue: function (dataView, dataOffset, littleEndian) {
32161                 return dataView.getUint32(dataOffset, littleEndian);
32162             },
32163             size: 4
32164         },
32165         // rational = two long values, first is numerator, second is denominator:
32166         5: {
32167             getValue: function (dataView, dataOffset, littleEndian) {
32168                 return dataView.getUint32(dataOffset, littleEndian) /
32169                     dataView.getUint32(dataOffset + 4, littleEndian);
32170             },
32171             size: 8
32172         },
32173         // slong, 32 bit signed int:
32174         9: {
32175             getValue: function (dataView, dataOffset, littleEndian) {
32176                 return dataView.getInt32(dataOffset, littleEndian);
32177             },
32178             size: 4
32179         },
32180         // srational, two slongs, first is numerator, second is denominator:
32181         10: {
32182             getValue: function (dataView, dataOffset, littleEndian) {
32183                 return dataView.getInt32(dataOffset, littleEndian) /
32184                     dataView.getInt32(dataOffset + 4, littleEndian);
32185             },
32186             size: 8
32187         }
32188     },
32189     
32190     footer : {
32191         STANDARD : [
32192             {
32193                 tag : 'div',
32194                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32195                 action : 'rotate-left',
32196                 cn : [
32197                     {
32198                         tag : 'button',
32199                         cls : 'btn btn-default',
32200                         html : '<i class="fa fa-undo"></i>'
32201                     }
32202                 ]
32203             },
32204             {
32205                 tag : 'div',
32206                 cls : 'btn-group roo-upload-cropbox-picture',
32207                 action : 'picture',
32208                 cn : [
32209                     {
32210                         tag : 'button',
32211                         cls : 'btn btn-default',
32212                         html : '<i class="fa fa-picture-o"></i>'
32213                     }
32214                 ]
32215             },
32216             {
32217                 tag : 'div',
32218                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32219                 action : 'rotate-right',
32220                 cn : [
32221                     {
32222                         tag : 'button',
32223                         cls : 'btn btn-default',
32224                         html : '<i class="fa fa-repeat"></i>'
32225                     }
32226                 ]
32227             }
32228         ],
32229         DOCUMENT : [
32230             {
32231                 tag : 'div',
32232                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32233                 action : 'rotate-left',
32234                 cn : [
32235                     {
32236                         tag : 'button',
32237                         cls : 'btn btn-default',
32238                         html : '<i class="fa fa-undo"></i>'
32239                     }
32240                 ]
32241             },
32242             {
32243                 tag : 'div',
32244                 cls : 'btn-group roo-upload-cropbox-download',
32245                 action : 'download',
32246                 cn : [
32247                     {
32248                         tag : 'button',
32249                         cls : 'btn btn-default',
32250                         html : '<i class="fa fa-download"></i>'
32251                     }
32252                 ]
32253             },
32254             {
32255                 tag : 'div',
32256                 cls : 'btn-group roo-upload-cropbox-crop',
32257                 action : 'crop',
32258                 cn : [
32259                     {
32260                         tag : 'button',
32261                         cls : 'btn btn-default',
32262                         html : '<i class="fa fa-crop"></i>'
32263                     }
32264                 ]
32265             },
32266             {
32267                 tag : 'div',
32268                 cls : 'btn-group roo-upload-cropbox-trash',
32269                 action : 'trash',
32270                 cn : [
32271                     {
32272                         tag : 'button',
32273                         cls : 'btn btn-default',
32274                         html : '<i class="fa fa-trash"></i>'
32275                     }
32276                 ]
32277             },
32278             {
32279                 tag : 'div',
32280                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32281                 action : 'rotate-right',
32282                 cn : [
32283                     {
32284                         tag : 'button',
32285                         cls : 'btn btn-default',
32286                         html : '<i class="fa fa-repeat"></i>'
32287                     }
32288                 ]
32289             }
32290         ],
32291         ROTATOR : [
32292             {
32293                 tag : 'div',
32294                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32295                 action : 'rotate-left',
32296                 cn : [
32297                     {
32298                         tag : 'button',
32299                         cls : 'btn btn-default',
32300                         html : '<i class="fa fa-undo"></i>'
32301                     }
32302                 ]
32303             },
32304             {
32305                 tag : 'div',
32306                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32307                 action : 'rotate-right',
32308                 cn : [
32309                     {
32310                         tag : 'button',
32311                         cls : 'btn btn-default',
32312                         html : '<i class="fa fa-repeat"></i>'
32313                     }
32314                 ]
32315             }
32316         ]
32317     }
32318 });
32319
32320 /*
32321 * Licence: LGPL
32322 */
32323
32324 /**
32325  * @class Roo.bootstrap.DocumentManager
32326  * @extends Roo.bootstrap.Component
32327  * Bootstrap DocumentManager class
32328  * @cfg {String} paramName default 'imageUpload'
32329  * @cfg {String} toolTipName default 'filename'
32330  * @cfg {String} method default POST
32331  * @cfg {String} url action url
32332  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32333  * @cfg {Boolean} multiple multiple upload default true
32334  * @cfg {Number} thumbSize default 300
32335  * @cfg {String} fieldLabel
32336  * @cfg {Number} labelWidth default 4
32337  * @cfg {String} labelAlign (left|top) default left
32338  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32339 * @cfg {Number} labellg set the width of label (1-12)
32340  * @cfg {Number} labelmd set the width of label (1-12)
32341  * @cfg {Number} labelsm set the width of label (1-12)
32342  * @cfg {Number} labelxs set the width of label (1-12)
32343  * 
32344  * @constructor
32345  * Create a new DocumentManager
32346  * @param {Object} config The config object
32347  */
32348
32349 Roo.bootstrap.DocumentManager = function(config){
32350     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32351     
32352     this.files = [];
32353     this.delegates = [];
32354     
32355     this.addEvents({
32356         /**
32357          * @event initial
32358          * Fire when initial the DocumentManager
32359          * @param {Roo.bootstrap.DocumentManager} this
32360          */
32361         "initial" : true,
32362         /**
32363          * @event inspect
32364          * inspect selected file
32365          * @param {Roo.bootstrap.DocumentManager} this
32366          * @param {File} file
32367          */
32368         "inspect" : true,
32369         /**
32370          * @event exception
32371          * Fire when xhr load exception
32372          * @param {Roo.bootstrap.DocumentManager} this
32373          * @param {XMLHttpRequest} xhr
32374          */
32375         "exception" : true,
32376         /**
32377          * @event afterupload
32378          * Fire when xhr load exception
32379          * @param {Roo.bootstrap.DocumentManager} this
32380          * @param {XMLHttpRequest} xhr
32381          */
32382         "afterupload" : true,
32383         /**
32384          * @event prepare
32385          * prepare the form data
32386          * @param {Roo.bootstrap.DocumentManager} this
32387          * @param {Object} formData
32388          */
32389         "prepare" : true,
32390         /**
32391          * @event remove
32392          * Fire when remove the file
32393          * @param {Roo.bootstrap.DocumentManager} this
32394          * @param {Object} file
32395          */
32396         "remove" : true,
32397         /**
32398          * @event refresh
32399          * Fire after refresh the file
32400          * @param {Roo.bootstrap.DocumentManager} this
32401          */
32402         "refresh" : true,
32403         /**
32404          * @event click
32405          * Fire after click the image
32406          * @param {Roo.bootstrap.DocumentManager} this
32407          * @param {Object} file
32408          */
32409         "click" : true,
32410         /**
32411          * @event edit
32412          * Fire when upload a image and editable set to true
32413          * @param {Roo.bootstrap.DocumentManager} this
32414          * @param {Object} file
32415          */
32416         "edit" : true,
32417         /**
32418          * @event beforeselectfile
32419          * Fire before select file
32420          * @param {Roo.bootstrap.DocumentManager} this
32421          */
32422         "beforeselectfile" : true,
32423         /**
32424          * @event process
32425          * Fire before process file
32426          * @param {Roo.bootstrap.DocumentManager} this
32427          * @param {Object} file
32428          */
32429         "process" : true,
32430         /**
32431          * @event previewrendered
32432          * Fire when preview rendered
32433          * @param {Roo.bootstrap.DocumentManager} this
32434          * @param {Object} file
32435          */
32436         "previewrendered" : true,
32437         /**
32438          */
32439         "previewResize" : true
32440         
32441     });
32442 };
32443
32444 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32445     
32446     boxes : 0,
32447     inputName : '',
32448     thumbSize : 300,
32449     multiple : true,
32450     files : false,
32451     method : 'POST',
32452     url : '',
32453     paramName : 'imageUpload',
32454     toolTipName : 'filename',
32455     fieldLabel : '',
32456     labelWidth : 4,
32457     labelAlign : 'left',
32458     editable : true,
32459     delegates : false,
32460     xhr : false, 
32461     
32462     labellg : 0,
32463     labelmd : 0,
32464     labelsm : 0,
32465     labelxs : 0,
32466     
32467     getAutoCreate : function()
32468     {   
32469         var managerWidget = {
32470             tag : 'div',
32471             cls : 'roo-document-manager',
32472             cn : [
32473                 {
32474                     tag : 'input',
32475                     cls : 'roo-document-manager-selector',
32476                     type : 'file'
32477                 },
32478                 {
32479                     tag : 'div',
32480                     cls : 'roo-document-manager-uploader',
32481                     cn : [
32482                         {
32483                             tag : 'div',
32484                             cls : 'roo-document-manager-upload-btn',
32485                             html : '<i class="fa fa-plus"></i>'
32486                         }
32487                     ]
32488                     
32489                 }
32490             ]
32491         };
32492         
32493         var content = [
32494             {
32495                 tag : 'div',
32496                 cls : 'column col-md-12',
32497                 cn : managerWidget
32498             }
32499         ];
32500         
32501         if(this.fieldLabel.length){
32502             
32503             content = [
32504                 {
32505                     tag : 'div',
32506                     cls : 'column col-md-12',
32507                     html : this.fieldLabel
32508                 },
32509                 {
32510                     tag : 'div',
32511                     cls : 'column col-md-12',
32512                     cn : managerWidget
32513                 }
32514             ];
32515
32516             if(this.labelAlign == 'left'){
32517                 content = [
32518                     {
32519                         tag : 'div',
32520                         cls : 'column',
32521                         html : this.fieldLabel
32522                     },
32523                     {
32524                         tag : 'div',
32525                         cls : 'column',
32526                         cn : managerWidget
32527                     }
32528                 ];
32529                 
32530                 if(this.labelWidth > 12){
32531                     content[0].style = "width: " + this.labelWidth + 'px';
32532                 }
32533
32534                 if(this.labelWidth < 13 && this.labelmd == 0){
32535                     this.labelmd = this.labelWidth;
32536                 }
32537
32538                 if(this.labellg > 0){
32539                     content[0].cls += ' col-lg-' + this.labellg;
32540                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32541                 }
32542
32543                 if(this.labelmd > 0){
32544                     content[0].cls += ' col-md-' + this.labelmd;
32545                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32546                 }
32547
32548                 if(this.labelsm > 0){
32549                     content[0].cls += ' col-sm-' + this.labelsm;
32550                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32551                 }
32552
32553                 if(this.labelxs > 0){
32554                     content[0].cls += ' col-xs-' + this.labelxs;
32555                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32556                 }
32557                 
32558             }
32559         }
32560         
32561         var cfg = {
32562             tag : 'div',
32563             cls : 'row clearfix',
32564             cn : content
32565         };
32566         
32567         return cfg;
32568         
32569     },
32570     
32571     initEvents : function()
32572     {
32573         this.managerEl = this.el.select('.roo-document-manager', true).first();
32574         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32575         
32576         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32577         this.selectorEl.hide();
32578         
32579         if(this.multiple){
32580             this.selectorEl.attr('multiple', 'multiple');
32581         }
32582         
32583         this.selectorEl.on('change', this.onFileSelected, this);
32584         
32585         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32586         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32587         
32588         this.uploader.on('click', this.onUploaderClick, this);
32589         
32590         this.renderProgressDialog();
32591         
32592         var _this = this;
32593         
32594         window.addEventListener("resize", function() { _this.refresh(); } );
32595         
32596         this.fireEvent('initial', this);
32597     },
32598     
32599     renderProgressDialog : function()
32600     {
32601         var _this = this;
32602         
32603         this.progressDialog = new Roo.bootstrap.Modal({
32604             cls : 'roo-document-manager-progress-dialog',
32605             allow_close : false,
32606             animate : false,
32607             title : '',
32608             buttons : [
32609                 {
32610                     name  :'cancel',
32611                     weight : 'danger',
32612                     html : 'Cancel'
32613                 }
32614             ], 
32615             listeners : { 
32616                 btnclick : function() {
32617                     _this.uploadCancel();
32618                     this.hide();
32619                 }
32620             }
32621         });
32622          
32623         this.progressDialog.render(Roo.get(document.body));
32624          
32625         this.progress = new Roo.bootstrap.Progress({
32626             cls : 'roo-document-manager-progress',
32627             active : true,
32628             striped : true
32629         });
32630         
32631         this.progress.render(this.progressDialog.getChildContainer());
32632         
32633         this.progressBar = new Roo.bootstrap.ProgressBar({
32634             cls : 'roo-document-manager-progress-bar',
32635             aria_valuenow : 0,
32636             aria_valuemin : 0,
32637             aria_valuemax : 12,
32638             panel : 'success'
32639         });
32640         
32641         this.progressBar.render(this.progress.getChildContainer());
32642     },
32643     
32644     onUploaderClick : function(e)
32645     {
32646         e.preventDefault();
32647      
32648         if(this.fireEvent('beforeselectfile', this) != false){
32649             this.selectorEl.dom.click();
32650         }
32651         
32652     },
32653     
32654     onFileSelected : function(e)
32655     {
32656         e.preventDefault();
32657         
32658         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32659             return;
32660         }
32661         
32662         Roo.each(this.selectorEl.dom.files, function(file){
32663             if(this.fireEvent('inspect', this, file) != false){
32664                 this.files.push(file);
32665             }
32666         }, this);
32667         
32668         this.queue();
32669         
32670     },
32671     
32672     queue : function()
32673     {
32674         this.selectorEl.dom.value = '';
32675         
32676         if(!this.files || !this.files.length){
32677             return;
32678         }
32679         
32680         if(this.boxes > 0 && this.files.length > this.boxes){
32681             this.files = this.files.slice(0, this.boxes);
32682         }
32683         
32684         this.uploader.show();
32685         
32686         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32687             this.uploader.hide();
32688         }
32689         
32690         var _this = this;
32691         
32692         var files = [];
32693         
32694         var docs = [];
32695         
32696         Roo.each(this.files, function(file){
32697             
32698             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32699                 var f = this.renderPreview(file);
32700                 files.push(f);
32701                 return;
32702             }
32703             
32704             if(file.type.indexOf('image') != -1){
32705                 this.delegates.push(
32706                     (function(){
32707                         _this.process(file);
32708                     }).createDelegate(this)
32709                 );
32710         
32711                 return;
32712             }
32713             
32714             docs.push(
32715                 (function(){
32716                     _this.process(file);
32717                 }).createDelegate(this)
32718             );
32719             
32720         }, this);
32721         
32722         this.files = files;
32723         
32724         this.delegates = this.delegates.concat(docs);
32725         
32726         if(!this.delegates.length){
32727             this.refresh();
32728             return;
32729         }
32730         
32731         this.progressBar.aria_valuemax = this.delegates.length;
32732         
32733         this.arrange();
32734         
32735         return;
32736     },
32737     
32738     arrange : function()
32739     {
32740         if(!this.delegates.length){
32741             this.progressDialog.hide();
32742             this.refresh();
32743             return;
32744         }
32745         
32746         var delegate = this.delegates.shift();
32747         
32748         this.progressDialog.show();
32749         
32750         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32751         
32752         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32753         
32754         delegate();
32755     },
32756     
32757     refresh : function()
32758     {
32759         this.uploader.show();
32760         
32761         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32762             this.uploader.hide();
32763         }
32764         
32765         Roo.isTouch ? this.closable(false) : this.closable(true);
32766         
32767         this.fireEvent('refresh', this);
32768     },
32769     
32770     onRemove : function(e, el, o)
32771     {
32772         e.preventDefault();
32773         
32774         this.fireEvent('remove', this, o);
32775         
32776     },
32777     
32778     remove : function(o)
32779     {
32780         var files = [];
32781         
32782         Roo.each(this.files, function(file){
32783             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32784                 files.push(file);
32785                 return;
32786             }
32787
32788             o.target.remove();
32789
32790         }, this);
32791         
32792         this.files = files;
32793         
32794         this.refresh();
32795     },
32796     
32797     clear : function()
32798     {
32799         Roo.each(this.files, function(file){
32800             if(!file.target){
32801                 return;
32802             }
32803             
32804             file.target.remove();
32805
32806         }, this);
32807         
32808         this.files = [];
32809         
32810         this.refresh();
32811     },
32812     
32813     onClick : function(e, el, o)
32814     {
32815         e.preventDefault();
32816         
32817         this.fireEvent('click', this, o);
32818         
32819     },
32820     
32821     closable : function(closable)
32822     {
32823         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32824             
32825             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32826             
32827             if(closable){
32828                 el.show();
32829                 return;
32830             }
32831             
32832             el.hide();
32833             
32834         }, this);
32835     },
32836     
32837     xhrOnLoad : function(xhr)
32838     {
32839         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32840             el.remove();
32841         }, this);
32842         
32843         if (xhr.readyState !== 4) {
32844             this.arrange();
32845             this.fireEvent('exception', this, xhr);
32846             return;
32847         }
32848
32849         var response = Roo.decode(xhr.responseText);
32850         
32851         if(!response.success){
32852             this.arrange();
32853             this.fireEvent('exception', this, xhr);
32854             return;
32855         }
32856         
32857         var file = this.renderPreview(response.data);
32858         
32859         this.files.push(file);
32860         
32861         this.arrange();
32862         
32863         this.fireEvent('afterupload', this, xhr);
32864         
32865     },
32866     
32867     xhrOnError : function(xhr)
32868     {
32869         Roo.log('xhr on error');
32870         
32871         var response = Roo.decode(xhr.responseText);
32872           
32873         Roo.log(response);
32874         
32875         this.arrange();
32876     },
32877     
32878     process : function(file)
32879     {
32880         if(this.fireEvent('process', this, file) !== false){
32881             if(this.editable && file.type.indexOf('image') != -1){
32882                 this.fireEvent('edit', this, file);
32883                 return;
32884             }
32885
32886             this.uploadStart(file, false);
32887
32888             return;
32889         }
32890         
32891     },
32892     
32893     uploadStart : function(file, crop)
32894     {
32895         this.xhr = new XMLHttpRequest();
32896         
32897         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32898             this.arrange();
32899             return;
32900         }
32901         
32902         file.xhr = this.xhr;
32903             
32904         this.managerEl.createChild({
32905             tag : 'div',
32906             cls : 'roo-document-manager-loading',
32907             cn : [
32908                 {
32909                     tag : 'div',
32910                     tooltip : file.name,
32911                     cls : 'roo-document-manager-thumb',
32912                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32913                 }
32914             ]
32915
32916         });
32917
32918         this.xhr.open(this.method, this.url, true);
32919         
32920         var headers = {
32921             "Accept": "application/json",
32922             "Cache-Control": "no-cache",
32923             "X-Requested-With": "XMLHttpRequest"
32924         };
32925         
32926         for (var headerName in headers) {
32927             var headerValue = headers[headerName];
32928             if (headerValue) {
32929                 this.xhr.setRequestHeader(headerName, headerValue);
32930             }
32931         }
32932         
32933         var _this = this;
32934         
32935         this.xhr.onload = function()
32936         {
32937             _this.xhrOnLoad(_this.xhr);
32938         }
32939         
32940         this.xhr.onerror = function()
32941         {
32942             _this.xhrOnError(_this.xhr);
32943         }
32944         
32945         var formData = new FormData();
32946
32947         formData.append('returnHTML', 'NO');
32948         
32949         if(crop){
32950             formData.append('crop', crop);
32951         }
32952         
32953         formData.append(this.paramName, file, file.name);
32954         
32955         var options = {
32956             file : file, 
32957             manually : false
32958         };
32959         
32960         if(this.fireEvent('prepare', this, formData, options) != false){
32961             
32962             if(options.manually){
32963                 return;
32964             }
32965             
32966             this.xhr.send(formData);
32967             return;
32968         };
32969         
32970         this.uploadCancel();
32971     },
32972     
32973     uploadCancel : function()
32974     {
32975         if (this.xhr) {
32976             this.xhr.abort();
32977         }
32978         
32979         this.delegates = [];
32980         
32981         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32982             el.remove();
32983         }, this);
32984         
32985         this.arrange();
32986     },
32987     
32988     renderPreview : function(file)
32989     {
32990         if(typeof(file.target) != 'undefined' && file.target){
32991             return file;
32992         }
32993         
32994         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32995         
32996         var previewEl = this.managerEl.createChild({
32997             tag : 'div',
32998             cls : 'roo-document-manager-preview',
32999             cn : [
33000                 {
33001                     tag : 'div',
33002                     tooltip : file[this.toolTipName],
33003                     cls : 'roo-document-manager-thumb',
33004                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33005                 },
33006                 {
33007                     tag : 'button',
33008                     cls : 'close',
33009                     html : '<i class="fa fa-times-circle"></i>'
33010                 }
33011             ]
33012         });
33013
33014         var close = previewEl.select('button.close', true).first();
33015
33016         close.on('click', this.onRemove, this, file);
33017
33018         file.target = previewEl;
33019
33020         var image = previewEl.select('img', true).first();
33021         
33022         var _this = this;
33023         
33024         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33025         
33026         image.on('click', this.onClick, this, file);
33027         
33028         this.fireEvent('previewrendered', this, file);
33029         
33030         return file;
33031         
33032     },
33033     
33034     onPreviewLoad : function(file, image)
33035     {
33036         if(typeof(file.target) == 'undefined' || !file.target){
33037             return;
33038         }
33039         
33040         var width = image.dom.naturalWidth || image.dom.width;
33041         var height = image.dom.naturalHeight || image.dom.height;
33042         
33043         if(!this.previewResize) {
33044             return;
33045         }
33046         
33047         if(width > height){
33048             file.target.addClass('wide');
33049             return;
33050         }
33051         
33052         file.target.addClass('tall');
33053         return;
33054         
33055     },
33056     
33057     uploadFromSource : function(file, crop)
33058     {
33059         this.xhr = new XMLHttpRequest();
33060         
33061         this.managerEl.createChild({
33062             tag : 'div',
33063             cls : 'roo-document-manager-loading',
33064             cn : [
33065                 {
33066                     tag : 'div',
33067                     tooltip : file.name,
33068                     cls : 'roo-document-manager-thumb',
33069                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33070                 }
33071             ]
33072
33073         });
33074
33075         this.xhr.open(this.method, this.url, true);
33076         
33077         var headers = {
33078             "Accept": "application/json",
33079             "Cache-Control": "no-cache",
33080             "X-Requested-With": "XMLHttpRequest"
33081         };
33082         
33083         for (var headerName in headers) {
33084             var headerValue = headers[headerName];
33085             if (headerValue) {
33086                 this.xhr.setRequestHeader(headerName, headerValue);
33087             }
33088         }
33089         
33090         var _this = this;
33091         
33092         this.xhr.onload = function()
33093         {
33094             _this.xhrOnLoad(_this.xhr);
33095         }
33096         
33097         this.xhr.onerror = function()
33098         {
33099             _this.xhrOnError(_this.xhr);
33100         }
33101         
33102         var formData = new FormData();
33103
33104         formData.append('returnHTML', 'NO');
33105         
33106         formData.append('crop', crop);
33107         
33108         if(typeof(file.filename) != 'undefined'){
33109             formData.append('filename', file.filename);
33110         }
33111         
33112         if(typeof(file.mimetype) != 'undefined'){
33113             formData.append('mimetype', file.mimetype);
33114         }
33115         
33116         Roo.log(formData);
33117         
33118         if(this.fireEvent('prepare', this, formData) != false){
33119             this.xhr.send(formData);
33120         };
33121     }
33122 });
33123
33124 /*
33125 * Licence: LGPL
33126 */
33127
33128 /**
33129  * @class Roo.bootstrap.DocumentViewer
33130  * @extends Roo.bootstrap.Component
33131  * Bootstrap DocumentViewer class
33132  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33133  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33134  * 
33135  * @constructor
33136  * Create a new DocumentViewer
33137  * @param {Object} config The config object
33138  */
33139
33140 Roo.bootstrap.DocumentViewer = function(config){
33141     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33142     
33143     this.addEvents({
33144         /**
33145          * @event initial
33146          * Fire after initEvent
33147          * @param {Roo.bootstrap.DocumentViewer} this
33148          */
33149         "initial" : true,
33150         /**
33151          * @event click
33152          * Fire after click
33153          * @param {Roo.bootstrap.DocumentViewer} this
33154          */
33155         "click" : true,
33156         /**
33157          * @event download
33158          * Fire after download button
33159          * @param {Roo.bootstrap.DocumentViewer} this
33160          */
33161         "download" : true,
33162         /**
33163          * @event trash
33164          * Fire after trash button
33165          * @param {Roo.bootstrap.DocumentViewer} this
33166          */
33167         "trash" : true
33168         
33169     });
33170 };
33171
33172 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33173     
33174     showDownload : true,
33175     
33176     showTrash : true,
33177     
33178     getAutoCreate : function()
33179     {
33180         var cfg = {
33181             tag : 'div',
33182             cls : 'roo-document-viewer',
33183             cn : [
33184                 {
33185                     tag : 'div',
33186                     cls : 'roo-document-viewer-body',
33187                     cn : [
33188                         {
33189                             tag : 'div',
33190                             cls : 'roo-document-viewer-thumb',
33191                             cn : [
33192                                 {
33193                                     tag : 'img',
33194                                     cls : 'roo-document-viewer-image'
33195                                 }
33196                             ]
33197                         }
33198                     ]
33199                 },
33200                 {
33201                     tag : 'div',
33202                     cls : 'roo-document-viewer-footer',
33203                     cn : {
33204                         tag : 'div',
33205                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33206                         cn : [
33207                             {
33208                                 tag : 'div',
33209                                 cls : 'btn-group roo-document-viewer-download',
33210                                 cn : [
33211                                     {
33212                                         tag : 'button',
33213                                         cls : 'btn btn-default',
33214                                         html : '<i class="fa fa-download"></i>'
33215                                     }
33216                                 ]
33217                             },
33218                             {
33219                                 tag : 'div',
33220                                 cls : 'btn-group roo-document-viewer-trash',
33221                                 cn : [
33222                                     {
33223                                         tag : 'button',
33224                                         cls : 'btn btn-default',
33225                                         html : '<i class="fa fa-trash"></i>'
33226                                     }
33227                                 ]
33228                             }
33229                         ]
33230                     }
33231                 }
33232             ]
33233         };
33234         
33235         return cfg;
33236     },
33237     
33238     initEvents : function()
33239     {
33240         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33241         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33242         
33243         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33244         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33245         
33246         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33247         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33248         
33249         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33250         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33251         
33252         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33253         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33254         
33255         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33256         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33257         
33258         this.bodyEl.on('click', this.onClick, this);
33259         this.downloadBtn.on('click', this.onDownload, this);
33260         this.trashBtn.on('click', this.onTrash, this);
33261         
33262         this.downloadBtn.hide();
33263         this.trashBtn.hide();
33264         
33265         if(this.showDownload){
33266             this.downloadBtn.show();
33267         }
33268         
33269         if(this.showTrash){
33270             this.trashBtn.show();
33271         }
33272         
33273         if(!this.showDownload && !this.showTrash) {
33274             this.footerEl.hide();
33275         }
33276         
33277     },
33278     
33279     initial : function()
33280     {
33281         this.fireEvent('initial', this);
33282         
33283     },
33284     
33285     onClick : function(e)
33286     {
33287         e.preventDefault();
33288         
33289         this.fireEvent('click', this);
33290     },
33291     
33292     onDownload : function(e)
33293     {
33294         e.preventDefault();
33295         
33296         this.fireEvent('download', this);
33297     },
33298     
33299     onTrash : function(e)
33300     {
33301         e.preventDefault();
33302         
33303         this.fireEvent('trash', this);
33304     }
33305     
33306 });
33307 /*
33308  * - LGPL
33309  *
33310  * nav progress bar
33311  * 
33312  */
33313
33314 /**
33315  * @class Roo.bootstrap.NavProgressBar
33316  * @extends Roo.bootstrap.Component
33317  * Bootstrap NavProgressBar class
33318  * 
33319  * @constructor
33320  * Create a new nav progress bar
33321  * @param {Object} config The config object
33322  */
33323
33324 Roo.bootstrap.NavProgressBar = function(config){
33325     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33326
33327     this.bullets = this.bullets || [];
33328    
33329 //    Roo.bootstrap.NavProgressBar.register(this);
33330      this.addEvents({
33331         /**
33332              * @event changed
33333              * Fires when the active item changes
33334              * @param {Roo.bootstrap.NavProgressBar} this
33335              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33336              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33337          */
33338         'changed': true
33339      });
33340     
33341 };
33342
33343 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33344     
33345     bullets : [],
33346     barItems : [],
33347     
33348     getAutoCreate : function()
33349     {
33350         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33351         
33352         cfg = {
33353             tag : 'div',
33354             cls : 'roo-navigation-bar-group',
33355             cn : [
33356                 {
33357                     tag : 'div',
33358                     cls : 'roo-navigation-top-bar'
33359                 },
33360                 {
33361                     tag : 'div',
33362                     cls : 'roo-navigation-bullets-bar',
33363                     cn : [
33364                         {
33365                             tag : 'ul',
33366                             cls : 'roo-navigation-bar'
33367                         }
33368                     ]
33369                 },
33370                 
33371                 {
33372                     tag : 'div',
33373                     cls : 'roo-navigation-bottom-bar'
33374                 }
33375             ]
33376             
33377         };
33378         
33379         return cfg;
33380         
33381     },
33382     
33383     initEvents: function() 
33384     {
33385         
33386     },
33387     
33388     onRender : function(ct, position) 
33389     {
33390         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33391         
33392         if(this.bullets.length){
33393             Roo.each(this.bullets, function(b){
33394                this.addItem(b);
33395             }, this);
33396         }
33397         
33398         this.format();
33399         
33400     },
33401     
33402     addItem : function(cfg)
33403     {
33404         var item = new Roo.bootstrap.NavProgressItem(cfg);
33405         
33406         item.parentId = this.id;
33407         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33408         
33409         if(cfg.html){
33410             var top = new Roo.bootstrap.Element({
33411                 tag : 'div',
33412                 cls : 'roo-navigation-bar-text'
33413             });
33414             
33415             var bottom = new Roo.bootstrap.Element({
33416                 tag : 'div',
33417                 cls : 'roo-navigation-bar-text'
33418             });
33419             
33420             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33421             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33422             
33423             var topText = new Roo.bootstrap.Element({
33424                 tag : 'span',
33425                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33426             });
33427             
33428             var bottomText = new Roo.bootstrap.Element({
33429                 tag : 'span',
33430                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33431             });
33432             
33433             topText.onRender(top.el, null);
33434             bottomText.onRender(bottom.el, null);
33435             
33436             item.topEl = top;
33437             item.bottomEl = bottom;
33438         }
33439         
33440         this.barItems.push(item);
33441         
33442         return item;
33443     },
33444     
33445     getActive : function()
33446     {
33447         var active = false;
33448         
33449         Roo.each(this.barItems, function(v){
33450             
33451             if (!v.isActive()) {
33452                 return;
33453             }
33454             
33455             active = v;
33456             return false;
33457             
33458         });
33459         
33460         return active;
33461     },
33462     
33463     setActiveItem : function(item)
33464     {
33465         var prev = false;
33466         
33467         Roo.each(this.barItems, function(v){
33468             if (v.rid == item.rid) {
33469                 return ;
33470             }
33471             
33472             if (v.isActive()) {
33473                 v.setActive(false);
33474                 prev = v;
33475             }
33476         });
33477
33478         item.setActive(true);
33479         
33480         this.fireEvent('changed', this, item, prev);
33481     },
33482     
33483     getBarItem: function(rid)
33484     {
33485         var ret = false;
33486         
33487         Roo.each(this.barItems, function(e) {
33488             if (e.rid != rid) {
33489                 return;
33490             }
33491             
33492             ret =  e;
33493             return false;
33494         });
33495         
33496         return ret;
33497     },
33498     
33499     indexOfItem : function(item)
33500     {
33501         var index = false;
33502         
33503         Roo.each(this.barItems, function(v, i){
33504             
33505             if (v.rid != item.rid) {
33506                 return;
33507             }
33508             
33509             index = i;
33510             return false
33511         });
33512         
33513         return index;
33514     },
33515     
33516     setActiveNext : function()
33517     {
33518         var i = this.indexOfItem(this.getActive());
33519         
33520         if (i > this.barItems.length) {
33521             return;
33522         }
33523         
33524         this.setActiveItem(this.barItems[i+1]);
33525     },
33526     
33527     setActivePrev : function()
33528     {
33529         var i = this.indexOfItem(this.getActive());
33530         
33531         if (i  < 1) {
33532             return;
33533         }
33534         
33535         this.setActiveItem(this.barItems[i-1]);
33536     },
33537     
33538     format : function()
33539     {
33540         if(!this.barItems.length){
33541             return;
33542         }
33543      
33544         var width = 100 / this.barItems.length;
33545         
33546         Roo.each(this.barItems, function(i){
33547             i.el.setStyle('width', width + '%');
33548             i.topEl.el.setStyle('width', width + '%');
33549             i.bottomEl.el.setStyle('width', width + '%');
33550         }, this);
33551         
33552     }
33553     
33554 });
33555 /*
33556  * - LGPL
33557  *
33558  * Nav Progress Item
33559  * 
33560  */
33561
33562 /**
33563  * @class Roo.bootstrap.NavProgressItem
33564  * @extends Roo.bootstrap.Component
33565  * Bootstrap NavProgressItem class
33566  * @cfg {String} rid the reference id
33567  * @cfg {Boolean} active (true|false) Is item active default false
33568  * @cfg {Boolean} disabled (true|false) Is item active default false
33569  * @cfg {String} html
33570  * @cfg {String} position (top|bottom) text position default bottom
33571  * @cfg {String} icon show icon instead of number
33572  * 
33573  * @constructor
33574  * Create a new NavProgressItem
33575  * @param {Object} config The config object
33576  */
33577 Roo.bootstrap.NavProgressItem = function(config){
33578     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33579     this.addEvents({
33580         // raw events
33581         /**
33582          * @event click
33583          * The raw click event for the entire grid.
33584          * @param {Roo.bootstrap.NavProgressItem} this
33585          * @param {Roo.EventObject} e
33586          */
33587         "click" : true
33588     });
33589    
33590 };
33591
33592 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33593     
33594     rid : '',
33595     active : false,
33596     disabled : false,
33597     html : '',
33598     position : 'bottom',
33599     icon : false,
33600     
33601     getAutoCreate : function()
33602     {
33603         var iconCls = 'roo-navigation-bar-item-icon';
33604         
33605         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33606         
33607         var cfg = {
33608             tag: 'li',
33609             cls: 'roo-navigation-bar-item',
33610             cn : [
33611                 {
33612                     tag : 'i',
33613                     cls : iconCls
33614                 }
33615             ]
33616         };
33617         
33618         if(this.active){
33619             cfg.cls += ' active';
33620         }
33621         if(this.disabled){
33622             cfg.cls += ' disabled';
33623         }
33624         
33625         return cfg;
33626     },
33627     
33628     disable : function()
33629     {
33630         this.setDisabled(true);
33631     },
33632     
33633     enable : function()
33634     {
33635         this.setDisabled(false);
33636     },
33637     
33638     initEvents: function() 
33639     {
33640         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33641         
33642         this.iconEl.on('click', this.onClick, this);
33643     },
33644     
33645     onClick : function(e)
33646     {
33647         e.preventDefault();
33648         
33649         if(this.disabled){
33650             return;
33651         }
33652         
33653         if(this.fireEvent('click', this, e) === false){
33654             return;
33655         };
33656         
33657         this.parent().setActiveItem(this);
33658     },
33659     
33660     isActive: function () 
33661     {
33662         return this.active;
33663     },
33664     
33665     setActive : function(state)
33666     {
33667         if(this.active == state){
33668             return;
33669         }
33670         
33671         this.active = state;
33672         
33673         if (state) {
33674             this.el.addClass('active');
33675             return;
33676         }
33677         
33678         this.el.removeClass('active');
33679         
33680         return;
33681     },
33682     
33683     setDisabled : function(state)
33684     {
33685         if(this.disabled == state){
33686             return;
33687         }
33688         
33689         this.disabled = state;
33690         
33691         if (state) {
33692             this.el.addClass('disabled');
33693             return;
33694         }
33695         
33696         this.el.removeClass('disabled');
33697     },
33698     
33699     tooltipEl : function()
33700     {
33701         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33702     }
33703 });
33704  
33705
33706  /*
33707  * - LGPL
33708  *
33709  * FieldLabel
33710  * 
33711  */
33712
33713 /**
33714  * @class Roo.bootstrap.FieldLabel
33715  * @extends Roo.bootstrap.Component
33716  * Bootstrap FieldLabel class
33717  * @cfg {String} html contents of the element
33718  * @cfg {String} tag tag of the element default label
33719  * @cfg {String} cls class of the element
33720  * @cfg {String} target label target 
33721  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33722  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33723  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33724  * @cfg {String} iconTooltip default "This field is required"
33725  * @cfg {String} indicatorpos (left|right) default left
33726  * 
33727  * @constructor
33728  * Create a new FieldLabel
33729  * @param {Object} config The config object
33730  */
33731
33732 Roo.bootstrap.FieldLabel = function(config){
33733     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33734     
33735     this.addEvents({
33736             /**
33737              * @event invalid
33738              * Fires after the field has been marked as invalid.
33739              * @param {Roo.form.FieldLabel} this
33740              * @param {String} msg The validation message
33741              */
33742             invalid : true,
33743             /**
33744              * @event valid
33745              * Fires after the field has been validated with no errors.
33746              * @param {Roo.form.FieldLabel} this
33747              */
33748             valid : true
33749         });
33750 };
33751
33752 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33753     
33754     tag: 'label',
33755     cls: '',
33756     html: '',
33757     target: '',
33758     allowBlank : true,
33759     invalidClass : 'has-warning',
33760     validClass : 'has-success',
33761     iconTooltip : 'This field is required',
33762     indicatorpos : 'left',
33763     
33764     getAutoCreate : function(){
33765         
33766         var cls = "";
33767         if (!this.allowBlank) {
33768             cls  = "visible";
33769         }
33770         
33771         var cfg = {
33772             tag : this.tag,
33773             cls : 'roo-bootstrap-field-label ' + this.cls,
33774             for : this.target,
33775             cn : [
33776                 {
33777                     tag : 'i',
33778                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33779                     tooltip : this.iconTooltip
33780                 },
33781                 {
33782                     tag : 'span',
33783                     html : this.html
33784                 }
33785             ] 
33786         };
33787         
33788         if(this.indicatorpos == 'right'){
33789             var cfg = {
33790                 tag : this.tag,
33791                 cls : 'roo-bootstrap-field-label ' + this.cls,
33792                 for : this.target,
33793                 cn : [
33794                     {
33795                         tag : 'span',
33796                         html : this.html
33797                     },
33798                     {
33799                         tag : 'i',
33800                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33801                         tooltip : this.iconTooltip
33802                     }
33803                 ] 
33804             };
33805         }
33806         
33807         return cfg;
33808     },
33809     
33810     initEvents: function() 
33811     {
33812         Roo.bootstrap.Element.superclass.initEvents.call(this);
33813         
33814         this.indicator = this.indicatorEl();
33815         
33816         if(this.indicator){
33817             this.indicator.removeClass('visible');
33818             this.indicator.addClass('invisible');
33819         }
33820         
33821         Roo.bootstrap.FieldLabel.register(this);
33822     },
33823     
33824     indicatorEl : function()
33825     {
33826         var indicator = this.el.select('i.roo-required-indicator',true).first();
33827         
33828         if(!indicator){
33829             return false;
33830         }
33831         
33832         return indicator;
33833         
33834     },
33835     
33836     /**
33837      * Mark this field as valid
33838      */
33839     markValid : function()
33840     {
33841         if(this.indicator){
33842             this.indicator.removeClass('visible');
33843             this.indicator.addClass('invisible');
33844         }
33845         if (Roo.bootstrap.version == 3) {
33846             this.el.removeClass(this.invalidClass);
33847             this.el.addClass(this.validClass);
33848         } else {
33849             this.el.removeClass('is-invalid');
33850             this.el.addClass('is-valid');
33851         }
33852         
33853         
33854         this.fireEvent('valid', this);
33855     },
33856     
33857     /**
33858      * Mark this field as invalid
33859      * @param {String} msg The validation message
33860      */
33861     markInvalid : function(msg)
33862     {
33863         if(this.indicator){
33864             this.indicator.removeClass('invisible');
33865             this.indicator.addClass('visible');
33866         }
33867           if (Roo.bootstrap.version == 3) {
33868             this.el.removeClass(this.validClass);
33869             this.el.addClass(this.invalidClass);
33870         } else {
33871             this.el.removeClass('is-valid');
33872             this.el.addClass('is-invalid');
33873         }
33874         
33875         
33876         this.fireEvent('invalid', this, msg);
33877     }
33878     
33879    
33880 });
33881
33882 Roo.apply(Roo.bootstrap.FieldLabel, {
33883     
33884     groups: {},
33885     
33886      /**
33887     * register a FieldLabel Group
33888     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33889     */
33890     register : function(label)
33891     {
33892         if(this.groups.hasOwnProperty(label.target)){
33893             return;
33894         }
33895      
33896         this.groups[label.target] = label;
33897         
33898     },
33899     /**
33900     * fetch a FieldLabel Group based on the target
33901     * @param {string} target
33902     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33903     */
33904     get: function(target) {
33905         if (typeof(this.groups[target]) == 'undefined') {
33906             return false;
33907         }
33908         
33909         return this.groups[target] ;
33910     }
33911 });
33912
33913  
33914
33915  /*
33916  * - LGPL
33917  *
33918  * page DateSplitField.
33919  * 
33920  */
33921
33922
33923 /**
33924  * @class Roo.bootstrap.DateSplitField
33925  * @extends Roo.bootstrap.Component
33926  * Bootstrap DateSplitField class
33927  * @cfg {string} fieldLabel - the label associated
33928  * @cfg {Number} labelWidth set the width of label (0-12)
33929  * @cfg {String} labelAlign (top|left)
33930  * @cfg {Boolean} dayAllowBlank (true|false) default false
33931  * @cfg {Boolean} monthAllowBlank (true|false) default false
33932  * @cfg {Boolean} yearAllowBlank (true|false) default false
33933  * @cfg {string} dayPlaceholder 
33934  * @cfg {string} monthPlaceholder
33935  * @cfg {string} yearPlaceholder
33936  * @cfg {string} dayFormat default 'd'
33937  * @cfg {string} monthFormat default 'm'
33938  * @cfg {string} yearFormat default 'Y'
33939  * @cfg {Number} labellg set the width of label (1-12)
33940  * @cfg {Number} labelmd set the width of label (1-12)
33941  * @cfg {Number} labelsm set the width of label (1-12)
33942  * @cfg {Number} labelxs set the width of label (1-12)
33943
33944  *     
33945  * @constructor
33946  * Create a new DateSplitField
33947  * @param {Object} config The config object
33948  */
33949
33950 Roo.bootstrap.DateSplitField = function(config){
33951     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33952     
33953     this.addEvents({
33954         // raw events
33955          /**
33956          * @event years
33957          * getting the data of years
33958          * @param {Roo.bootstrap.DateSplitField} this
33959          * @param {Object} years
33960          */
33961         "years" : true,
33962         /**
33963          * @event days
33964          * getting the data of days
33965          * @param {Roo.bootstrap.DateSplitField} this
33966          * @param {Object} days
33967          */
33968         "days" : true,
33969         /**
33970          * @event invalid
33971          * Fires after the field has been marked as invalid.
33972          * @param {Roo.form.Field} this
33973          * @param {String} msg The validation message
33974          */
33975         invalid : true,
33976        /**
33977          * @event valid
33978          * Fires after the field has been validated with no errors.
33979          * @param {Roo.form.Field} this
33980          */
33981         valid : true
33982     });
33983 };
33984
33985 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33986     
33987     fieldLabel : '',
33988     labelAlign : 'top',
33989     labelWidth : 3,
33990     dayAllowBlank : false,
33991     monthAllowBlank : false,
33992     yearAllowBlank : false,
33993     dayPlaceholder : '',
33994     monthPlaceholder : '',
33995     yearPlaceholder : '',
33996     dayFormat : 'd',
33997     monthFormat : 'm',
33998     yearFormat : 'Y',
33999     isFormField : true,
34000     labellg : 0,
34001     labelmd : 0,
34002     labelsm : 0,
34003     labelxs : 0,
34004     
34005     getAutoCreate : function()
34006     {
34007         var cfg = {
34008             tag : 'div',
34009             cls : 'row roo-date-split-field-group',
34010             cn : [
34011                 {
34012                     tag : 'input',
34013                     type : 'hidden',
34014                     cls : 'form-hidden-field roo-date-split-field-group-value',
34015                     name : this.name
34016                 }
34017             ]
34018         };
34019         
34020         var labelCls = 'col-md-12';
34021         var contentCls = 'col-md-4';
34022         
34023         if(this.fieldLabel){
34024             
34025             var label = {
34026                 tag : 'div',
34027                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34028                 cn : [
34029                     {
34030                         tag : 'label',
34031                         html : this.fieldLabel
34032                     }
34033                 ]
34034             };
34035             
34036             if(this.labelAlign == 'left'){
34037             
34038                 if(this.labelWidth > 12){
34039                     label.style = "width: " + this.labelWidth + 'px';
34040                 }
34041
34042                 if(this.labelWidth < 13 && this.labelmd == 0){
34043                     this.labelmd = this.labelWidth;
34044                 }
34045
34046                 if(this.labellg > 0){
34047                     labelCls = ' col-lg-' + this.labellg;
34048                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34049                 }
34050
34051                 if(this.labelmd > 0){
34052                     labelCls = ' col-md-' + this.labelmd;
34053                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34054                 }
34055
34056                 if(this.labelsm > 0){
34057                     labelCls = ' col-sm-' + this.labelsm;
34058                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34059                 }
34060
34061                 if(this.labelxs > 0){
34062                     labelCls = ' col-xs-' + this.labelxs;
34063                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34064                 }
34065             }
34066             
34067             label.cls += ' ' + labelCls;
34068             
34069             cfg.cn.push(label);
34070         }
34071         
34072         Roo.each(['day', 'month', 'year'], function(t){
34073             cfg.cn.push({
34074                 tag : 'div',
34075                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34076             });
34077         }, this);
34078         
34079         return cfg;
34080     },
34081     
34082     inputEl: function ()
34083     {
34084         return this.el.select('.roo-date-split-field-group-value', true).first();
34085     },
34086     
34087     onRender : function(ct, position) 
34088     {
34089         var _this = this;
34090         
34091         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34092         
34093         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34094         
34095         this.dayField = new Roo.bootstrap.ComboBox({
34096             allowBlank : this.dayAllowBlank,
34097             alwaysQuery : true,
34098             displayField : 'value',
34099             editable : false,
34100             fieldLabel : '',
34101             forceSelection : true,
34102             mode : 'local',
34103             placeholder : this.dayPlaceholder,
34104             selectOnFocus : true,
34105             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34106             triggerAction : 'all',
34107             typeAhead : true,
34108             valueField : 'value',
34109             store : new Roo.data.SimpleStore({
34110                 data : (function() {    
34111                     var days = [];
34112                     _this.fireEvent('days', _this, days);
34113                     return days;
34114                 })(),
34115                 fields : [ 'value' ]
34116             }),
34117             listeners : {
34118                 select : function (_self, record, index)
34119                 {
34120                     _this.setValue(_this.getValue());
34121                 }
34122             }
34123         });
34124
34125         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34126         
34127         this.monthField = new Roo.bootstrap.MonthField({
34128             after : '<i class=\"fa fa-calendar\"></i>',
34129             allowBlank : this.monthAllowBlank,
34130             placeholder : this.monthPlaceholder,
34131             readOnly : true,
34132             listeners : {
34133                 render : function (_self)
34134                 {
34135                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34136                         e.preventDefault();
34137                         _self.focus();
34138                     });
34139                 },
34140                 select : function (_self, oldvalue, newvalue)
34141                 {
34142                     _this.setValue(_this.getValue());
34143                 }
34144             }
34145         });
34146         
34147         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34148         
34149         this.yearField = new Roo.bootstrap.ComboBox({
34150             allowBlank : this.yearAllowBlank,
34151             alwaysQuery : true,
34152             displayField : 'value',
34153             editable : false,
34154             fieldLabel : '',
34155             forceSelection : true,
34156             mode : 'local',
34157             placeholder : this.yearPlaceholder,
34158             selectOnFocus : true,
34159             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34160             triggerAction : 'all',
34161             typeAhead : true,
34162             valueField : 'value',
34163             store : new Roo.data.SimpleStore({
34164                 data : (function() {
34165                     var years = [];
34166                     _this.fireEvent('years', _this, years);
34167                     return years;
34168                 })(),
34169                 fields : [ 'value' ]
34170             }),
34171             listeners : {
34172                 select : function (_self, record, index)
34173                 {
34174                     _this.setValue(_this.getValue());
34175                 }
34176             }
34177         });
34178
34179         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34180     },
34181     
34182     setValue : function(v, format)
34183     {
34184         this.inputEl.dom.value = v;
34185         
34186         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34187         
34188         var d = Date.parseDate(v, f);
34189         
34190         if(!d){
34191             this.validate();
34192             return;
34193         }
34194         
34195         this.setDay(d.format(this.dayFormat));
34196         this.setMonth(d.format(this.monthFormat));
34197         this.setYear(d.format(this.yearFormat));
34198         
34199         this.validate();
34200         
34201         return;
34202     },
34203     
34204     setDay : function(v)
34205     {
34206         this.dayField.setValue(v);
34207         this.inputEl.dom.value = this.getValue();
34208         this.validate();
34209         return;
34210     },
34211     
34212     setMonth : function(v)
34213     {
34214         this.monthField.setValue(v, true);
34215         this.inputEl.dom.value = this.getValue();
34216         this.validate();
34217         return;
34218     },
34219     
34220     setYear : function(v)
34221     {
34222         this.yearField.setValue(v);
34223         this.inputEl.dom.value = this.getValue();
34224         this.validate();
34225         return;
34226     },
34227     
34228     getDay : function()
34229     {
34230         return this.dayField.getValue();
34231     },
34232     
34233     getMonth : function()
34234     {
34235         return this.monthField.getValue();
34236     },
34237     
34238     getYear : function()
34239     {
34240         return this.yearField.getValue();
34241     },
34242     
34243     getValue : function()
34244     {
34245         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34246         
34247         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34248         
34249         return date;
34250     },
34251     
34252     reset : function()
34253     {
34254         this.setDay('');
34255         this.setMonth('');
34256         this.setYear('');
34257         this.inputEl.dom.value = '';
34258         this.validate();
34259         return;
34260     },
34261     
34262     validate : function()
34263     {
34264         var d = this.dayField.validate();
34265         var m = this.monthField.validate();
34266         var y = this.yearField.validate();
34267         
34268         var valid = true;
34269         
34270         if(
34271                 (!this.dayAllowBlank && !d) ||
34272                 (!this.monthAllowBlank && !m) ||
34273                 (!this.yearAllowBlank && !y)
34274         ){
34275             valid = false;
34276         }
34277         
34278         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34279             return valid;
34280         }
34281         
34282         if(valid){
34283             this.markValid();
34284             return valid;
34285         }
34286         
34287         this.markInvalid();
34288         
34289         return valid;
34290     },
34291     
34292     markValid : function()
34293     {
34294         
34295         var label = this.el.select('label', true).first();
34296         var icon = this.el.select('i.fa-star', true).first();
34297
34298         if(label && icon){
34299             icon.remove();
34300         }
34301         
34302         this.fireEvent('valid', this);
34303     },
34304     
34305      /**
34306      * Mark this field as invalid
34307      * @param {String} msg The validation message
34308      */
34309     markInvalid : function(msg)
34310     {
34311         
34312         var label = this.el.select('label', true).first();
34313         var icon = this.el.select('i.fa-star', true).first();
34314
34315         if(label && !icon){
34316             this.el.select('.roo-date-split-field-label', true).createChild({
34317                 tag : 'i',
34318                 cls : 'text-danger fa fa-lg fa-star',
34319                 tooltip : 'This field is required',
34320                 style : 'margin-right:5px;'
34321             }, label, true);
34322         }
34323         
34324         this.fireEvent('invalid', this, msg);
34325     },
34326     
34327     clearInvalid : function()
34328     {
34329         var label = this.el.select('label', true).first();
34330         var icon = this.el.select('i.fa-star', true).first();
34331
34332         if(label && icon){
34333             icon.remove();
34334         }
34335         
34336         this.fireEvent('valid', this);
34337     },
34338     
34339     getName: function()
34340     {
34341         return this.name;
34342     }
34343     
34344 });
34345
34346  /**
34347  *
34348  * This is based on 
34349  * http://masonry.desandro.com
34350  *
34351  * The idea is to render all the bricks based on vertical width...
34352  *
34353  * The original code extends 'outlayer' - we might need to use that....
34354  * 
34355  */
34356
34357
34358 /**
34359  * @class Roo.bootstrap.LayoutMasonry
34360  * @extends Roo.bootstrap.Component
34361  * Bootstrap Layout Masonry class
34362  * 
34363  * @constructor
34364  * Create a new Element
34365  * @param {Object} config The config object
34366  */
34367
34368 Roo.bootstrap.LayoutMasonry = function(config){
34369     
34370     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34371     
34372     this.bricks = [];
34373     
34374     Roo.bootstrap.LayoutMasonry.register(this);
34375     
34376     this.addEvents({
34377         // raw events
34378         /**
34379          * @event layout
34380          * Fire after layout the items
34381          * @param {Roo.bootstrap.LayoutMasonry} this
34382          * @param {Roo.EventObject} e
34383          */
34384         "layout" : true
34385     });
34386     
34387 };
34388
34389 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34390     
34391     /**
34392      * @cfg {Boolean} isLayoutInstant = no animation?
34393      */   
34394     isLayoutInstant : false, // needed?
34395    
34396     /**
34397      * @cfg {Number} boxWidth  width of the columns
34398      */   
34399     boxWidth : 450,
34400     
34401       /**
34402      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34403      */   
34404     boxHeight : 0,
34405     
34406     /**
34407      * @cfg {Number} padWidth padding below box..
34408      */   
34409     padWidth : 10, 
34410     
34411     /**
34412      * @cfg {Number} gutter gutter width..
34413      */   
34414     gutter : 10,
34415     
34416      /**
34417      * @cfg {Number} maxCols maximum number of columns
34418      */   
34419     
34420     maxCols: 0,
34421     
34422     /**
34423      * @cfg {Boolean} isAutoInitial defalut true
34424      */   
34425     isAutoInitial : true, 
34426     
34427     containerWidth: 0,
34428     
34429     /**
34430      * @cfg {Boolean} isHorizontal defalut false
34431      */   
34432     isHorizontal : false, 
34433
34434     currentSize : null,
34435     
34436     tag: 'div',
34437     
34438     cls: '',
34439     
34440     bricks: null, //CompositeElement
34441     
34442     cols : 1,
34443     
34444     _isLayoutInited : false,
34445     
34446 //    isAlternative : false, // only use for vertical layout...
34447     
34448     /**
34449      * @cfg {Number} alternativePadWidth padding below box..
34450      */   
34451     alternativePadWidth : 50,
34452     
34453     selectedBrick : [],
34454     
34455     getAutoCreate : function(){
34456         
34457         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34458         
34459         var cfg = {
34460             tag: this.tag,
34461             cls: 'blog-masonary-wrapper ' + this.cls,
34462             cn : {
34463                 cls : 'mas-boxes masonary'
34464             }
34465         };
34466         
34467         return cfg;
34468     },
34469     
34470     getChildContainer: function( )
34471     {
34472         if (this.boxesEl) {
34473             return this.boxesEl;
34474         }
34475         
34476         this.boxesEl = this.el.select('.mas-boxes').first();
34477         
34478         return this.boxesEl;
34479     },
34480     
34481     
34482     initEvents : function()
34483     {
34484         var _this = this;
34485         
34486         if(this.isAutoInitial){
34487             Roo.log('hook children rendered');
34488             this.on('childrenrendered', function() {
34489                 Roo.log('children rendered');
34490                 _this.initial();
34491             } ,this);
34492         }
34493     },
34494     
34495     initial : function()
34496     {
34497         this.selectedBrick = [];
34498         
34499         this.currentSize = this.el.getBox(true);
34500         
34501         Roo.EventManager.onWindowResize(this.resize, this); 
34502
34503         if(!this.isAutoInitial){
34504             this.layout();
34505             return;
34506         }
34507         
34508         this.layout();
34509         
34510         return;
34511         //this.layout.defer(500,this);
34512         
34513     },
34514     
34515     resize : function()
34516     {
34517         var cs = this.el.getBox(true);
34518         
34519         if (
34520                 this.currentSize.width == cs.width && 
34521                 this.currentSize.x == cs.x && 
34522                 this.currentSize.height == cs.height && 
34523                 this.currentSize.y == cs.y 
34524         ) {
34525             Roo.log("no change in with or X or Y");
34526             return;
34527         }
34528         
34529         this.currentSize = cs;
34530         
34531         this.layout();
34532         
34533     },
34534     
34535     layout : function()
34536     {   
34537         this._resetLayout();
34538         
34539         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34540         
34541         this.layoutItems( isInstant );
34542       
34543         this._isLayoutInited = true;
34544         
34545         this.fireEvent('layout', this);
34546         
34547     },
34548     
34549     _resetLayout : function()
34550     {
34551         if(this.isHorizontal){
34552             this.horizontalMeasureColumns();
34553             return;
34554         }
34555         
34556         this.verticalMeasureColumns();
34557         
34558     },
34559     
34560     verticalMeasureColumns : function()
34561     {
34562         this.getContainerWidth();
34563         
34564 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34565 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34566 //            return;
34567 //        }
34568         
34569         var boxWidth = this.boxWidth + this.padWidth;
34570         
34571         if(this.containerWidth < this.boxWidth){
34572             boxWidth = this.containerWidth
34573         }
34574         
34575         var containerWidth = this.containerWidth;
34576         
34577         var cols = Math.floor(containerWidth / boxWidth);
34578         
34579         this.cols = Math.max( cols, 1 );
34580         
34581         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34582         
34583         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34584         
34585         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34586         
34587         this.colWidth = boxWidth + avail - this.padWidth;
34588         
34589         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34590         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34591     },
34592     
34593     horizontalMeasureColumns : function()
34594     {
34595         this.getContainerWidth();
34596         
34597         var boxWidth = this.boxWidth;
34598         
34599         if(this.containerWidth < boxWidth){
34600             boxWidth = this.containerWidth;
34601         }
34602         
34603         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34604         
34605         this.el.setHeight(boxWidth);
34606         
34607     },
34608     
34609     getContainerWidth : function()
34610     {
34611         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34612     },
34613     
34614     layoutItems : function( isInstant )
34615     {
34616         Roo.log(this.bricks);
34617         
34618         var items = Roo.apply([], this.bricks);
34619         
34620         if(this.isHorizontal){
34621             this._horizontalLayoutItems( items , isInstant );
34622             return;
34623         }
34624         
34625 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34626 //            this._verticalAlternativeLayoutItems( items , isInstant );
34627 //            return;
34628 //        }
34629         
34630         this._verticalLayoutItems( items , isInstant );
34631         
34632     },
34633     
34634     _verticalLayoutItems : function ( items , isInstant)
34635     {
34636         if ( !items || !items.length ) {
34637             return;
34638         }
34639         
34640         var standard = [
34641             ['xs', 'xs', 'xs', 'tall'],
34642             ['xs', 'xs', 'tall'],
34643             ['xs', 'xs', 'sm'],
34644             ['xs', 'xs', 'xs'],
34645             ['xs', 'tall'],
34646             ['xs', 'sm'],
34647             ['xs', 'xs'],
34648             ['xs'],
34649             
34650             ['sm', 'xs', 'xs'],
34651             ['sm', 'xs'],
34652             ['sm'],
34653             
34654             ['tall', 'xs', 'xs', 'xs'],
34655             ['tall', 'xs', 'xs'],
34656             ['tall', 'xs'],
34657             ['tall']
34658             
34659         ];
34660         
34661         var queue = [];
34662         
34663         var boxes = [];
34664         
34665         var box = [];
34666         
34667         Roo.each(items, function(item, k){
34668             
34669             switch (item.size) {
34670                 // these layouts take up a full box,
34671                 case 'md' :
34672                 case 'md-left' :
34673                 case 'md-right' :
34674                 case 'wide' :
34675                     
34676                     if(box.length){
34677                         boxes.push(box);
34678                         box = [];
34679                     }
34680                     
34681                     boxes.push([item]);
34682                     
34683                     break;
34684                     
34685                 case 'xs' :
34686                 case 'sm' :
34687                 case 'tall' :
34688                     
34689                     box.push(item);
34690                     
34691                     break;
34692                 default :
34693                     break;
34694                     
34695             }
34696             
34697         }, this);
34698         
34699         if(box.length){
34700             boxes.push(box);
34701             box = [];
34702         }
34703         
34704         var filterPattern = function(box, length)
34705         {
34706             if(!box.length){
34707                 return;
34708             }
34709             
34710             var match = false;
34711             
34712             var pattern = box.slice(0, length);
34713             
34714             var format = [];
34715             
34716             Roo.each(pattern, function(i){
34717                 format.push(i.size);
34718             }, this);
34719             
34720             Roo.each(standard, function(s){
34721                 
34722                 if(String(s) != String(format)){
34723                     return;
34724                 }
34725                 
34726                 match = true;
34727                 return false;
34728                 
34729             }, this);
34730             
34731             if(!match && length == 1){
34732                 return;
34733             }
34734             
34735             if(!match){
34736                 filterPattern(box, length - 1);
34737                 return;
34738             }
34739                 
34740             queue.push(pattern);
34741
34742             box = box.slice(length, box.length);
34743
34744             filterPattern(box, 4);
34745
34746             return;
34747             
34748         }
34749         
34750         Roo.each(boxes, function(box, k){
34751             
34752             if(!box.length){
34753                 return;
34754             }
34755             
34756             if(box.length == 1){
34757                 queue.push(box);
34758                 return;
34759             }
34760             
34761             filterPattern(box, 4);
34762             
34763         }, this);
34764         
34765         this._processVerticalLayoutQueue( queue, isInstant );
34766         
34767     },
34768     
34769 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34770 //    {
34771 //        if ( !items || !items.length ) {
34772 //            return;
34773 //        }
34774 //
34775 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34776 //        
34777 //    },
34778     
34779     _horizontalLayoutItems : function ( items , isInstant)
34780     {
34781         if ( !items || !items.length || items.length < 3) {
34782             return;
34783         }
34784         
34785         items.reverse();
34786         
34787         var eItems = items.slice(0, 3);
34788         
34789         items = items.slice(3, items.length);
34790         
34791         var standard = [
34792             ['xs', 'xs', 'xs', 'wide'],
34793             ['xs', 'xs', 'wide'],
34794             ['xs', 'xs', 'sm'],
34795             ['xs', 'xs', 'xs'],
34796             ['xs', 'wide'],
34797             ['xs', 'sm'],
34798             ['xs', 'xs'],
34799             ['xs'],
34800             
34801             ['sm', 'xs', 'xs'],
34802             ['sm', 'xs'],
34803             ['sm'],
34804             
34805             ['wide', 'xs', 'xs', 'xs'],
34806             ['wide', 'xs', 'xs'],
34807             ['wide', 'xs'],
34808             ['wide'],
34809             
34810             ['wide-thin']
34811         ];
34812         
34813         var queue = [];
34814         
34815         var boxes = [];
34816         
34817         var box = [];
34818         
34819         Roo.each(items, function(item, k){
34820             
34821             switch (item.size) {
34822                 case 'md' :
34823                 case 'md-left' :
34824                 case 'md-right' :
34825                 case 'tall' :
34826                     
34827                     if(box.length){
34828                         boxes.push(box);
34829                         box = [];
34830                     }
34831                     
34832                     boxes.push([item]);
34833                     
34834                     break;
34835                     
34836                 case 'xs' :
34837                 case 'sm' :
34838                 case 'wide' :
34839                 case 'wide-thin' :
34840                     
34841                     box.push(item);
34842                     
34843                     break;
34844                 default :
34845                     break;
34846                     
34847             }
34848             
34849         }, this);
34850         
34851         if(box.length){
34852             boxes.push(box);
34853             box = [];
34854         }
34855         
34856         var filterPattern = function(box, length)
34857         {
34858             if(!box.length){
34859                 return;
34860             }
34861             
34862             var match = false;
34863             
34864             var pattern = box.slice(0, length);
34865             
34866             var format = [];
34867             
34868             Roo.each(pattern, function(i){
34869                 format.push(i.size);
34870             }, this);
34871             
34872             Roo.each(standard, function(s){
34873                 
34874                 if(String(s) != String(format)){
34875                     return;
34876                 }
34877                 
34878                 match = true;
34879                 return false;
34880                 
34881             }, this);
34882             
34883             if(!match && length == 1){
34884                 return;
34885             }
34886             
34887             if(!match){
34888                 filterPattern(box, length - 1);
34889                 return;
34890             }
34891                 
34892             queue.push(pattern);
34893
34894             box = box.slice(length, box.length);
34895
34896             filterPattern(box, 4);
34897
34898             return;
34899             
34900         }
34901         
34902         Roo.each(boxes, function(box, k){
34903             
34904             if(!box.length){
34905                 return;
34906             }
34907             
34908             if(box.length == 1){
34909                 queue.push(box);
34910                 return;
34911             }
34912             
34913             filterPattern(box, 4);
34914             
34915         }, this);
34916         
34917         
34918         var prune = [];
34919         
34920         var pos = this.el.getBox(true);
34921         
34922         var minX = pos.x;
34923         
34924         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34925         
34926         var hit_end = false;
34927         
34928         Roo.each(queue, function(box){
34929             
34930             if(hit_end){
34931                 
34932                 Roo.each(box, function(b){
34933                 
34934                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34935                     b.el.hide();
34936
34937                 }, this);
34938
34939                 return;
34940             }
34941             
34942             var mx = 0;
34943             
34944             Roo.each(box, function(b){
34945                 
34946                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34947                 b.el.show();
34948
34949                 mx = Math.max(mx, b.x);
34950                 
34951             }, this);
34952             
34953             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34954             
34955             if(maxX < minX){
34956                 
34957                 Roo.each(box, function(b){
34958                 
34959                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34960                     b.el.hide();
34961                     
34962                 }, this);
34963                 
34964                 hit_end = true;
34965                 
34966                 return;
34967             }
34968             
34969             prune.push(box);
34970             
34971         }, this);
34972         
34973         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34974     },
34975     
34976     /** Sets position of item in DOM
34977     * @param {Element} item
34978     * @param {Number} x - horizontal position
34979     * @param {Number} y - vertical position
34980     * @param {Boolean} isInstant - disables transitions
34981     */
34982     _processVerticalLayoutQueue : function( queue, isInstant )
34983     {
34984         var pos = this.el.getBox(true);
34985         var x = pos.x;
34986         var y = pos.y;
34987         var maxY = [];
34988         
34989         for (var i = 0; i < this.cols; i++){
34990             maxY[i] = pos.y;
34991         }
34992         
34993         Roo.each(queue, function(box, k){
34994             
34995             var col = k % this.cols;
34996             
34997             Roo.each(box, function(b,kk){
34998                 
34999                 b.el.position('absolute');
35000                 
35001                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35002                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35003                 
35004                 if(b.size == 'md-left' || b.size == 'md-right'){
35005                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35006                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35007                 }
35008                 
35009                 b.el.setWidth(width);
35010                 b.el.setHeight(height);
35011                 // iframe?
35012                 b.el.select('iframe',true).setSize(width,height);
35013                 
35014             }, this);
35015             
35016             for (var i = 0; i < this.cols; i++){
35017                 
35018                 if(maxY[i] < maxY[col]){
35019                     col = i;
35020                     continue;
35021                 }
35022                 
35023                 col = Math.min(col, i);
35024                 
35025             }
35026             
35027             x = pos.x + col * (this.colWidth + this.padWidth);
35028             
35029             y = maxY[col];
35030             
35031             var positions = [];
35032             
35033             switch (box.length){
35034                 case 1 :
35035                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35036                     break;
35037                 case 2 :
35038                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35039                     break;
35040                 case 3 :
35041                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35042                     break;
35043                 case 4 :
35044                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35045                     break;
35046                 default :
35047                     break;
35048             }
35049             
35050             Roo.each(box, function(b,kk){
35051                 
35052                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35053                 
35054                 var sz = b.el.getSize();
35055                 
35056                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35057                 
35058             }, this);
35059             
35060         }, this);
35061         
35062         var mY = 0;
35063         
35064         for (var i = 0; i < this.cols; i++){
35065             mY = Math.max(mY, maxY[i]);
35066         }
35067         
35068         this.el.setHeight(mY - pos.y);
35069         
35070     },
35071     
35072 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35073 //    {
35074 //        var pos = this.el.getBox(true);
35075 //        var x = pos.x;
35076 //        var y = pos.y;
35077 //        var maxX = pos.right;
35078 //        
35079 //        var maxHeight = 0;
35080 //        
35081 //        Roo.each(items, function(item, k){
35082 //            
35083 //            var c = k % 2;
35084 //            
35085 //            item.el.position('absolute');
35086 //                
35087 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35088 //
35089 //            item.el.setWidth(width);
35090 //
35091 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35092 //
35093 //            item.el.setHeight(height);
35094 //            
35095 //            if(c == 0){
35096 //                item.el.setXY([x, y], isInstant ? false : true);
35097 //            } else {
35098 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35099 //            }
35100 //            
35101 //            y = y + height + this.alternativePadWidth;
35102 //            
35103 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35104 //            
35105 //        }, this);
35106 //        
35107 //        this.el.setHeight(maxHeight);
35108 //        
35109 //    },
35110     
35111     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35112     {
35113         var pos = this.el.getBox(true);
35114         
35115         var minX = pos.x;
35116         var minY = pos.y;
35117         
35118         var maxX = pos.right;
35119         
35120         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35121         
35122         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35123         
35124         Roo.each(queue, function(box, k){
35125             
35126             Roo.each(box, function(b, kk){
35127                 
35128                 b.el.position('absolute');
35129                 
35130                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35131                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35132                 
35133                 if(b.size == 'md-left' || b.size == 'md-right'){
35134                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35135                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35136                 }
35137                 
35138                 b.el.setWidth(width);
35139                 b.el.setHeight(height);
35140                 
35141             }, this);
35142             
35143             if(!box.length){
35144                 return;
35145             }
35146             
35147             var positions = [];
35148             
35149             switch (box.length){
35150                 case 1 :
35151                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35152                     break;
35153                 case 2 :
35154                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35155                     break;
35156                 case 3 :
35157                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35158                     break;
35159                 case 4 :
35160                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35161                     break;
35162                 default :
35163                     break;
35164             }
35165             
35166             Roo.each(box, function(b,kk){
35167                 
35168                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35169                 
35170                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35171                 
35172             }, this);
35173             
35174         }, this);
35175         
35176     },
35177     
35178     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35179     {
35180         Roo.each(eItems, function(b,k){
35181             
35182             b.size = (k == 0) ? 'sm' : 'xs';
35183             b.x = (k == 0) ? 2 : 1;
35184             b.y = (k == 0) ? 2 : 1;
35185             
35186             b.el.position('absolute');
35187             
35188             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35189                 
35190             b.el.setWidth(width);
35191             
35192             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35193             
35194             b.el.setHeight(height);
35195             
35196         }, this);
35197
35198         var positions = [];
35199         
35200         positions.push({
35201             x : maxX - this.unitWidth * 2 - this.gutter,
35202             y : minY
35203         });
35204         
35205         positions.push({
35206             x : maxX - this.unitWidth,
35207             y : minY + (this.unitWidth + this.gutter) * 2
35208         });
35209         
35210         positions.push({
35211             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35212             y : minY
35213         });
35214         
35215         Roo.each(eItems, function(b,k){
35216             
35217             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35218
35219         }, this);
35220         
35221     },
35222     
35223     getVerticalOneBoxColPositions : function(x, y, box)
35224     {
35225         var pos = [];
35226         
35227         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35228         
35229         if(box[0].size == 'md-left'){
35230             rand = 0;
35231         }
35232         
35233         if(box[0].size == 'md-right'){
35234             rand = 1;
35235         }
35236         
35237         pos.push({
35238             x : x + (this.unitWidth + this.gutter) * rand,
35239             y : y
35240         });
35241         
35242         return pos;
35243     },
35244     
35245     getVerticalTwoBoxColPositions : function(x, y, box)
35246     {
35247         var pos = [];
35248         
35249         if(box[0].size == 'xs'){
35250             
35251             pos.push({
35252                 x : x,
35253                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35254             });
35255
35256             pos.push({
35257                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35258                 y : y
35259             });
35260             
35261             return pos;
35262             
35263         }
35264         
35265         pos.push({
35266             x : x,
35267             y : y
35268         });
35269
35270         pos.push({
35271             x : x + (this.unitWidth + this.gutter) * 2,
35272             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35273         });
35274         
35275         return pos;
35276         
35277     },
35278     
35279     getVerticalThreeBoxColPositions : function(x, y, box)
35280     {
35281         var pos = [];
35282         
35283         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35284             
35285             pos.push({
35286                 x : x,
35287                 y : y
35288             });
35289
35290             pos.push({
35291                 x : x + (this.unitWidth + this.gutter) * 1,
35292                 y : y
35293             });
35294             
35295             pos.push({
35296                 x : x + (this.unitWidth + this.gutter) * 2,
35297                 y : y
35298             });
35299             
35300             return pos;
35301             
35302         }
35303         
35304         if(box[0].size == 'xs' && box[1].size == 'xs'){
35305             
35306             pos.push({
35307                 x : x,
35308                 y : y
35309             });
35310
35311             pos.push({
35312                 x : x,
35313                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35314             });
35315             
35316             pos.push({
35317                 x : x + (this.unitWidth + this.gutter) * 1,
35318                 y : y
35319             });
35320             
35321             return pos;
35322             
35323         }
35324         
35325         pos.push({
35326             x : x,
35327             y : y
35328         });
35329
35330         pos.push({
35331             x : x + (this.unitWidth + this.gutter) * 2,
35332             y : y
35333         });
35334
35335         pos.push({
35336             x : x + (this.unitWidth + this.gutter) * 2,
35337             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35338         });
35339             
35340         return pos;
35341         
35342     },
35343     
35344     getVerticalFourBoxColPositions : function(x, y, box)
35345     {
35346         var pos = [];
35347         
35348         if(box[0].size == 'xs'){
35349             
35350             pos.push({
35351                 x : x,
35352                 y : y
35353             });
35354
35355             pos.push({
35356                 x : x,
35357                 y : y + (this.unitHeight + this.gutter) * 1
35358             });
35359             
35360             pos.push({
35361                 x : x,
35362                 y : y + (this.unitHeight + this.gutter) * 2
35363             });
35364             
35365             pos.push({
35366                 x : x + (this.unitWidth + this.gutter) * 1,
35367                 y : y
35368             });
35369             
35370             return pos;
35371             
35372         }
35373         
35374         pos.push({
35375             x : x,
35376             y : y
35377         });
35378
35379         pos.push({
35380             x : x + (this.unitWidth + this.gutter) * 2,
35381             y : y
35382         });
35383
35384         pos.push({
35385             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35386             y : y + (this.unitHeight + this.gutter) * 1
35387         });
35388
35389         pos.push({
35390             x : x + (this.unitWidth + this.gutter) * 2,
35391             y : y + (this.unitWidth + this.gutter) * 2
35392         });
35393
35394         return pos;
35395         
35396     },
35397     
35398     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35399     {
35400         var pos = [];
35401         
35402         if(box[0].size == 'md-left'){
35403             pos.push({
35404                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35405                 y : minY
35406             });
35407             
35408             return pos;
35409         }
35410         
35411         if(box[0].size == 'md-right'){
35412             pos.push({
35413                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35414                 y : minY + (this.unitWidth + this.gutter) * 1
35415             });
35416             
35417             return pos;
35418         }
35419         
35420         var rand = Math.floor(Math.random() * (4 - box[0].y));
35421         
35422         pos.push({
35423             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35424             y : minY + (this.unitWidth + this.gutter) * rand
35425         });
35426         
35427         return pos;
35428         
35429     },
35430     
35431     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35432     {
35433         var pos = [];
35434         
35435         if(box[0].size == 'xs'){
35436             
35437             pos.push({
35438                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35439                 y : minY
35440             });
35441
35442             pos.push({
35443                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35444                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35445             });
35446             
35447             return pos;
35448             
35449         }
35450         
35451         pos.push({
35452             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35453             y : minY
35454         });
35455
35456         pos.push({
35457             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35458             y : minY + (this.unitWidth + this.gutter) * 2
35459         });
35460         
35461         return pos;
35462         
35463     },
35464     
35465     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35466     {
35467         var pos = [];
35468         
35469         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35470             
35471             pos.push({
35472                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35473                 y : minY
35474             });
35475
35476             pos.push({
35477                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35478                 y : minY + (this.unitWidth + this.gutter) * 1
35479             });
35480             
35481             pos.push({
35482                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35483                 y : minY + (this.unitWidth + this.gutter) * 2
35484             });
35485             
35486             return pos;
35487             
35488         }
35489         
35490         if(box[0].size == 'xs' && box[1].size == 'xs'){
35491             
35492             pos.push({
35493                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35494                 y : minY
35495             });
35496
35497             pos.push({
35498                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35499                 y : minY
35500             });
35501             
35502             pos.push({
35503                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35504                 y : minY + (this.unitWidth + this.gutter) * 1
35505             });
35506             
35507             return pos;
35508             
35509         }
35510         
35511         pos.push({
35512             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35513             y : minY
35514         });
35515
35516         pos.push({
35517             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35518             y : minY + (this.unitWidth + this.gutter) * 2
35519         });
35520
35521         pos.push({
35522             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35523             y : minY + (this.unitWidth + this.gutter) * 2
35524         });
35525             
35526         return pos;
35527         
35528     },
35529     
35530     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35531     {
35532         var pos = [];
35533         
35534         if(box[0].size == 'xs'){
35535             
35536             pos.push({
35537                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35538                 y : minY
35539             });
35540
35541             pos.push({
35542                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35543                 y : minY
35544             });
35545             
35546             pos.push({
35547                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35548                 y : minY
35549             });
35550             
35551             pos.push({
35552                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35553                 y : minY + (this.unitWidth + this.gutter) * 1
35554             });
35555             
35556             return pos;
35557             
35558         }
35559         
35560         pos.push({
35561             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35562             y : minY
35563         });
35564         
35565         pos.push({
35566             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35567             y : minY + (this.unitWidth + this.gutter) * 2
35568         });
35569         
35570         pos.push({
35571             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35572             y : minY + (this.unitWidth + this.gutter) * 2
35573         });
35574         
35575         pos.push({
35576             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35577             y : minY + (this.unitWidth + this.gutter) * 2
35578         });
35579
35580         return pos;
35581         
35582     },
35583     
35584     /**
35585     * remove a Masonry Brick
35586     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35587     */
35588     removeBrick : function(brick_id)
35589     {
35590         if (!brick_id) {
35591             return;
35592         }
35593         
35594         for (var i = 0; i<this.bricks.length; i++) {
35595             if (this.bricks[i].id == brick_id) {
35596                 this.bricks.splice(i,1);
35597                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35598                 this.initial();
35599             }
35600         }
35601     },
35602     
35603     /**
35604     * adds a Masonry Brick
35605     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35606     */
35607     addBrick : function(cfg)
35608     {
35609         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35610         //this.register(cn);
35611         cn.parentId = this.id;
35612         cn.render(this.el);
35613         return cn;
35614     },
35615     
35616     /**
35617     * register a Masonry Brick
35618     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35619     */
35620     
35621     register : function(brick)
35622     {
35623         this.bricks.push(brick);
35624         brick.masonryId = this.id;
35625     },
35626     
35627     /**
35628     * clear all the Masonry Brick
35629     */
35630     clearAll : function()
35631     {
35632         this.bricks = [];
35633         //this.getChildContainer().dom.innerHTML = "";
35634         this.el.dom.innerHTML = '';
35635     },
35636     
35637     getSelected : function()
35638     {
35639         if (!this.selectedBrick) {
35640             return false;
35641         }
35642         
35643         return this.selectedBrick;
35644     }
35645 });
35646
35647 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35648     
35649     groups: {},
35650      /**
35651     * register a Masonry Layout
35652     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35653     */
35654     
35655     register : function(layout)
35656     {
35657         this.groups[layout.id] = layout;
35658     },
35659     /**
35660     * fetch a  Masonry Layout based on the masonry layout ID
35661     * @param {string} the masonry layout to add
35662     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35663     */
35664     
35665     get: function(layout_id) {
35666         if (typeof(this.groups[layout_id]) == 'undefined') {
35667             return false;
35668         }
35669         return this.groups[layout_id] ;
35670     }
35671     
35672     
35673     
35674 });
35675
35676  
35677
35678  /**
35679  *
35680  * This is based on 
35681  * http://masonry.desandro.com
35682  *
35683  * The idea is to render all the bricks based on vertical width...
35684  *
35685  * The original code extends 'outlayer' - we might need to use that....
35686  * 
35687  */
35688
35689
35690 /**
35691  * @class Roo.bootstrap.LayoutMasonryAuto
35692  * @extends Roo.bootstrap.Component
35693  * Bootstrap Layout Masonry class
35694  * 
35695  * @constructor
35696  * Create a new Element
35697  * @param {Object} config The config object
35698  */
35699
35700 Roo.bootstrap.LayoutMasonryAuto = function(config){
35701     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35702 };
35703
35704 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35705     
35706       /**
35707      * @cfg {Boolean} isFitWidth  - resize the width..
35708      */   
35709     isFitWidth : false,  // options..
35710     /**
35711      * @cfg {Boolean} isOriginLeft = left align?
35712      */   
35713     isOriginLeft : true,
35714     /**
35715      * @cfg {Boolean} isOriginTop = top align?
35716      */   
35717     isOriginTop : false,
35718     /**
35719      * @cfg {Boolean} isLayoutInstant = no animation?
35720      */   
35721     isLayoutInstant : false, // needed?
35722     /**
35723      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35724      */   
35725     isResizingContainer : true,
35726     /**
35727      * @cfg {Number} columnWidth  width of the columns 
35728      */   
35729     
35730     columnWidth : 0,
35731     
35732     /**
35733      * @cfg {Number} maxCols maximum number of columns
35734      */   
35735     
35736     maxCols: 0,
35737     /**
35738      * @cfg {Number} padHeight padding below box..
35739      */   
35740     
35741     padHeight : 10, 
35742     
35743     /**
35744      * @cfg {Boolean} isAutoInitial defalut true
35745      */   
35746     
35747     isAutoInitial : true, 
35748     
35749     // private?
35750     gutter : 0,
35751     
35752     containerWidth: 0,
35753     initialColumnWidth : 0,
35754     currentSize : null,
35755     
35756     colYs : null, // array.
35757     maxY : 0,
35758     padWidth: 10,
35759     
35760     
35761     tag: 'div',
35762     cls: '',
35763     bricks: null, //CompositeElement
35764     cols : 0, // array?
35765     // element : null, // wrapped now this.el
35766     _isLayoutInited : null, 
35767     
35768     
35769     getAutoCreate : function(){
35770         
35771         var cfg = {
35772             tag: this.tag,
35773             cls: 'blog-masonary-wrapper ' + this.cls,
35774             cn : {
35775                 cls : 'mas-boxes masonary'
35776             }
35777         };
35778         
35779         return cfg;
35780     },
35781     
35782     getChildContainer: function( )
35783     {
35784         if (this.boxesEl) {
35785             return this.boxesEl;
35786         }
35787         
35788         this.boxesEl = this.el.select('.mas-boxes').first();
35789         
35790         return this.boxesEl;
35791     },
35792     
35793     
35794     initEvents : function()
35795     {
35796         var _this = this;
35797         
35798         if(this.isAutoInitial){
35799             Roo.log('hook children rendered');
35800             this.on('childrenrendered', function() {
35801                 Roo.log('children rendered');
35802                 _this.initial();
35803             } ,this);
35804         }
35805         
35806     },
35807     
35808     initial : function()
35809     {
35810         this.reloadItems();
35811
35812         this.currentSize = this.el.getBox(true);
35813
35814         /// was window resize... - let's see if this works..
35815         Roo.EventManager.onWindowResize(this.resize, this); 
35816
35817         if(!this.isAutoInitial){
35818             this.layout();
35819             return;
35820         }
35821         
35822         this.layout.defer(500,this);
35823     },
35824     
35825     reloadItems: function()
35826     {
35827         this.bricks = this.el.select('.masonry-brick', true);
35828         
35829         this.bricks.each(function(b) {
35830             //Roo.log(b.getSize());
35831             if (!b.attr('originalwidth')) {
35832                 b.attr('originalwidth',  b.getSize().width);
35833             }
35834             
35835         });
35836         
35837         Roo.log(this.bricks.elements.length);
35838     },
35839     
35840     resize : function()
35841     {
35842         Roo.log('resize');
35843         var cs = this.el.getBox(true);
35844         
35845         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35846             Roo.log("no change in with or X");
35847             return;
35848         }
35849         this.currentSize = cs;
35850         this.layout();
35851     },
35852     
35853     layout : function()
35854     {
35855          Roo.log('layout');
35856         this._resetLayout();
35857         //this._manageStamps();
35858       
35859         // don't animate first layout
35860         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35861         this.layoutItems( isInstant );
35862       
35863         // flag for initalized
35864         this._isLayoutInited = true;
35865     },
35866     
35867     layoutItems : function( isInstant )
35868     {
35869         //var items = this._getItemsForLayout( this.items );
35870         // original code supports filtering layout items.. we just ignore it..
35871         
35872         this._layoutItems( this.bricks , isInstant );
35873       
35874         this._postLayout();
35875     },
35876     _layoutItems : function ( items , isInstant)
35877     {
35878        //this.fireEvent( 'layout', this, items );
35879     
35880
35881         if ( !items || !items.elements.length ) {
35882           // no items, emit event with empty array
35883             return;
35884         }
35885
35886         var queue = [];
35887         items.each(function(item) {
35888             Roo.log("layout item");
35889             Roo.log(item);
35890             // get x/y object from method
35891             var position = this._getItemLayoutPosition( item );
35892             // enqueue
35893             position.item = item;
35894             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35895             queue.push( position );
35896         }, this);
35897       
35898         this._processLayoutQueue( queue );
35899     },
35900     /** Sets position of item in DOM
35901     * @param {Element} item
35902     * @param {Number} x - horizontal position
35903     * @param {Number} y - vertical position
35904     * @param {Boolean} isInstant - disables transitions
35905     */
35906     _processLayoutQueue : function( queue )
35907     {
35908         for ( var i=0, len = queue.length; i < len; i++ ) {
35909             var obj = queue[i];
35910             obj.item.position('absolute');
35911             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35912         }
35913     },
35914       
35915     
35916     /**
35917     * Any logic you want to do after each layout,
35918     * i.e. size the container
35919     */
35920     _postLayout : function()
35921     {
35922         this.resizeContainer();
35923     },
35924     
35925     resizeContainer : function()
35926     {
35927         if ( !this.isResizingContainer ) {
35928             return;
35929         }
35930         var size = this._getContainerSize();
35931         if ( size ) {
35932             this.el.setSize(size.width,size.height);
35933             this.boxesEl.setSize(size.width,size.height);
35934         }
35935     },
35936     
35937     
35938     
35939     _resetLayout : function()
35940     {
35941         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35942         this.colWidth = this.el.getWidth();
35943         //this.gutter = this.el.getWidth(); 
35944         
35945         this.measureColumns();
35946
35947         // reset column Y
35948         var i = this.cols;
35949         this.colYs = [];
35950         while (i--) {
35951             this.colYs.push( 0 );
35952         }
35953     
35954         this.maxY = 0;
35955     },
35956
35957     measureColumns : function()
35958     {
35959         this.getContainerWidth();
35960       // if columnWidth is 0, default to outerWidth of first item
35961         if ( !this.columnWidth ) {
35962             var firstItem = this.bricks.first();
35963             Roo.log(firstItem);
35964             this.columnWidth  = this.containerWidth;
35965             if (firstItem && firstItem.attr('originalwidth') ) {
35966                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35967             }
35968             // columnWidth fall back to item of first element
35969             Roo.log("set column width?");
35970                         this.initialColumnWidth = this.columnWidth  ;
35971
35972             // if first elem has no width, default to size of container
35973             
35974         }
35975         
35976         
35977         if (this.initialColumnWidth) {
35978             this.columnWidth = this.initialColumnWidth;
35979         }
35980         
35981         
35982             
35983         // column width is fixed at the top - however if container width get's smaller we should
35984         // reduce it...
35985         
35986         // this bit calcs how man columns..
35987             
35988         var columnWidth = this.columnWidth += this.gutter;
35989       
35990         // calculate columns
35991         var containerWidth = this.containerWidth + this.gutter;
35992         
35993         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35994         // fix rounding errors, typically with gutters
35995         var excess = columnWidth - containerWidth % columnWidth;
35996         
35997         
35998         // if overshoot is less than a pixel, round up, otherwise floor it
35999         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36000         cols = Math[ mathMethod ]( cols );
36001         this.cols = Math.max( cols, 1 );
36002         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36003         
36004          // padding positioning..
36005         var totalColWidth = this.cols * this.columnWidth;
36006         var padavail = this.containerWidth - totalColWidth;
36007         // so for 2 columns - we need 3 'pads'
36008         
36009         var padNeeded = (1+this.cols) * this.padWidth;
36010         
36011         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36012         
36013         this.columnWidth += padExtra
36014         //this.padWidth = Math.floor(padavail /  ( this.cols));
36015         
36016         // adjust colum width so that padding is fixed??
36017         
36018         // we have 3 columns ... total = width * 3
36019         // we have X left over... that should be used by 
36020         
36021         //if (this.expandC) {
36022             
36023         //}
36024         
36025         
36026         
36027     },
36028     
36029     getContainerWidth : function()
36030     {
36031        /* // container is parent if fit width
36032         var container = this.isFitWidth ? this.element.parentNode : this.element;
36033         // check that this.size and size are there
36034         // IE8 triggers resize on body size change, so they might not be
36035         
36036         var size = getSize( container );  //FIXME
36037         this.containerWidth = size && size.innerWidth; //FIXME
36038         */
36039          
36040         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36041         
36042     },
36043     
36044     _getItemLayoutPosition : function( item )  // what is item?
36045     {
36046         // we resize the item to our columnWidth..
36047       
36048         item.setWidth(this.columnWidth);
36049         item.autoBoxAdjust  = false;
36050         
36051         var sz = item.getSize();
36052  
36053         // how many columns does this brick span
36054         var remainder = this.containerWidth % this.columnWidth;
36055         
36056         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36057         // round if off by 1 pixel, otherwise use ceil
36058         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36059         colSpan = Math.min( colSpan, this.cols );
36060         
36061         // normally this should be '1' as we dont' currently allow multi width columns..
36062         
36063         var colGroup = this._getColGroup( colSpan );
36064         // get the minimum Y value from the columns
36065         var minimumY = Math.min.apply( Math, colGroup );
36066         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36067         
36068         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36069          
36070         // position the brick
36071         var position = {
36072             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36073             y: this.currentSize.y + minimumY + this.padHeight
36074         };
36075         
36076         Roo.log(position);
36077         // apply setHeight to necessary columns
36078         var setHeight = minimumY + sz.height + this.padHeight;
36079         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36080         
36081         var setSpan = this.cols + 1 - colGroup.length;
36082         for ( var i = 0; i < setSpan; i++ ) {
36083           this.colYs[ shortColIndex + i ] = setHeight ;
36084         }
36085       
36086         return position;
36087     },
36088     
36089     /**
36090      * @param {Number} colSpan - number of columns the element spans
36091      * @returns {Array} colGroup
36092      */
36093     _getColGroup : function( colSpan )
36094     {
36095         if ( colSpan < 2 ) {
36096           // if brick spans only one column, use all the column Ys
36097           return this.colYs;
36098         }
36099       
36100         var colGroup = [];
36101         // how many different places could this brick fit horizontally
36102         var groupCount = this.cols + 1 - colSpan;
36103         // for each group potential horizontal position
36104         for ( var i = 0; i < groupCount; i++ ) {
36105           // make an array of colY values for that one group
36106           var groupColYs = this.colYs.slice( i, i + colSpan );
36107           // and get the max value of the array
36108           colGroup[i] = Math.max.apply( Math, groupColYs );
36109         }
36110         return colGroup;
36111     },
36112     /*
36113     _manageStamp : function( stamp )
36114     {
36115         var stampSize =  stamp.getSize();
36116         var offset = stamp.getBox();
36117         // get the columns that this stamp affects
36118         var firstX = this.isOriginLeft ? offset.x : offset.right;
36119         var lastX = firstX + stampSize.width;
36120         var firstCol = Math.floor( firstX / this.columnWidth );
36121         firstCol = Math.max( 0, firstCol );
36122         
36123         var lastCol = Math.floor( lastX / this.columnWidth );
36124         // lastCol should not go over if multiple of columnWidth #425
36125         lastCol -= lastX % this.columnWidth ? 0 : 1;
36126         lastCol = Math.min( this.cols - 1, lastCol );
36127         
36128         // set colYs to bottom of the stamp
36129         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36130             stampSize.height;
36131             
36132         for ( var i = firstCol; i <= lastCol; i++ ) {
36133           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36134         }
36135     },
36136     */
36137     
36138     _getContainerSize : function()
36139     {
36140         this.maxY = Math.max.apply( Math, this.colYs );
36141         var size = {
36142             height: this.maxY
36143         };
36144       
36145         if ( this.isFitWidth ) {
36146             size.width = this._getContainerFitWidth();
36147         }
36148       
36149         return size;
36150     },
36151     
36152     _getContainerFitWidth : function()
36153     {
36154         var unusedCols = 0;
36155         // count unused columns
36156         var i = this.cols;
36157         while ( --i ) {
36158           if ( this.colYs[i] !== 0 ) {
36159             break;
36160           }
36161           unusedCols++;
36162         }
36163         // fit container to columns that have been used
36164         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36165     },
36166     
36167     needsResizeLayout : function()
36168     {
36169         var previousWidth = this.containerWidth;
36170         this.getContainerWidth();
36171         return previousWidth !== this.containerWidth;
36172     }
36173  
36174 });
36175
36176  
36177
36178  /*
36179  * - LGPL
36180  *
36181  * element
36182  * 
36183  */
36184
36185 /**
36186  * @class Roo.bootstrap.MasonryBrick
36187  * @extends Roo.bootstrap.Component
36188  * Bootstrap MasonryBrick class
36189  * 
36190  * @constructor
36191  * Create a new MasonryBrick
36192  * @param {Object} config The config object
36193  */
36194
36195 Roo.bootstrap.MasonryBrick = function(config){
36196     
36197     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36198     
36199     Roo.bootstrap.MasonryBrick.register(this);
36200     
36201     this.addEvents({
36202         // raw events
36203         /**
36204          * @event click
36205          * When a MasonryBrick is clcik
36206          * @param {Roo.bootstrap.MasonryBrick} this
36207          * @param {Roo.EventObject} e
36208          */
36209         "click" : true
36210     });
36211 };
36212
36213 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36214     
36215     /**
36216      * @cfg {String} title
36217      */   
36218     title : '',
36219     /**
36220      * @cfg {String} html
36221      */   
36222     html : '',
36223     /**
36224      * @cfg {String} bgimage
36225      */   
36226     bgimage : '',
36227     /**
36228      * @cfg {String} videourl
36229      */   
36230     videourl : '',
36231     /**
36232      * @cfg {String} cls
36233      */   
36234     cls : '',
36235     /**
36236      * @cfg {String} href
36237      */   
36238     href : '',
36239     /**
36240      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36241      */   
36242     size : 'xs',
36243     
36244     /**
36245      * @cfg {String} placetitle (center|bottom)
36246      */   
36247     placetitle : '',
36248     
36249     /**
36250      * @cfg {Boolean} isFitContainer defalut true
36251      */   
36252     isFitContainer : true, 
36253     
36254     /**
36255      * @cfg {Boolean} preventDefault defalut false
36256      */   
36257     preventDefault : false, 
36258     
36259     /**
36260      * @cfg {Boolean} inverse defalut false
36261      */   
36262     maskInverse : false, 
36263     
36264     getAutoCreate : function()
36265     {
36266         if(!this.isFitContainer){
36267             return this.getSplitAutoCreate();
36268         }
36269         
36270         var cls = 'masonry-brick masonry-brick-full';
36271         
36272         if(this.href.length){
36273             cls += ' masonry-brick-link';
36274         }
36275         
36276         if(this.bgimage.length){
36277             cls += ' masonry-brick-image';
36278         }
36279         
36280         if(this.maskInverse){
36281             cls += ' mask-inverse';
36282         }
36283         
36284         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36285             cls += ' enable-mask';
36286         }
36287         
36288         if(this.size){
36289             cls += ' masonry-' + this.size + '-brick';
36290         }
36291         
36292         if(this.placetitle.length){
36293             
36294             switch (this.placetitle) {
36295                 case 'center' :
36296                     cls += ' masonry-center-title';
36297                     break;
36298                 case 'bottom' :
36299                     cls += ' masonry-bottom-title';
36300                     break;
36301                 default:
36302                     break;
36303             }
36304             
36305         } else {
36306             if(!this.html.length && !this.bgimage.length){
36307                 cls += ' masonry-center-title';
36308             }
36309
36310             if(!this.html.length && this.bgimage.length){
36311                 cls += ' masonry-bottom-title';
36312             }
36313         }
36314         
36315         if(this.cls){
36316             cls += ' ' + this.cls;
36317         }
36318         
36319         var cfg = {
36320             tag: (this.href.length) ? 'a' : 'div',
36321             cls: cls,
36322             cn: [
36323                 {
36324                     tag: 'div',
36325                     cls: 'masonry-brick-mask'
36326                 },
36327                 {
36328                     tag: 'div',
36329                     cls: 'masonry-brick-paragraph',
36330                     cn: []
36331                 }
36332             ]
36333         };
36334         
36335         if(this.href.length){
36336             cfg.href = this.href;
36337         }
36338         
36339         var cn = cfg.cn[1].cn;
36340         
36341         if(this.title.length){
36342             cn.push({
36343                 tag: 'h4',
36344                 cls: 'masonry-brick-title',
36345                 html: this.title
36346             });
36347         }
36348         
36349         if(this.html.length){
36350             cn.push({
36351                 tag: 'p',
36352                 cls: 'masonry-brick-text',
36353                 html: this.html
36354             });
36355         }
36356         
36357         if (!this.title.length && !this.html.length) {
36358             cfg.cn[1].cls += ' hide';
36359         }
36360         
36361         if(this.bgimage.length){
36362             cfg.cn.push({
36363                 tag: 'img',
36364                 cls: 'masonry-brick-image-view',
36365                 src: this.bgimage
36366             });
36367         }
36368         
36369         if(this.videourl.length){
36370             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36371             // youtube support only?
36372             cfg.cn.push({
36373                 tag: 'iframe',
36374                 cls: 'masonry-brick-image-view',
36375                 src: vurl,
36376                 frameborder : 0,
36377                 allowfullscreen : true
36378             });
36379         }
36380         
36381         return cfg;
36382         
36383     },
36384     
36385     getSplitAutoCreate : function()
36386     {
36387         var cls = 'masonry-brick masonry-brick-split';
36388         
36389         if(this.href.length){
36390             cls += ' masonry-brick-link';
36391         }
36392         
36393         if(this.bgimage.length){
36394             cls += ' masonry-brick-image';
36395         }
36396         
36397         if(this.size){
36398             cls += ' masonry-' + this.size + '-brick';
36399         }
36400         
36401         switch (this.placetitle) {
36402             case 'center' :
36403                 cls += ' masonry-center-title';
36404                 break;
36405             case 'bottom' :
36406                 cls += ' masonry-bottom-title';
36407                 break;
36408             default:
36409                 if(!this.bgimage.length){
36410                     cls += ' masonry-center-title';
36411                 }
36412
36413                 if(this.bgimage.length){
36414                     cls += ' masonry-bottom-title';
36415                 }
36416                 break;
36417         }
36418         
36419         if(this.cls){
36420             cls += ' ' + this.cls;
36421         }
36422         
36423         var cfg = {
36424             tag: (this.href.length) ? 'a' : 'div',
36425             cls: cls,
36426             cn: [
36427                 {
36428                     tag: 'div',
36429                     cls: 'masonry-brick-split-head',
36430                     cn: [
36431                         {
36432                             tag: 'div',
36433                             cls: 'masonry-brick-paragraph',
36434                             cn: []
36435                         }
36436                     ]
36437                 },
36438                 {
36439                     tag: 'div',
36440                     cls: 'masonry-brick-split-body',
36441                     cn: []
36442                 }
36443             ]
36444         };
36445         
36446         if(this.href.length){
36447             cfg.href = this.href;
36448         }
36449         
36450         if(this.title.length){
36451             cfg.cn[0].cn[0].cn.push({
36452                 tag: 'h4',
36453                 cls: 'masonry-brick-title',
36454                 html: this.title
36455             });
36456         }
36457         
36458         if(this.html.length){
36459             cfg.cn[1].cn.push({
36460                 tag: 'p',
36461                 cls: 'masonry-brick-text',
36462                 html: this.html
36463             });
36464         }
36465
36466         if(this.bgimage.length){
36467             cfg.cn[0].cn.push({
36468                 tag: 'img',
36469                 cls: 'masonry-brick-image-view',
36470                 src: this.bgimage
36471             });
36472         }
36473         
36474         if(this.videourl.length){
36475             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36476             // youtube support only?
36477             cfg.cn[0].cn.cn.push({
36478                 tag: 'iframe',
36479                 cls: 'masonry-brick-image-view',
36480                 src: vurl,
36481                 frameborder : 0,
36482                 allowfullscreen : true
36483             });
36484         }
36485         
36486         return cfg;
36487     },
36488     
36489     initEvents: function() 
36490     {
36491         switch (this.size) {
36492             case 'xs' :
36493                 this.x = 1;
36494                 this.y = 1;
36495                 break;
36496             case 'sm' :
36497                 this.x = 2;
36498                 this.y = 2;
36499                 break;
36500             case 'md' :
36501             case 'md-left' :
36502             case 'md-right' :
36503                 this.x = 3;
36504                 this.y = 3;
36505                 break;
36506             case 'tall' :
36507                 this.x = 2;
36508                 this.y = 3;
36509                 break;
36510             case 'wide' :
36511                 this.x = 3;
36512                 this.y = 2;
36513                 break;
36514             case 'wide-thin' :
36515                 this.x = 3;
36516                 this.y = 1;
36517                 break;
36518                         
36519             default :
36520                 break;
36521         }
36522         
36523         if(Roo.isTouch){
36524             this.el.on('touchstart', this.onTouchStart, this);
36525             this.el.on('touchmove', this.onTouchMove, this);
36526             this.el.on('touchend', this.onTouchEnd, this);
36527             this.el.on('contextmenu', this.onContextMenu, this);
36528         } else {
36529             this.el.on('mouseenter'  ,this.enter, this);
36530             this.el.on('mouseleave', this.leave, this);
36531             this.el.on('click', this.onClick, this);
36532         }
36533         
36534         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36535             this.parent().bricks.push(this);   
36536         }
36537         
36538     },
36539     
36540     onClick: function(e, el)
36541     {
36542         var time = this.endTimer - this.startTimer;
36543         // Roo.log(e.preventDefault());
36544         if(Roo.isTouch){
36545             if(time > 1000){
36546                 e.preventDefault();
36547                 return;
36548             }
36549         }
36550         
36551         if(!this.preventDefault){
36552             return;
36553         }
36554         
36555         e.preventDefault();
36556         
36557         if (this.activeClass != '') {
36558             this.selectBrick();
36559         }
36560         
36561         this.fireEvent('click', this, e);
36562     },
36563     
36564     enter: function(e, el)
36565     {
36566         e.preventDefault();
36567         
36568         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36569             return;
36570         }
36571         
36572         if(this.bgimage.length && this.html.length){
36573             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36574         }
36575     },
36576     
36577     leave: function(e, el)
36578     {
36579         e.preventDefault();
36580         
36581         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36582             return;
36583         }
36584         
36585         if(this.bgimage.length && this.html.length){
36586             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36587         }
36588     },
36589     
36590     onTouchStart: function(e, el)
36591     {
36592 //        e.preventDefault();
36593         
36594         this.touchmoved = false;
36595         
36596         if(!this.isFitContainer){
36597             return;
36598         }
36599         
36600         if(!this.bgimage.length || !this.html.length){
36601             return;
36602         }
36603         
36604         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36605         
36606         this.timer = new Date().getTime();
36607         
36608     },
36609     
36610     onTouchMove: function(e, el)
36611     {
36612         this.touchmoved = true;
36613     },
36614     
36615     onContextMenu : function(e,el)
36616     {
36617         e.preventDefault();
36618         e.stopPropagation();
36619         return false;
36620     },
36621     
36622     onTouchEnd: function(e, el)
36623     {
36624 //        e.preventDefault();
36625         
36626         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36627         
36628             this.leave(e,el);
36629             
36630             return;
36631         }
36632         
36633         if(!this.bgimage.length || !this.html.length){
36634             
36635             if(this.href.length){
36636                 window.location.href = this.href;
36637             }
36638             
36639             return;
36640         }
36641         
36642         if(!this.isFitContainer){
36643             return;
36644         }
36645         
36646         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36647         
36648         window.location.href = this.href;
36649     },
36650     
36651     //selection on single brick only
36652     selectBrick : function() {
36653         
36654         if (!this.parentId) {
36655             return;
36656         }
36657         
36658         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36659         var index = m.selectedBrick.indexOf(this.id);
36660         
36661         if ( index > -1) {
36662             m.selectedBrick.splice(index,1);
36663             this.el.removeClass(this.activeClass);
36664             return;
36665         }
36666         
36667         for(var i = 0; i < m.selectedBrick.length; i++) {
36668             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36669             b.el.removeClass(b.activeClass);
36670         }
36671         
36672         m.selectedBrick = [];
36673         
36674         m.selectedBrick.push(this.id);
36675         this.el.addClass(this.activeClass);
36676         return;
36677     },
36678     
36679     isSelected : function(){
36680         return this.el.hasClass(this.activeClass);
36681         
36682     }
36683 });
36684
36685 Roo.apply(Roo.bootstrap.MasonryBrick, {
36686     
36687     //groups: {},
36688     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36689      /**
36690     * register a Masonry Brick
36691     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36692     */
36693     
36694     register : function(brick)
36695     {
36696         //this.groups[brick.id] = brick;
36697         this.groups.add(brick.id, brick);
36698     },
36699     /**
36700     * fetch a  masonry brick based on the masonry brick ID
36701     * @param {string} the masonry brick to add
36702     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36703     */
36704     
36705     get: function(brick_id) 
36706     {
36707         // if (typeof(this.groups[brick_id]) == 'undefined') {
36708         //     return false;
36709         // }
36710         // return this.groups[brick_id] ;
36711         
36712         if(this.groups.key(brick_id)) {
36713             return this.groups.key(brick_id);
36714         }
36715         
36716         return false;
36717     }
36718     
36719     
36720     
36721 });
36722
36723  /*
36724  * - LGPL
36725  *
36726  * element
36727  * 
36728  */
36729
36730 /**
36731  * @class Roo.bootstrap.Brick
36732  * @extends Roo.bootstrap.Component
36733  * Bootstrap Brick class
36734  * 
36735  * @constructor
36736  * Create a new Brick
36737  * @param {Object} config The config object
36738  */
36739
36740 Roo.bootstrap.Brick = function(config){
36741     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36742     
36743     this.addEvents({
36744         // raw events
36745         /**
36746          * @event click
36747          * When a Brick is click
36748          * @param {Roo.bootstrap.Brick} this
36749          * @param {Roo.EventObject} e
36750          */
36751         "click" : true
36752     });
36753 };
36754
36755 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36756     
36757     /**
36758      * @cfg {String} title
36759      */   
36760     title : '',
36761     /**
36762      * @cfg {String} html
36763      */   
36764     html : '',
36765     /**
36766      * @cfg {String} bgimage
36767      */   
36768     bgimage : '',
36769     /**
36770      * @cfg {String} cls
36771      */   
36772     cls : '',
36773     /**
36774      * @cfg {String} href
36775      */   
36776     href : '',
36777     /**
36778      * @cfg {String} video
36779      */   
36780     video : '',
36781     /**
36782      * @cfg {Boolean} square
36783      */   
36784     square : true,
36785     
36786     getAutoCreate : function()
36787     {
36788         var cls = 'roo-brick';
36789         
36790         if(this.href.length){
36791             cls += ' roo-brick-link';
36792         }
36793         
36794         if(this.bgimage.length){
36795             cls += ' roo-brick-image';
36796         }
36797         
36798         if(!this.html.length && !this.bgimage.length){
36799             cls += ' roo-brick-center-title';
36800         }
36801         
36802         if(!this.html.length && this.bgimage.length){
36803             cls += ' roo-brick-bottom-title';
36804         }
36805         
36806         if(this.cls){
36807             cls += ' ' + this.cls;
36808         }
36809         
36810         var cfg = {
36811             tag: (this.href.length) ? 'a' : 'div',
36812             cls: cls,
36813             cn: [
36814                 {
36815                     tag: 'div',
36816                     cls: 'roo-brick-paragraph',
36817                     cn: []
36818                 }
36819             ]
36820         };
36821         
36822         if(this.href.length){
36823             cfg.href = this.href;
36824         }
36825         
36826         var cn = cfg.cn[0].cn;
36827         
36828         if(this.title.length){
36829             cn.push({
36830                 tag: 'h4',
36831                 cls: 'roo-brick-title',
36832                 html: this.title
36833             });
36834         }
36835         
36836         if(this.html.length){
36837             cn.push({
36838                 tag: 'p',
36839                 cls: 'roo-brick-text',
36840                 html: this.html
36841             });
36842         } else {
36843             cn.cls += ' hide';
36844         }
36845         
36846         if(this.bgimage.length){
36847             cfg.cn.push({
36848                 tag: 'img',
36849                 cls: 'roo-brick-image-view',
36850                 src: this.bgimage
36851             });
36852         }
36853         
36854         return cfg;
36855     },
36856     
36857     initEvents: function() 
36858     {
36859         if(this.title.length || this.html.length){
36860             this.el.on('mouseenter'  ,this.enter, this);
36861             this.el.on('mouseleave', this.leave, this);
36862         }
36863         
36864         Roo.EventManager.onWindowResize(this.resize, this); 
36865         
36866         if(this.bgimage.length){
36867             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36868             this.imageEl.on('load', this.onImageLoad, this);
36869             return;
36870         }
36871         
36872         this.resize();
36873     },
36874     
36875     onImageLoad : function()
36876     {
36877         this.resize();
36878     },
36879     
36880     resize : function()
36881     {
36882         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36883         
36884         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36885         
36886         if(this.bgimage.length){
36887             var image = this.el.select('.roo-brick-image-view', true).first();
36888             
36889             image.setWidth(paragraph.getWidth());
36890             
36891             if(this.square){
36892                 image.setHeight(paragraph.getWidth());
36893             }
36894             
36895             this.el.setHeight(image.getHeight());
36896             paragraph.setHeight(image.getHeight());
36897             
36898         }
36899         
36900     },
36901     
36902     enter: function(e, el)
36903     {
36904         e.preventDefault();
36905         
36906         if(this.bgimage.length){
36907             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36908             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36909         }
36910     },
36911     
36912     leave: function(e, el)
36913     {
36914         e.preventDefault();
36915         
36916         if(this.bgimage.length){
36917             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36918             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36919         }
36920     }
36921     
36922 });
36923
36924  
36925
36926  /*
36927  * - LGPL
36928  *
36929  * Number field 
36930  */
36931
36932 /**
36933  * @class Roo.bootstrap.NumberField
36934  * @extends Roo.bootstrap.Input
36935  * Bootstrap NumberField class
36936  * 
36937  * 
36938  * 
36939  * 
36940  * @constructor
36941  * Create a new NumberField
36942  * @param {Object} config The config object
36943  */
36944
36945 Roo.bootstrap.NumberField = function(config){
36946     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36947 };
36948
36949 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36950     
36951     /**
36952      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36953      */
36954     allowDecimals : true,
36955     /**
36956      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36957      */
36958     decimalSeparator : ".",
36959     /**
36960      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36961      */
36962     decimalPrecision : 2,
36963     /**
36964      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36965      */
36966     allowNegative : true,
36967     
36968     /**
36969      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36970      */
36971     allowZero: true,
36972     /**
36973      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36974      */
36975     minValue : Number.NEGATIVE_INFINITY,
36976     /**
36977      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36978      */
36979     maxValue : Number.MAX_VALUE,
36980     /**
36981      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36982      */
36983     minText : "The minimum value for this field is {0}",
36984     /**
36985      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36986      */
36987     maxText : "The maximum value for this field is {0}",
36988     /**
36989      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36990      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36991      */
36992     nanText : "{0} is not a valid number",
36993     /**
36994      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36995      */
36996     thousandsDelimiter : false,
36997     /**
36998      * @cfg {String} valueAlign alignment of value
36999      */
37000     valueAlign : "left",
37001
37002     getAutoCreate : function()
37003     {
37004         var hiddenInput = {
37005             tag: 'input',
37006             type: 'hidden',
37007             id: Roo.id(),
37008             cls: 'hidden-number-input'
37009         };
37010         
37011         if (this.name) {
37012             hiddenInput.name = this.name;
37013         }
37014         
37015         this.name = '';
37016         
37017         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37018         
37019         this.name = hiddenInput.name;
37020         
37021         if(cfg.cn.length > 0) {
37022             cfg.cn.push(hiddenInput);
37023         }
37024         
37025         return cfg;
37026     },
37027
37028     // private
37029     initEvents : function()
37030     {   
37031         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37032         
37033         var allowed = "0123456789";
37034         
37035         if(this.allowDecimals){
37036             allowed += this.decimalSeparator;
37037         }
37038         
37039         if(this.allowNegative){
37040             allowed += "-";
37041         }
37042         
37043         if(this.thousandsDelimiter) {
37044             allowed += ",";
37045         }
37046         
37047         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37048         
37049         var keyPress = function(e){
37050             
37051             var k = e.getKey();
37052             
37053             var c = e.getCharCode();
37054             
37055             if(
37056                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37057                     allowed.indexOf(String.fromCharCode(c)) === -1
37058             ){
37059                 e.stopEvent();
37060                 return;
37061             }
37062             
37063             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37064                 return;
37065             }
37066             
37067             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37068                 e.stopEvent();
37069             }
37070         };
37071         
37072         this.el.on("keypress", keyPress, this);
37073     },
37074     
37075     validateValue : function(value)
37076     {
37077         
37078         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37079             return false;
37080         }
37081         
37082         var num = this.parseValue(value);
37083         
37084         if(isNaN(num)){
37085             this.markInvalid(String.format(this.nanText, value));
37086             return false;
37087         }
37088         
37089         if(num < this.minValue){
37090             this.markInvalid(String.format(this.minText, this.minValue));
37091             return false;
37092         }
37093         
37094         if(num > this.maxValue){
37095             this.markInvalid(String.format(this.maxText, this.maxValue));
37096             return false;
37097         }
37098         
37099         return true;
37100     },
37101
37102     getValue : function()
37103     {
37104         var v = this.hiddenEl().getValue();
37105         
37106         return this.fixPrecision(this.parseValue(v));
37107     },
37108
37109     parseValue : function(value)
37110     {
37111         if(this.thousandsDelimiter) {
37112             value += "";
37113             r = new RegExp(",", "g");
37114             value = value.replace(r, "");
37115         }
37116         
37117         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37118         return isNaN(value) ? '' : value;
37119     },
37120
37121     fixPrecision : function(value)
37122     {
37123         if(this.thousandsDelimiter) {
37124             value += "";
37125             r = new RegExp(",", "g");
37126             value = value.replace(r, "");
37127         }
37128         
37129         var nan = isNaN(value);
37130         
37131         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37132             return nan ? '' : value;
37133         }
37134         return parseFloat(value).toFixed(this.decimalPrecision);
37135     },
37136
37137     setValue : function(v)
37138     {
37139         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37140         
37141         this.value = v;
37142         
37143         if(this.rendered){
37144             
37145             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37146             
37147             this.inputEl().dom.value = (v == '') ? '' :
37148                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37149             
37150             if(!this.allowZero && v === '0') {
37151                 this.hiddenEl().dom.value = '';
37152                 this.inputEl().dom.value = '';
37153             }
37154             
37155             this.validate();
37156         }
37157     },
37158
37159     decimalPrecisionFcn : function(v)
37160     {
37161         return Math.floor(v);
37162     },
37163
37164     beforeBlur : function()
37165     {
37166         var v = this.parseValue(this.getRawValue());
37167         
37168         if(v || v === 0 || v === ''){
37169             this.setValue(v);
37170         }
37171     },
37172     
37173     hiddenEl : function()
37174     {
37175         return this.el.select('input.hidden-number-input',true).first();
37176     }
37177     
37178 });
37179
37180  
37181
37182 /*
37183 * Licence: LGPL
37184 */
37185
37186 /**
37187  * @class Roo.bootstrap.DocumentSlider
37188  * @extends Roo.bootstrap.Component
37189  * Bootstrap DocumentSlider class
37190  * 
37191  * @constructor
37192  * Create a new DocumentViewer
37193  * @param {Object} config The config object
37194  */
37195
37196 Roo.bootstrap.DocumentSlider = function(config){
37197     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37198     
37199     this.files = [];
37200     
37201     this.addEvents({
37202         /**
37203          * @event initial
37204          * Fire after initEvent
37205          * @param {Roo.bootstrap.DocumentSlider} this
37206          */
37207         "initial" : true,
37208         /**
37209          * @event update
37210          * Fire after update
37211          * @param {Roo.bootstrap.DocumentSlider} this
37212          */
37213         "update" : true,
37214         /**
37215          * @event click
37216          * Fire after click
37217          * @param {Roo.bootstrap.DocumentSlider} this
37218          */
37219         "click" : true
37220     });
37221 };
37222
37223 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37224     
37225     files : false,
37226     
37227     indicator : 0,
37228     
37229     getAutoCreate : function()
37230     {
37231         var cfg = {
37232             tag : 'div',
37233             cls : 'roo-document-slider',
37234             cn : [
37235                 {
37236                     tag : 'div',
37237                     cls : 'roo-document-slider-header',
37238                     cn : [
37239                         {
37240                             tag : 'div',
37241                             cls : 'roo-document-slider-header-title'
37242                         }
37243                     ]
37244                 },
37245                 {
37246                     tag : 'div',
37247                     cls : 'roo-document-slider-body',
37248                     cn : [
37249                         {
37250                             tag : 'div',
37251                             cls : 'roo-document-slider-prev',
37252                             cn : [
37253                                 {
37254                                     tag : 'i',
37255                                     cls : 'fa fa-chevron-left'
37256                                 }
37257                             ]
37258                         },
37259                         {
37260                             tag : 'div',
37261                             cls : 'roo-document-slider-thumb',
37262                             cn : [
37263                                 {
37264                                     tag : 'img',
37265                                     cls : 'roo-document-slider-image'
37266                                 }
37267                             ]
37268                         },
37269                         {
37270                             tag : 'div',
37271                             cls : 'roo-document-slider-next',
37272                             cn : [
37273                                 {
37274                                     tag : 'i',
37275                                     cls : 'fa fa-chevron-right'
37276                                 }
37277                             ]
37278                         }
37279                     ]
37280                 }
37281             ]
37282         };
37283         
37284         return cfg;
37285     },
37286     
37287     initEvents : function()
37288     {
37289         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37290         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37291         
37292         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37293         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37294         
37295         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37296         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37297         
37298         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37299         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37300         
37301         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37302         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37303         
37304         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37305         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37306         
37307         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37308         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37309         
37310         this.thumbEl.on('click', this.onClick, this);
37311         
37312         this.prevIndicator.on('click', this.prev, this);
37313         
37314         this.nextIndicator.on('click', this.next, this);
37315         
37316     },
37317     
37318     initial : function()
37319     {
37320         if(this.files.length){
37321             this.indicator = 1;
37322             this.update()
37323         }
37324         
37325         this.fireEvent('initial', this);
37326     },
37327     
37328     update : function()
37329     {
37330         this.imageEl.attr('src', this.files[this.indicator - 1]);
37331         
37332         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37333         
37334         this.prevIndicator.show();
37335         
37336         if(this.indicator == 1){
37337             this.prevIndicator.hide();
37338         }
37339         
37340         this.nextIndicator.show();
37341         
37342         if(this.indicator == this.files.length){
37343             this.nextIndicator.hide();
37344         }
37345         
37346         this.thumbEl.scrollTo('top');
37347         
37348         this.fireEvent('update', this);
37349     },
37350     
37351     onClick : function(e)
37352     {
37353         e.preventDefault();
37354         
37355         this.fireEvent('click', this);
37356     },
37357     
37358     prev : function(e)
37359     {
37360         e.preventDefault();
37361         
37362         this.indicator = Math.max(1, this.indicator - 1);
37363         
37364         this.update();
37365     },
37366     
37367     next : function(e)
37368     {
37369         e.preventDefault();
37370         
37371         this.indicator = Math.min(this.files.length, this.indicator + 1);
37372         
37373         this.update();
37374     }
37375 });
37376 /*
37377  * - LGPL
37378  *
37379  * RadioSet
37380  *
37381  *
37382  */
37383
37384 /**
37385  * @class Roo.bootstrap.RadioSet
37386  * @extends Roo.bootstrap.Input
37387  * Bootstrap RadioSet class
37388  * @cfg {String} indicatorpos (left|right) default left
37389  * @cfg {Boolean} inline (true|false) inline the element (default true)
37390  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37391  * @constructor
37392  * Create a new RadioSet
37393  * @param {Object} config The config object
37394  */
37395
37396 Roo.bootstrap.RadioSet = function(config){
37397     
37398     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37399     
37400     this.radioes = [];
37401     
37402     Roo.bootstrap.RadioSet.register(this);
37403     
37404     this.addEvents({
37405         /**
37406         * @event check
37407         * Fires when the element is checked or unchecked.
37408         * @param {Roo.bootstrap.RadioSet} this This radio
37409         * @param {Roo.bootstrap.Radio} item The checked item
37410         */
37411        check : true,
37412        /**
37413         * @event click
37414         * Fires when the element is click.
37415         * @param {Roo.bootstrap.RadioSet} this This radio set
37416         * @param {Roo.bootstrap.Radio} item The checked item
37417         * @param {Roo.EventObject} e The event object
37418         */
37419        click : true
37420     });
37421     
37422 };
37423
37424 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37425
37426     radioes : false,
37427     
37428     inline : true,
37429     
37430     weight : '',
37431     
37432     indicatorpos : 'left',
37433     
37434     getAutoCreate : function()
37435     {
37436         var label = {
37437             tag : 'label',
37438             cls : 'roo-radio-set-label',
37439             cn : [
37440                 {
37441                     tag : 'span',
37442                     html : this.fieldLabel
37443                 }
37444             ]
37445         };
37446         if (Roo.bootstrap.version == 3) {
37447             
37448             
37449             if(this.indicatorpos == 'left'){
37450                 label.cn.unshift({
37451                     tag : 'i',
37452                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37453                     tooltip : 'This field is required'
37454                 });
37455             } else {
37456                 label.cn.push({
37457                     tag : 'i',
37458                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37459                     tooltip : 'This field is required'
37460                 });
37461             }
37462         }
37463         var items = {
37464             tag : 'div',
37465             cls : 'roo-radio-set-items'
37466         };
37467         
37468         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37469         
37470         if (align === 'left' && this.fieldLabel.length) {
37471             
37472             items = {
37473                 cls : "roo-radio-set-right", 
37474                 cn: [
37475                     items
37476                 ]
37477             };
37478             
37479             if(this.labelWidth > 12){
37480                 label.style = "width: " + this.labelWidth + 'px';
37481             }
37482             
37483             if(this.labelWidth < 13 && this.labelmd == 0){
37484                 this.labelmd = this.labelWidth;
37485             }
37486             
37487             if(this.labellg > 0){
37488                 label.cls += ' col-lg-' + this.labellg;
37489                 items.cls += ' col-lg-' + (12 - this.labellg);
37490             }
37491             
37492             if(this.labelmd > 0){
37493                 label.cls += ' col-md-' + this.labelmd;
37494                 items.cls += ' col-md-' + (12 - this.labelmd);
37495             }
37496             
37497             if(this.labelsm > 0){
37498                 label.cls += ' col-sm-' + this.labelsm;
37499                 items.cls += ' col-sm-' + (12 - this.labelsm);
37500             }
37501             
37502             if(this.labelxs > 0){
37503                 label.cls += ' col-xs-' + this.labelxs;
37504                 items.cls += ' col-xs-' + (12 - this.labelxs);
37505             }
37506         }
37507         
37508         var cfg = {
37509             tag : 'div',
37510             cls : 'roo-radio-set',
37511             cn : [
37512                 {
37513                     tag : 'input',
37514                     cls : 'roo-radio-set-input',
37515                     type : 'hidden',
37516                     name : this.name,
37517                     value : this.value ? this.value :  ''
37518                 },
37519                 label,
37520                 items
37521             ]
37522         };
37523         
37524         if(this.weight.length){
37525             cfg.cls += ' roo-radio-' + this.weight;
37526         }
37527         
37528         if(this.inline) {
37529             cfg.cls += ' roo-radio-set-inline';
37530         }
37531         
37532         var settings=this;
37533         ['xs','sm','md','lg'].map(function(size){
37534             if (settings[size]) {
37535                 cfg.cls += ' col-' + size + '-' + settings[size];
37536             }
37537         });
37538         
37539         return cfg;
37540         
37541     },
37542
37543     initEvents : function()
37544     {
37545         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37546         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37547         
37548         if(!this.fieldLabel.length){
37549             this.labelEl.hide();
37550         }
37551         
37552         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37553         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37554         
37555         this.indicator = this.indicatorEl();
37556         
37557         if(this.indicator){
37558             this.indicator.addClass('invisible');
37559         }
37560         
37561         this.originalValue = this.getValue();
37562         
37563     },
37564     
37565     inputEl: function ()
37566     {
37567         return this.el.select('.roo-radio-set-input', true).first();
37568     },
37569     
37570     getChildContainer : function()
37571     {
37572         return this.itemsEl;
37573     },
37574     
37575     register : function(item)
37576     {
37577         this.radioes.push(item);
37578         
37579     },
37580     
37581     validate : function()
37582     {   
37583         if(this.getVisibilityEl().hasClass('hidden')){
37584             return true;
37585         }
37586         
37587         var valid = false;
37588         
37589         Roo.each(this.radioes, function(i){
37590             if(!i.checked){
37591                 return;
37592             }
37593             
37594             valid = true;
37595             return false;
37596         });
37597         
37598         if(this.allowBlank) {
37599             return true;
37600         }
37601         
37602         if(this.disabled || valid){
37603             this.markValid();
37604             return true;
37605         }
37606         
37607         this.markInvalid();
37608         return false;
37609         
37610     },
37611     
37612     markValid : function()
37613     {
37614         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37615             this.indicatorEl().removeClass('visible');
37616             this.indicatorEl().addClass('invisible');
37617         }
37618         
37619         
37620         if (Roo.bootstrap.version == 3) {
37621             this.el.removeClass([this.invalidClass, this.validClass]);
37622             this.el.addClass(this.validClass);
37623         } else {
37624             this.el.removeClass(['is-invalid','is-valid']);
37625             this.el.addClass(['is-valid']);
37626         }
37627         this.fireEvent('valid', this);
37628     },
37629     
37630     markInvalid : function(msg)
37631     {
37632         if(this.allowBlank || this.disabled){
37633             return;
37634         }
37635         
37636         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37637             this.indicatorEl().removeClass('invisible');
37638             this.indicatorEl().addClass('visible');
37639         }
37640         if (Roo.bootstrap.version == 3) {
37641             this.el.removeClass([this.invalidClass, this.validClass]);
37642             this.el.addClass(this.invalidClass);
37643         } else {
37644             this.el.removeClass(['is-invalid','is-valid']);
37645             this.el.addClass(['is-invalid']);
37646         }
37647         
37648         this.fireEvent('invalid', this, msg);
37649         
37650     },
37651     
37652     setValue : function(v, suppressEvent)
37653     {   
37654         if(this.value === v){
37655             return;
37656         }
37657         
37658         this.value = v;
37659         
37660         if(this.rendered){
37661             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37662         }
37663         
37664         Roo.each(this.radioes, function(i){
37665             i.checked = false;
37666             i.el.removeClass('checked');
37667         });
37668         
37669         Roo.each(this.radioes, function(i){
37670             
37671             if(i.value === v || i.value.toString() === v.toString()){
37672                 i.checked = true;
37673                 i.el.addClass('checked');
37674                 
37675                 if(suppressEvent !== true){
37676                     this.fireEvent('check', this, i);
37677                 }
37678                 
37679                 return false;
37680             }
37681             
37682         }, this);
37683         
37684         this.validate();
37685     },
37686     
37687     clearInvalid : function(){
37688         
37689         if(!this.el || this.preventMark){
37690             return;
37691         }
37692         
37693         this.el.removeClass([this.invalidClass]);
37694         
37695         this.fireEvent('valid', this);
37696     }
37697     
37698 });
37699
37700 Roo.apply(Roo.bootstrap.RadioSet, {
37701     
37702     groups: {},
37703     
37704     register : function(set)
37705     {
37706         this.groups[set.name] = set;
37707     },
37708     
37709     get: function(name) 
37710     {
37711         if (typeof(this.groups[name]) == 'undefined') {
37712             return false;
37713         }
37714         
37715         return this.groups[name] ;
37716     }
37717     
37718 });
37719 /*
37720  * Based on:
37721  * Ext JS Library 1.1.1
37722  * Copyright(c) 2006-2007, Ext JS, LLC.
37723  *
37724  * Originally Released Under LGPL - original licence link has changed is not relivant.
37725  *
37726  * Fork - LGPL
37727  * <script type="text/javascript">
37728  */
37729
37730
37731 /**
37732  * @class Roo.bootstrap.SplitBar
37733  * @extends Roo.util.Observable
37734  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37735  * <br><br>
37736  * Usage:
37737  * <pre><code>
37738 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37739                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37740 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37741 split.minSize = 100;
37742 split.maxSize = 600;
37743 split.animate = true;
37744 split.on('moved', splitterMoved);
37745 </code></pre>
37746  * @constructor
37747  * Create a new SplitBar
37748  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37749  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37750  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37751  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37752                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37753                         position of the SplitBar).
37754  */
37755 Roo.bootstrap.SplitBar = function(cfg){
37756     
37757     /** @private */
37758     
37759     //{
37760     //  dragElement : elm
37761     //  resizingElement: el,
37762         // optional..
37763     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37764     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37765         // existingProxy ???
37766     //}
37767     
37768     this.el = Roo.get(cfg.dragElement, true);
37769     this.el.dom.unselectable = "on";
37770     /** @private */
37771     this.resizingEl = Roo.get(cfg.resizingElement, true);
37772
37773     /**
37774      * @private
37775      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37776      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37777      * @type Number
37778      */
37779     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37780     
37781     /**
37782      * The minimum size of the resizing element. (Defaults to 0)
37783      * @type Number
37784      */
37785     this.minSize = 0;
37786     
37787     /**
37788      * The maximum size of the resizing element. (Defaults to 2000)
37789      * @type Number
37790      */
37791     this.maxSize = 2000;
37792     
37793     /**
37794      * Whether to animate the transition to the new size
37795      * @type Boolean
37796      */
37797     this.animate = false;
37798     
37799     /**
37800      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37801      * @type Boolean
37802      */
37803     this.useShim = false;
37804     
37805     /** @private */
37806     this.shim = null;
37807     
37808     if(!cfg.existingProxy){
37809         /** @private */
37810         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37811     }else{
37812         this.proxy = Roo.get(cfg.existingProxy).dom;
37813     }
37814     /** @private */
37815     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37816     
37817     /** @private */
37818     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37819     
37820     /** @private */
37821     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37822     
37823     /** @private */
37824     this.dragSpecs = {};
37825     
37826     /**
37827      * @private The adapter to use to positon and resize elements
37828      */
37829     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37830     this.adapter.init(this);
37831     
37832     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37833         /** @private */
37834         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37835         this.el.addClass("roo-splitbar-h");
37836     }else{
37837         /** @private */
37838         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37839         this.el.addClass("roo-splitbar-v");
37840     }
37841     
37842     this.addEvents({
37843         /**
37844          * @event resize
37845          * Fires when the splitter is moved (alias for {@link #event-moved})
37846          * @param {Roo.bootstrap.SplitBar} this
37847          * @param {Number} newSize the new width or height
37848          */
37849         "resize" : true,
37850         /**
37851          * @event moved
37852          * Fires when the splitter is moved
37853          * @param {Roo.bootstrap.SplitBar} this
37854          * @param {Number} newSize the new width or height
37855          */
37856         "moved" : true,
37857         /**
37858          * @event beforeresize
37859          * Fires before the splitter is dragged
37860          * @param {Roo.bootstrap.SplitBar} this
37861          */
37862         "beforeresize" : true,
37863
37864         "beforeapply" : true
37865     });
37866
37867     Roo.util.Observable.call(this);
37868 };
37869
37870 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37871     onStartProxyDrag : function(x, y){
37872         this.fireEvent("beforeresize", this);
37873         if(!this.overlay){
37874             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37875             o.unselectable();
37876             o.enableDisplayMode("block");
37877             // all splitbars share the same overlay
37878             Roo.bootstrap.SplitBar.prototype.overlay = o;
37879         }
37880         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37881         this.overlay.show();
37882         Roo.get(this.proxy).setDisplayed("block");
37883         var size = this.adapter.getElementSize(this);
37884         this.activeMinSize = this.getMinimumSize();;
37885         this.activeMaxSize = this.getMaximumSize();;
37886         var c1 = size - this.activeMinSize;
37887         var c2 = Math.max(this.activeMaxSize - size, 0);
37888         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37889             this.dd.resetConstraints();
37890             this.dd.setXConstraint(
37891                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37892                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37893             );
37894             this.dd.setYConstraint(0, 0);
37895         }else{
37896             this.dd.resetConstraints();
37897             this.dd.setXConstraint(0, 0);
37898             this.dd.setYConstraint(
37899                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37900                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37901             );
37902          }
37903         this.dragSpecs.startSize = size;
37904         this.dragSpecs.startPoint = [x, y];
37905         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37906     },
37907     
37908     /** 
37909      * @private Called after the drag operation by the DDProxy
37910      */
37911     onEndProxyDrag : function(e){
37912         Roo.get(this.proxy).setDisplayed(false);
37913         var endPoint = Roo.lib.Event.getXY(e);
37914         if(this.overlay){
37915             this.overlay.hide();
37916         }
37917         var newSize;
37918         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37919             newSize = this.dragSpecs.startSize + 
37920                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37921                     endPoint[0] - this.dragSpecs.startPoint[0] :
37922                     this.dragSpecs.startPoint[0] - endPoint[0]
37923                 );
37924         }else{
37925             newSize = this.dragSpecs.startSize + 
37926                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37927                     endPoint[1] - this.dragSpecs.startPoint[1] :
37928                     this.dragSpecs.startPoint[1] - endPoint[1]
37929                 );
37930         }
37931         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37932         if(newSize != this.dragSpecs.startSize){
37933             if(this.fireEvent('beforeapply', this, newSize) !== false){
37934                 this.adapter.setElementSize(this, newSize);
37935                 this.fireEvent("moved", this, newSize);
37936                 this.fireEvent("resize", this, newSize);
37937             }
37938         }
37939     },
37940     
37941     /**
37942      * Get the adapter this SplitBar uses
37943      * @return The adapter object
37944      */
37945     getAdapter : function(){
37946         return this.adapter;
37947     },
37948     
37949     /**
37950      * Set the adapter this SplitBar uses
37951      * @param {Object} adapter A SplitBar adapter object
37952      */
37953     setAdapter : function(adapter){
37954         this.adapter = adapter;
37955         this.adapter.init(this);
37956     },
37957     
37958     /**
37959      * Gets the minimum size for the resizing element
37960      * @return {Number} The minimum size
37961      */
37962     getMinimumSize : function(){
37963         return this.minSize;
37964     },
37965     
37966     /**
37967      * Sets the minimum size for the resizing element
37968      * @param {Number} minSize The minimum size
37969      */
37970     setMinimumSize : function(minSize){
37971         this.minSize = minSize;
37972     },
37973     
37974     /**
37975      * Gets the maximum size for the resizing element
37976      * @return {Number} The maximum size
37977      */
37978     getMaximumSize : function(){
37979         return this.maxSize;
37980     },
37981     
37982     /**
37983      * Sets the maximum size for the resizing element
37984      * @param {Number} maxSize The maximum size
37985      */
37986     setMaximumSize : function(maxSize){
37987         this.maxSize = maxSize;
37988     },
37989     
37990     /**
37991      * Sets the initialize size for the resizing element
37992      * @param {Number} size The initial size
37993      */
37994     setCurrentSize : function(size){
37995         var oldAnimate = this.animate;
37996         this.animate = false;
37997         this.adapter.setElementSize(this, size);
37998         this.animate = oldAnimate;
37999     },
38000     
38001     /**
38002      * Destroy this splitbar. 
38003      * @param {Boolean} removeEl True to remove the element
38004      */
38005     destroy : function(removeEl){
38006         if(this.shim){
38007             this.shim.remove();
38008         }
38009         this.dd.unreg();
38010         this.proxy.parentNode.removeChild(this.proxy);
38011         if(removeEl){
38012             this.el.remove();
38013         }
38014     }
38015 });
38016
38017 /**
38018  * @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.
38019  */
38020 Roo.bootstrap.SplitBar.createProxy = function(dir){
38021     var proxy = new Roo.Element(document.createElement("div"));
38022     proxy.unselectable();
38023     var cls = 'roo-splitbar-proxy';
38024     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38025     document.body.appendChild(proxy.dom);
38026     return proxy.dom;
38027 };
38028
38029 /** 
38030  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38031  * Default Adapter. It assumes the splitter and resizing element are not positioned
38032  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38033  */
38034 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38035 };
38036
38037 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38038     // do nothing for now
38039     init : function(s){
38040     
38041     },
38042     /**
38043      * Called before drag operations to get the current size of the resizing element. 
38044      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38045      */
38046      getElementSize : function(s){
38047         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38048             return s.resizingEl.getWidth();
38049         }else{
38050             return s.resizingEl.getHeight();
38051         }
38052     },
38053     
38054     /**
38055      * Called after drag operations to set the size of the resizing element.
38056      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38057      * @param {Number} newSize The new size to set
38058      * @param {Function} onComplete A function to be invoked when resizing is complete
38059      */
38060     setElementSize : function(s, newSize, onComplete){
38061         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38062             if(!s.animate){
38063                 s.resizingEl.setWidth(newSize);
38064                 if(onComplete){
38065                     onComplete(s, newSize);
38066                 }
38067             }else{
38068                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38069             }
38070         }else{
38071             
38072             if(!s.animate){
38073                 s.resizingEl.setHeight(newSize);
38074                 if(onComplete){
38075                     onComplete(s, newSize);
38076                 }
38077             }else{
38078                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38079             }
38080         }
38081     }
38082 };
38083
38084 /** 
38085  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38086  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38087  * Adapter that  moves the splitter element to align with the resized sizing element. 
38088  * Used with an absolute positioned SplitBar.
38089  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38090  * document.body, make sure you assign an id to the body element.
38091  */
38092 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38093     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38094     this.container = Roo.get(container);
38095 };
38096
38097 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38098     init : function(s){
38099         this.basic.init(s);
38100     },
38101     
38102     getElementSize : function(s){
38103         return this.basic.getElementSize(s);
38104     },
38105     
38106     setElementSize : function(s, newSize, onComplete){
38107         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38108     },
38109     
38110     moveSplitter : function(s){
38111         var yes = Roo.bootstrap.SplitBar;
38112         switch(s.placement){
38113             case yes.LEFT:
38114                 s.el.setX(s.resizingEl.getRight());
38115                 break;
38116             case yes.RIGHT:
38117                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38118                 break;
38119             case yes.TOP:
38120                 s.el.setY(s.resizingEl.getBottom());
38121                 break;
38122             case yes.BOTTOM:
38123                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38124                 break;
38125         }
38126     }
38127 };
38128
38129 /**
38130  * Orientation constant - Create a vertical SplitBar
38131  * @static
38132  * @type Number
38133  */
38134 Roo.bootstrap.SplitBar.VERTICAL = 1;
38135
38136 /**
38137  * Orientation constant - Create a horizontal SplitBar
38138  * @static
38139  * @type Number
38140  */
38141 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38142
38143 /**
38144  * Placement constant - The resizing element is to the left of the splitter element
38145  * @static
38146  * @type Number
38147  */
38148 Roo.bootstrap.SplitBar.LEFT = 1;
38149
38150 /**
38151  * Placement constant - The resizing element is to the right of the splitter element
38152  * @static
38153  * @type Number
38154  */
38155 Roo.bootstrap.SplitBar.RIGHT = 2;
38156
38157 /**
38158  * Placement constant - The resizing element is positioned above the splitter element
38159  * @static
38160  * @type Number
38161  */
38162 Roo.bootstrap.SplitBar.TOP = 3;
38163
38164 /**
38165  * Placement constant - The resizing element is positioned under splitter element
38166  * @static
38167  * @type Number
38168  */
38169 Roo.bootstrap.SplitBar.BOTTOM = 4;
38170 Roo.namespace("Roo.bootstrap.layout");/*
38171  * Based on:
38172  * Ext JS Library 1.1.1
38173  * Copyright(c) 2006-2007, Ext JS, LLC.
38174  *
38175  * Originally Released Under LGPL - original licence link has changed is not relivant.
38176  *
38177  * Fork - LGPL
38178  * <script type="text/javascript">
38179  */
38180
38181 /**
38182  * @class Roo.bootstrap.layout.Manager
38183  * @extends Roo.bootstrap.Component
38184  * Base class for layout managers.
38185  */
38186 Roo.bootstrap.layout.Manager = function(config)
38187 {
38188     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38189
38190
38191
38192
38193
38194     /** false to disable window resize monitoring @type Boolean */
38195     this.monitorWindowResize = true;
38196     this.regions = {};
38197     this.addEvents({
38198         /**
38199          * @event layout
38200          * Fires when a layout is performed.
38201          * @param {Roo.LayoutManager} this
38202          */
38203         "layout" : true,
38204         /**
38205          * @event regionresized
38206          * Fires when the user resizes a region.
38207          * @param {Roo.LayoutRegion} region The resized region
38208          * @param {Number} newSize The new size (width for east/west, height for north/south)
38209          */
38210         "regionresized" : true,
38211         /**
38212          * @event regioncollapsed
38213          * Fires when a region is collapsed.
38214          * @param {Roo.LayoutRegion} region The collapsed region
38215          */
38216         "regioncollapsed" : true,
38217         /**
38218          * @event regionexpanded
38219          * Fires when a region is expanded.
38220          * @param {Roo.LayoutRegion} region The expanded region
38221          */
38222         "regionexpanded" : true
38223     });
38224     this.updating = false;
38225
38226     if (config.el) {
38227         this.el = Roo.get(config.el);
38228         this.initEvents();
38229     }
38230
38231 };
38232
38233 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38234
38235
38236     regions : null,
38237
38238     monitorWindowResize : true,
38239
38240
38241     updating : false,
38242
38243
38244     onRender : function(ct, position)
38245     {
38246         if(!this.el){
38247             this.el = Roo.get(ct);
38248             this.initEvents();
38249         }
38250         //this.fireEvent('render',this);
38251     },
38252
38253
38254     initEvents: function()
38255     {
38256
38257
38258         // ie scrollbar fix
38259         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38260             document.body.scroll = "no";
38261         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38262             this.el.position('relative');
38263         }
38264         this.id = this.el.id;
38265         this.el.addClass("roo-layout-container");
38266         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38267         if(this.el.dom != document.body ) {
38268             this.el.on('resize', this.layout,this);
38269             this.el.on('show', this.layout,this);
38270         }
38271
38272     },
38273
38274     /**
38275      * Returns true if this layout is currently being updated
38276      * @return {Boolean}
38277      */
38278     isUpdating : function(){
38279         return this.updating;
38280     },
38281
38282     /**
38283      * Suspend the LayoutManager from doing auto-layouts while
38284      * making multiple add or remove calls
38285      */
38286     beginUpdate : function(){
38287         this.updating = true;
38288     },
38289
38290     /**
38291      * Restore auto-layouts and optionally disable the manager from performing a layout
38292      * @param {Boolean} noLayout true to disable a layout update
38293      */
38294     endUpdate : function(noLayout){
38295         this.updating = false;
38296         if(!noLayout){
38297             this.layout();
38298         }
38299     },
38300
38301     layout: function(){
38302         // abstract...
38303     },
38304
38305     onRegionResized : function(region, newSize){
38306         this.fireEvent("regionresized", region, newSize);
38307         this.layout();
38308     },
38309
38310     onRegionCollapsed : function(region){
38311         this.fireEvent("regioncollapsed", region);
38312     },
38313
38314     onRegionExpanded : function(region){
38315         this.fireEvent("regionexpanded", region);
38316     },
38317
38318     /**
38319      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38320      * performs box-model adjustments.
38321      * @return {Object} The size as an object {width: (the width), height: (the height)}
38322      */
38323     getViewSize : function()
38324     {
38325         var size;
38326         if(this.el.dom != document.body){
38327             size = this.el.getSize();
38328         }else{
38329             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38330         }
38331         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38332         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38333         return size;
38334     },
38335
38336     /**
38337      * Returns the Element this layout is bound to.
38338      * @return {Roo.Element}
38339      */
38340     getEl : function(){
38341         return this.el;
38342     },
38343
38344     /**
38345      * Returns the specified region.
38346      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38347      * @return {Roo.LayoutRegion}
38348      */
38349     getRegion : function(target){
38350         return this.regions[target.toLowerCase()];
38351     },
38352
38353     onWindowResize : function(){
38354         if(this.monitorWindowResize){
38355             this.layout();
38356         }
38357     }
38358 });
38359 /*
38360  * Based on:
38361  * Ext JS Library 1.1.1
38362  * Copyright(c) 2006-2007, Ext JS, LLC.
38363  *
38364  * Originally Released Under LGPL - original licence link has changed is not relivant.
38365  *
38366  * Fork - LGPL
38367  * <script type="text/javascript">
38368  */
38369 /**
38370  * @class Roo.bootstrap.layout.Border
38371  * @extends Roo.bootstrap.layout.Manager
38372  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38373  * please see: examples/bootstrap/nested.html<br><br>
38374  
38375 <b>The container the layout is rendered into can be either the body element or any other element.
38376 If it is not the body element, the container needs to either be an absolute positioned element,
38377 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38378 the container size if it is not the body element.</b>
38379
38380 * @constructor
38381 * Create a new Border
38382 * @param {Object} config Configuration options
38383  */
38384 Roo.bootstrap.layout.Border = function(config){
38385     config = config || {};
38386     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38387     
38388     
38389     
38390     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38391         if(config[region]){
38392             config[region].region = region;
38393             this.addRegion(config[region]);
38394         }
38395     },this);
38396     
38397 };
38398
38399 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38400
38401 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38402     
38403     parent : false, // this might point to a 'nest' or a ???
38404     
38405     /**
38406      * Creates and adds a new region if it doesn't already exist.
38407      * @param {String} target The target region key (north, south, east, west or center).
38408      * @param {Object} config The regions config object
38409      * @return {BorderLayoutRegion} The new region
38410      */
38411     addRegion : function(config)
38412     {
38413         if(!this.regions[config.region]){
38414             var r = this.factory(config);
38415             this.bindRegion(r);
38416         }
38417         return this.regions[config.region];
38418     },
38419
38420     // private (kinda)
38421     bindRegion : function(r){
38422         this.regions[r.config.region] = r;
38423         
38424         r.on("visibilitychange",    this.layout, this);
38425         r.on("paneladded",          this.layout, this);
38426         r.on("panelremoved",        this.layout, this);
38427         r.on("invalidated",         this.layout, this);
38428         r.on("resized",             this.onRegionResized, this);
38429         r.on("collapsed",           this.onRegionCollapsed, this);
38430         r.on("expanded",            this.onRegionExpanded, this);
38431     },
38432
38433     /**
38434      * Performs a layout update.
38435      */
38436     layout : function()
38437     {
38438         if(this.updating) {
38439             return;
38440         }
38441         
38442         // render all the rebions if they have not been done alreayd?
38443         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38444             if(this.regions[region] && !this.regions[region].bodyEl){
38445                 this.regions[region].onRender(this.el)
38446             }
38447         },this);
38448         
38449         var size = this.getViewSize();
38450         var w = size.width;
38451         var h = size.height;
38452         var centerW = w;
38453         var centerH = h;
38454         var centerY = 0;
38455         var centerX = 0;
38456         //var x = 0, y = 0;
38457
38458         var rs = this.regions;
38459         var north = rs["north"];
38460         var south = rs["south"]; 
38461         var west = rs["west"];
38462         var east = rs["east"];
38463         var center = rs["center"];
38464         //if(this.hideOnLayout){ // not supported anymore
38465             //c.el.setStyle("display", "none");
38466         //}
38467         if(north && north.isVisible()){
38468             var b = north.getBox();
38469             var m = north.getMargins();
38470             b.width = w - (m.left+m.right);
38471             b.x = m.left;
38472             b.y = m.top;
38473             centerY = b.height + b.y + m.bottom;
38474             centerH -= centerY;
38475             north.updateBox(this.safeBox(b));
38476         }
38477         if(south && south.isVisible()){
38478             var b = south.getBox();
38479             var m = south.getMargins();
38480             b.width = w - (m.left+m.right);
38481             b.x = m.left;
38482             var totalHeight = (b.height + m.top + m.bottom);
38483             b.y = h - totalHeight + m.top;
38484             centerH -= totalHeight;
38485             south.updateBox(this.safeBox(b));
38486         }
38487         if(west && west.isVisible()){
38488             var b = west.getBox();
38489             var m = west.getMargins();
38490             b.height = centerH - (m.top+m.bottom);
38491             b.x = m.left;
38492             b.y = centerY + m.top;
38493             var totalWidth = (b.width + m.left + m.right);
38494             centerX += totalWidth;
38495             centerW -= totalWidth;
38496             west.updateBox(this.safeBox(b));
38497         }
38498         if(east && east.isVisible()){
38499             var b = east.getBox();
38500             var m = east.getMargins();
38501             b.height = centerH - (m.top+m.bottom);
38502             var totalWidth = (b.width + m.left + m.right);
38503             b.x = w - totalWidth + m.left;
38504             b.y = centerY + m.top;
38505             centerW -= totalWidth;
38506             east.updateBox(this.safeBox(b));
38507         }
38508         if(center){
38509             var m = center.getMargins();
38510             var centerBox = {
38511                 x: centerX + m.left,
38512                 y: centerY + m.top,
38513                 width: centerW - (m.left+m.right),
38514                 height: centerH - (m.top+m.bottom)
38515             };
38516             //if(this.hideOnLayout){
38517                 //center.el.setStyle("display", "block");
38518             //}
38519             center.updateBox(this.safeBox(centerBox));
38520         }
38521         this.el.repaint();
38522         this.fireEvent("layout", this);
38523     },
38524
38525     // private
38526     safeBox : function(box){
38527         box.width = Math.max(0, box.width);
38528         box.height = Math.max(0, box.height);
38529         return box;
38530     },
38531
38532     /**
38533      * Adds a ContentPanel (or subclass) to this layout.
38534      * @param {String} target The target region key (north, south, east, west or center).
38535      * @param {Roo.ContentPanel} panel The panel to add
38536      * @return {Roo.ContentPanel} The added panel
38537      */
38538     add : function(target, panel){
38539          
38540         target = target.toLowerCase();
38541         return this.regions[target].add(panel);
38542     },
38543
38544     /**
38545      * Remove a ContentPanel (or subclass) to this layout.
38546      * @param {String} target The target region key (north, south, east, west or center).
38547      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38548      * @return {Roo.ContentPanel} The removed panel
38549      */
38550     remove : function(target, panel){
38551         target = target.toLowerCase();
38552         return this.regions[target].remove(panel);
38553     },
38554
38555     /**
38556      * Searches all regions for a panel with the specified id
38557      * @param {String} panelId
38558      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38559      */
38560     findPanel : function(panelId){
38561         var rs = this.regions;
38562         for(var target in rs){
38563             if(typeof rs[target] != "function"){
38564                 var p = rs[target].getPanel(panelId);
38565                 if(p){
38566                     return p;
38567                 }
38568             }
38569         }
38570         return null;
38571     },
38572
38573     /**
38574      * Searches all regions for a panel with the specified id and activates (shows) it.
38575      * @param {String/ContentPanel} panelId The panels id or the panel itself
38576      * @return {Roo.ContentPanel} The shown panel or null
38577      */
38578     showPanel : function(panelId) {
38579       var rs = this.regions;
38580       for(var target in rs){
38581          var r = rs[target];
38582          if(typeof r != "function"){
38583             if(r.hasPanel(panelId)){
38584                return r.showPanel(panelId);
38585             }
38586          }
38587       }
38588       return null;
38589    },
38590
38591    /**
38592      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38593      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38594      */
38595    /*
38596     restoreState : function(provider){
38597         if(!provider){
38598             provider = Roo.state.Manager;
38599         }
38600         var sm = new Roo.LayoutStateManager();
38601         sm.init(this, provider);
38602     },
38603 */
38604  
38605  
38606     /**
38607      * Adds a xtype elements to the layout.
38608      * <pre><code>
38609
38610 layout.addxtype({
38611        xtype : 'ContentPanel',
38612        region: 'west',
38613        items: [ .... ]
38614    }
38615 );
38616
38617 layout.addxtype({
38618         xtype : 'NestedLayoutPanel',
38619         region: 'west',
38620         layout: {
38621            center: { },
38622            west: { }   
38623         },
38624         items : [ ... list of content panels or nested layout panels.. ]
38625    }
38626 );
38627 </code></pre>
38628      * @param {Object} cfg Xtype definition of item to add.
38629      */
38630     addxtype : function(cfg)
38631     {
38632         // basically accepts a pannel...
38633         // can accept a layout region..!?!?
38634         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38635         
38636         
38637         // theory?  children can only be panels??
38638         
38639         //if (!cfg.xtype.match(/Panel$/)) {
38640         //    return false;
38641         //}
38642         var ret = false;
38643         
38644         if (typeof(cfg.region) == 'undefined') {
38645             Roo.log("Failed to add Panel, region was not set");
38646             Roo.log(cfg);
38647             return false;
38648         }
38649         var region = cfg.region;
38650         delete cfg.region;
38651         
38652           
38653         var xitems = [];
38654         if (cfg.items) {
38655             xitems = cfg.items;
38656             delete cfg.items;
38657         }
38658         var nb = false;
38659         
38660         if ( region == 'center') {
38661             Roo.log("Center: " + cfg.title);
38662         }
38663         
38664         
38665         switch(cfg.xtype) 
38666         {
38667             case 'Content':  // ContentPanel (el, cfg)
38668             case 'Scroll':  // ContentPanel (el, cfg)
38669             case 'View': 
38670                 cfg.autoCreate = cfg.autoCreate || true;
38671                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38672                 //} else {
38673                 //    var el = this.el.createChild();
38674                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38675                 //}
38676                 
38677                 this.add(region, ret);
38678                 break;
38679             
38680             /*
38681             case 'TreePanel': // our new panel!
38682                 cfg.el = this.el.createChild();
38683                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38684                 this.add(region, ret);
38685                 break;
38686             */
38687             
38688             case 'Nest': 
38689                 // create a new Layout (which is  a Border Layout...
38690                 
38691                 var clayout = cfg.layout;
38692                 clayout.el  = this.el.createChild();
38693                 clayout.items   = clayout.items  || [];
38694                 
38695                 delete cfg.layout;
38696                 
38697                 // replace this exitems with the clayout ones..
38698                 xitems = clayout.items;
38699                  
38700                 // force background off if it's in center...
38701                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38702                     cfg.background = false;
38703                 }
38704                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38705                 
38706                 
38707                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38708                 //console.log('adding nested layout panel '  + cfg.toSource());
38709                 this.add(region, ret);
38710                 nb = {}; /// find first...
38711                 break;
38712             
38713             case 'Grid':
38714                 
38715                 // needs grid and region
38716                 
38717                 //var el = this.getRegion(region).el.createChild();
38718                 /*
38719                  *var el = this.el.createChild();
38720                 // create the grid first...
38721                 cfg.grid.container = el;
38722                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38723                 */
38724                 
38725                 if (region == 'center' && this.active ) {
38726                     cfg.background = false;
38727                 }
38728                 
38729                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38730                 
38731                 this.add(region, ret);
38732                 /*
38733                 if (cfg.background) {
38734                     // render grid on panel activation (if panel background)
38735                     ret.on('activate', function(gp) {
38736                         if (!gp.grid.rendered) {
38737                     //        gp.grid.render(el);
38738                         }
38739                     });
38740                 } else {
38741                   //  cfg.grid.render(el);
38742                 }
38743                 */
38744                 break;
38745            
38746            
38747             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38748                 // it was the old xcomponent building that caused this before.
38749                 // espeically if border is the top element in the tree.
38750                 ret = this;
38751                 break; 
38752                 
38753                     
38754                 
38755                 
38756                 
38757             default:
38758                 /*
38759                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38760                     
38761                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38762                     this.add(region, ret);
38763                 } else {
38764                 */
38765                     Roo.log(cfg);
38766                     throw "Can not add '" + cfg.xtype + "' to Border";
38767                     return null;
38768              
38769                                 
38770              
38771         }
38772         this.beginUpdate();
38773         // add children..
38774         var region = '';
38775         var abn = {};
38776         Roo.each(xitems, function(i)  {
38777             region = nb && i.region ? i.region : false;
38778             
38779             var add = ret.addxtype(i);
38780            
38781             if (region) {
38782                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38783                 if (!i.background) {
38784                     abn[region] = nb[region] ;
38785                 }
38786             }
38787             
38788         });
38789         this.endUpdate();
38790
38791         // make the last non-background panel active..
38792         //if (nb) { Roo.log(abn); }
38793         if (nb) {
38794             
38795             for(var r in abn) {
38796                 region = this.getRegion(r);
38797                 if (region) {
38798                     // tried using nb[r], but it does not work..
38799                      
38800                     region.showPanel(abn[r]);
38801                    
38802                 }
38803             }
38804         }
38805         return ret;
38806         
38807     },
38808     
38809     
38810 // private
38811     factory : function(cfg)
38812     {
38813         
38814         var validRegions = Roo.bootstrap.layout.Border.regions;
38815
38816         var target = cfg.region;
38817         cfg.mgr = this;
38818         
38819         var r = Roo.bootstrap.layout;
38820         Roo.log(target);
38821         switch(target){
38822             case "north":
38823                 return new r.North(cfg);
38824             case "south":
38825                 return new r.South(cfg);
38826             case "east":
38827                 return new r.East(cfg);
38828             case "west":
38829                 return new r.West(cfg);
38830             case "center":
38831                 return new r.Center(cfg);
38832         }
38833         throw 'Layout region "'+target+'" not supported.';
38834     }
38835     
38836     
38837 });
38838  /*
38839  * Based on:
38840  * Ext JS Library 1.1.1
38841  * Copyright(c) 2006-2007, Ext JS, LLC.
38842  *
38843  * Originally Released Under LGPL - original licence link has changed is not relivant.
38844  *
38845  * Fork - LGPL
38846  * <script type="text/javascript">
38847  */
38848  
38849 /**
38850  * @class Roo.bootstrap.layout.Basic
38851  * @extends Roo.util.Observable
38852  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38853  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38854  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38855  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38856  * @cfg {string}   region  the region that it inhabits..
38857  * @cfg {bool}   skipConfig skip config?
38858  * 
38859
38860  */
38861 Roo.bootstrap.layout.Basic = function(config){
38862     
38863     this.mgr = config.mgr;
38864     
38865     this.position = config.region;
38866     
38867     var skipConfig = config.skipConfig;
38868     
38869     this.events = {
38870         /**
38871          * @scope Roo.BasicLayoutRegion
38872          */
38873         
38874         /**
38875          * @event beforeremove
38876          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38877          * @param {Roo.LayoutRegion} this
38878          * @param {Roo.ContentPanel} panel The panel
38879          * @param {Object} e The cancel event object
38880          */
38881         "beforeremove" : true,
38882         /**
38883          * @event invalidated
38884          * Fires when the layout for this region is changed.
38885          * @param {Roo.LayoutRegion} this
38886          */
38887         "invalidated" : true,
38888         /**
38889          * @event visibilitychange
38890          * Fires when this region is shown or hidden 
38891          * @param {Roo.LayoutRegion} this
38892          * @param {Boolean} visibility true or false
38893          */
38894         "visibilitychange" : true,
38895         /**
38896          * @event paneladded
38897          * Fires when a panel is added. 
38898          * @param {Roo.LayoutRegion} this
38899          * @param {Roo.ContentPanel} panel The panel
38900          */
38901         "paneladded" : true,
38902         /**
38903          * @event panelremoved
38904          * Fires when a panel is removed. 
38905          * @param {Roo.LayoutRegion} this
38906          * @param {Roo.ContentPanel} panel The panel
38907          */
38908         "panelremoved" : true,
38909         /**
38910          * @event beforecollapse
38911          * Fires when this region before collapse.
38912          * @param {Roo.LayoutRegion} this
38913          */
38914         "beforecollapse" : true,
38915         /**
38916          * @event collapsed
38917          * Fires when this region is collapsed.
38918          * @param {Roo.LayoutRegion} this
38919          */
38920         "collapsed" : true,
38921         /**
38922          * @event expanded
38923          * Fires when this region is expanded.
38924          * @param {Roo.LayoutRegion} this
38925          */
38926         "expanded" : true,
38927         /**
38928          * @event slideshow
38929          * Fires when this region is slid into view.
38930          * @param {Roo.LayoutRegion} this
38931          */
38932         "slideshow" : true,
38933         /**
38934          * @event slidehide
38935          * Fires when this region slides out of view. 
38936          * @param {Roo.LayoutRegion} this
38937          */
38938         "slidehide" : true,
38939         /**
38940          * @event panelactivated
38941          * Fires when a panel is activated. 
38942          * @param {Roo.LayoutRegion} this
38943          * @param {Roo.ContentPanel} panel The activated panel
38944          */
38945         "panelactivated" : true,
38946         /**
38947          * @event resized
38948          * Fires when the user resizes this region. 
38949          * @param {Roo.LayoutRegion} this
38950          * @param {Number} newSize The new size (width for east/west, height for north/south)
38951          */
38952         "resized" : true
38953     };
38954     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38955     this.panels = new Roo.util.MixedCollection();
38956     this.panels.getKey = this.getPanelId.createDelegate(this);
38957     this.box = null;
38958     this.activePanel = null;
38959     // ensure listeners are added...
38960     
38961     if (config.listeners || config.events) {
38962         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38963             listeners : config.listeners || {},
38964             events : config.events || {}
38965         });
38966     }
38967     
38968     if(skipConfig !== true){
38969         this.applyConfig(config);
38970     }
38971 };
38972
38973 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38974 {
38975     getPanelId : function(p){
38976         return p.getId();
38977     },
38978     
38979     applyConfig : function(config){
38980         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38981         this.config = config;
38982         
38983     },
38984     
38985     /**
38986      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38987      * the width, for horizontal (north, south) the height.
38988      * @param {Number} newSize The new width or height
38989      */
38990     resizeTo : function(newSize){
38991         var el = this.el ? this.el :
38992                  (this.activePanel ? this.activePanel.getEl() : null);
38993         if(el){
38994             switch(this.position){
38995                 case "east":
38996                 case "west":
38997                     el.setWidth(newSize);
38998                     this.fireEvent("resized", this, newSize);
38999                 break;
39000                 case "north":
39001                 case "south":
39002                     el.setHeight(newSize);
39003                     this.fireEvent("resized", this, newSize);
39004                 break;                
39005             }
39006         }
39007     },
39008     
39009     getBox : function(){
39010         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39011     },
39012     
39013     getMargins : function(){
39014         return this.margins;
39015     },
39016     
39017     updateBox : function(box){
39018         this.box = box;
39019         var el = this.activePanel.getEl();
39020         el.dom.style.left = box.x + "px";
39021         el.dom.style.top = box.y + "px";
39022         this.activePanel.setSize(box.width, box.height);
39023     },
39024     
39025     /**
39026      * Returns the container element for this region.
39027      * @return {Roo.Element}
39028      */
39029     getEl : function(){
39030         return this.activePanel;
39031     },
39032     
39033     /**
39034      * Returns true if this region is currently visible.
39035      * @return {Boolean}
39036      */
39037     isVisible : function(){
39038         return this.activePanel ? true : false;
39039     },
39040     
39041     setActivePanel : function(panel){
39042         panel = this.getPanel(panel);
39043         if(this.activePanel && this.activePanel != panel){
39044             this.activePanel.setActiveState(false);
39045             this.activePanel.getEl().setLeftTop(-10000,-10000);
39046         }
39047         this.activePanel = panel;
39048         panel.setActiveState(true);
39049         if(this.box){
39050             panel.setSize(this.box.width, this.box.height);
39051         }
39052         this.fireEvent("panelactivated", this, panel);
39053         this.fireEvent("invalidated");
39054     },
39055     
39056     /**
39057      * Show the specified panel.
39058      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39059      * @return {Roo.ContentPanel} The shown panel or null
39060      */
39061     showPanel : function(panel){
39062         panel = this.getPanel(panel);
39063         if(panel){
39064             this.setActivePanel(panel);
39065         }
39066         return panel;
39067     },
39068     
39069     /**
39070      * Get the active panel for this region.
39071      * @return {Roo.ContentPanel} The active panel or null
39072      */
39073     getActivePanel : function(){
39074         return this.activePanel;
39075     },
39076     
39077     /**
39078      * Add the passed ContentPanel(s)
39079      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39080      * @return {Roo.ContentPanel} The panel added (if only one was added)
39081      */
39082     add : function(panel){
39083         if(arguments.length > 1){
39084             for(var i = 0, len = arguments.length; i < len; i++) {
39085                 this.add(arguments[i]);
39086             }
39087             return null;
39088         }
39089         if(this.hasPanel(panel)){
39090             this.showPanel(panel);
39091             return panel;
39092         }
39093         var el = panel.getEl();
39094         if(el.dom.parentNode != this.mgr.el.dom){
39095             this.mgr.el.dom.appendChild(el.dom);
39096         }
39097         if(panel.setRegion){
39098             panel.setRegion(this);
39099         }
39100         this.panels.add(panel);
39101         el.setStyle("position", "absolute");
39102         if(!panel.background){
39103             this.setActivePanel(panel);
39104             if(this.config.initialSize && this.panels.getCount()==1){
39105                 this.resizeTo(this.config.initialSize);
39106             }
39107         }
39108         this.fireEvent("paneladded", this, panel);
39109         return panel;
39110     },
39111     
39112     /**
39113      * Returns true if the panel is in this region.
39114      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39115      * @return {Boolean}
39116      */
39117     hasPanel : function(panel){
39118         if(typeof panel == "object"){ // must be panel obj
39119             panel = panel.getId();
39120         }
39121         return this.getPanel(panel) ? true : false;
39122     },
39123     
39124     /**
39125      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39126      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39127      * @param {Boolean} preservePanel Overrides the config preservePanel option
39128      * @return {Roo.ContentPanel} The panel that was removed
39129      */
39130     remove : function(panel, preservePanel){
39131         panel = this.getPanel(panel);
39132         if(!panel){
39133             return null;
39134         }
39135         var e = {};
39136         this.fireEvent("beforeremove", this, panel, e);
39137         if(e.cancel === true){
39138             return null;
39139         }
39140         var panelId = panel.getId();
39141         this.panels.removeKey(panelId);
39142         return panel;
39143     },
39144     
39145     /**
39146      * Returns the panel specified or null if it's not in this region.
39147      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39148      * @return {Roo.ContentPanel}
39149      */
39150     getPanel : function(id){
39151         if(typeof id == "object"){ // must be panel obj
39152             return id;
39153         }
39154         return this.panels.get(id);
39155     },
39156     
39157     /**
39158      * Returns this regions position (north/south/east/west/center).
39159      * @return {String} 
39160      */
39161     getPosition: function(){
39162         return this.position;    
39163     }
39164 });/*
39165  * Based on:
39166  * Ext JS Library 1.1.1
39167  * Copyright(c) 2006-2007, Ext JS, LLC.
39168  *
39169  * Originally Released Under LGPL - original licence link has changed is not relivant.
39170  *
39171  * Fork - LGPL
39172  * <script type="text/javascript">
39173  */
39174  
39175 /**
39176  * @class Roo.bootstrap.layout.Region
39177  * @extends Roo.bootstrap.layout.Basic
39178  * This class represents a region in a layout manager.
39179  
39180  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39181  * @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})
39182  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39183  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39184  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39185  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39186  * @cfg {String}    title           The title for the region (overrides panel titles)
39187  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39188  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39189  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39190  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39191  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39192  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39193  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39194  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39195  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39196  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39197
39198  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39199  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39200  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39201  * @cfg {Number}    width           For East/West panels
39202  * @cfg {Number}    height          For North/South panels
39203  * @cfg {Boolean}   split           To show the splitter
39204  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39205  * 
39206  * @cfg {string}   cls             Extra CSS classes to add to region
39207  * 
39208  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39209  * @cfg {string}   region  the region that it inhabits..
39210  *
39211
39212  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39213  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39214
39215  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39216  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39217  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39218  */
39219 Roo.bootstrap.layout.Region = function(config)
39220 {
39221     this.applyConfig(config);
39222
39223     var mgr = config.mgr;
39224     var pos = config.region;
39225     config.skipConfig = true;
39226     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39227     
39228     if (mgr.el) {
39229         this.onRender(mgr.el);   
39230     }
39231      
39232     this.visible = true;
39233     this.collapsed = false;
39234     this.unrendered_panels = [];
39235 };
39236
39237 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39238
39239     position: '', // set by wrapper (eg. north/south etc..)
39240     unrendered_panels : null,  // unrendered panels.
39241     
39242     tabPosition : false,
39243     
39244     mgr: false, // points to 'Border'
39245     
39246     
39247     createBody : function(){
39248         /** This region's body element 
39249         * @type Roo.Element */
39250         this.bodyEl = this.el.createChild({
39251                 tag: "div",
39252                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39253         });
39254     },
39255
39256     onRender: function(ctr, pos)
39257     {
39258         var dh = Roo.DomHelper;
39259         /** This region's container element 
39260         * @type Roo.Element */
39261         this.el = dh.append(ctr.dom, {
39262                 tag: "div",
39263                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39264             }, true);
39265         /** This region's title element 
39266         * @type Roo.Element */
39267     
39268         this.titleEl = dh.append(this.el.dom,  {
39269                 tag: "div",
39270                 unselectable: "on",
39271                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39272                 children:[
39273                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39274                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39275                 ]
39276             }, true);
39277         
39278         this.titleEl.enableDisplayMode();
39279         /** This region's title text element 
39280         * @type HTMLElement */
39281         this.titleTextEl = this.titleEl.dom.firstChild;
39282         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39283         /*
39284         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39285         this.closeBtn.enableDisplayMode();
39286         this.closeBtn.on("click", this.closeClicked, this);
39287         this.closeBtn.hide();
39288     */
39289         this.createBody(this.config);
39290         if(this.config.hideWhenEmpty){
39291             this.hide();
39292             this.on("paneladded", this.validateVisibility, this);
39293             this.on("panelremoved", this.validateVisibility, this);
39294         }
39295         if(this.autoScroll){
39296             this.bodyEl.setStyle("overflow", "auto");
39297         }else{
39298             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39299         }
39300         //if(c.titlebar !== false){
39301             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39302                 this.titleEl.hide();
39303             }else{
39304                 this.titleEl.show();
39305                 if(this.config.title){
39306                     this.titleTextEl.innerHTML = this.config.title;
39307                 }
39308             }
39309         //}
39310         if(this.config.collapsed){
39311             this.collapse(true);
39312         }
39313         if(this.config.hidden){
39314             this.hide();
39315         }
39316         
39317         if (this.unrendered_panels && this.unrendered_panels.length) {
39318             for (var i =0;i< this.unrendered_panels.length; i++) {
39319                 this.add(this.unrendered_panels[i]);
39320             }
39321             this.unrendered_panels = null;
39322             
39323         }
39324         
39325     },
39326     
39327     applyConfig : function(c)
39328     {
39329         /*
39330          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39331             var dh = Roo.DomHelper;
39332             if(c.titlebar !== false){
39333                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39334                 this.collapseBtn.on("click", this.collapse, this);
39335                 this.collapseBtn.enableDisplayMode();
39336                 /*
39337                 if(c.showPin === true || this.showPin){
39338                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39339                     this.stickBtn.enableDisplayMode();
39340                     this.stickBtn.on("click", this.expand, this);
39341                     this.stickBtn.hide();
39342                 }
39343                 
39344             }
39345             */
39346             /** This region's collapsed element
39347             * @type Roo.Element */
39348             /*
39349              *
39350             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39351                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39352             ]}, true);
39353             
39354             if(c.floatable !== false){
39355                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39356                this.collapsedEl.on("click", this.collapseClick, this);
39357             }
39358
39359             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39360                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39361                    id: "message", unselectable: "on", style:{"float":"left"}});
39362                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39363              }
39364             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39365             this.expandBtn.on("click", this.expand, this);
39366             
39367         }
39368         
39369         if(this.collapseBtn){
39370             this.collapseBtn.setVisible(c.collapsible == true);
39371         }
39372         
39373         this.cmargins = c.cmargins || this.cmargins ||
39374                          (this.position == "west" || this.position == "east" ?
39375                              {top: 0, left: 2, right:2, bottom: 0} :
39376                              {top: 2, left: 0, right:0, bottom: 2});
39377         */
39378         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39379         
39380         
39381         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39382         
39383         this.autoScroll = c.autoScroll || false;
39384         
39385         
39386        
39387         
39388         this.duration = c.duration || .30;
39389         this.slideDuration = c.slideDuration || .45;
39390         this.config = c;
39391        
39392     },
39393     /**
39394      * Returns true if this region is currently visible.
39395      * @return {Boolean}
39396      */
39397     isVisible : function(){
39398         return this.visible;
39399     },
39400
39401     /**
39402      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39403      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39404      */
39405     //setCollapsedTitle : function(title){
39406     //    title = title || "&#160;";
39407      //   if(this.collapsedTitleTextEl){
39408       //      this.collapsedTitleTextEl.innerHTML = title;
39409        // }
39410     //},
39411
39412     getBox : function(){
39413         var b;
39414       //  if(!this.collapsed){
39415             b = this.el.getBox(false, true);
39416        // }else{
39417           //  b = this.collapsedEl.getBox(false, true);
39418         //}
39419         return b;
39420     },
39421
39422     getMargins : function(){
39423         return this.margins;
39424         //return this.collapsed ? this.cmargins : this.margins;
39425     },
39426 /*
39427     highlight : function(){
39428         this.el.addClass("x-layout-panel-dragover");
39429     },
39430
39431     unhighlight : function(){
39432         this.el.removeClass("x-layout-panel-dragover");
39433     },
39434 */
39435     updateBox : function(box)
39436     {
39437         if (!this.bodyEl) {
39438             return; // not rendered yet..
39439         }
39440         
39441         this.box = box;
39442         if(!this.collapsed){
39443             this.el.dom.style.left = box.x + "px";
39444             this.el.dom.style.top = box.y + "px";
39445             this.updateBody(box.width, box.height);
39446         }else{
39447             this.collapsedEl.dom.style.left = box.x + "px";
39448             this.collapsedEl.dom.style.top = box.y + "px";
39449             this.collapsedEl.setSize(box.width, box.height);
39450         }
39451         if(this.tabs){
39452             this.tabs.autoSizeTabs();
39453         }
39454     },
39455
39456     updateBody : function(w, h)
39457     {
39458         if(w !== null){
39459             this.el.setWidth(w);
39460             w -= this.el.getBorderWidth("rl");
39461             if(this.config.adjustments){
39462                 w += this.config.adjustments[0];
39463             }
39464         }
39465         if(h !== null && h > 0){
39466             this.el.setHeight(h);
39467             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39468             h -= this.el.getBorderWidth("tb");
39469             if(this.config.adjustments){
39470                 h += this.config.adjustments[1];
39471             }
39472             this.bodyEl.setHeight(h);
39473             if(this.tabs){
39474                 h = this.tabs.syncHeight(h);
39475             }
39476         }
39477         if(this.panelSize){
39478             w = w !== null ? w : this.panelSize.width;
39479             h = h !== null ? h : this.panelSize.height;
39480         }
39481         if(this.activePanel){
39482             var el = this.activePanel.getEl();
39483             w = w !== null ? w : el.getWidth();
39484             h = h !== null ? h : el.getHeight();
39485             this.panelSize = {width: w, height: h};
39486             this.activePanel.setSize(w, h);
39487         }
39488         if(Roo.isIE && this.tabs){
39489             this.tabs.el.repaint();
39490         }
39491     },
39492
39493     /**
39494      * Returns the container element for this region.
39495      * @return {Roo.Element}
39496      */
39497     getEl : function(){
39498         return this.el;
39499     },
39500
39501     /**
39502      * Hides this region.
39503      */
39504     hide : function(){
39505         //if(!this.collapsed){
39506             this.el.dom.style.left = "-2000px";
39507             this.el.hide();
39508         //}else{
39509          //   this.collapsedEl.dom.style.left = "-2000px";
39510          //   this.collapsedEl.hide();
39511        // }
39512         this.visible = false;
39513         this.fireEvent("visibilitychange", this, false);
39514     },
39515
39516     /**
39517      * Shows this region if it was previously hidden.
39518      */
39519     show : function(){
39520         //if(!this.collapsed){
39521             this.el.show();
39522         //}else{
39523         //    this.collapsedEl.show();
39524        // }
39525         this.visible = true;
39526         this.fireEvent("visibilitychange", this, true);
39527     },
39528 /*
39529     closeClicked : function(){
39530         if(this.activePanel){
39531             this.remove(this.activePanel);
39532         }
39533     },
39534
39535     collapseClick : function(e){
39536         if(this.isSlid){
39537            e.stopPropagation();
39538            this.slideIn();
39539         }else{
39540            e.stopPropagation();
39541            this.slideOut();
39542         }
39543     },
39544 */
39545     /**
39546      * Collapses this region.
39547      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39548      */
39549     /*
39550     collapse : function(skipAnim, skipCheck = false){
39551         if(this.collapsed) {
39552             return;
39553         }
39554         
39555         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39556             
39557             this.collapsed = true;
39558             if(this.split){
39559                 this.split.el.hide();
39560             }
39561             if(this.config.animate && skipAnim !== true){
39562                 this.fireEvent("invalidated", this);
39563                 this.animateCollapse();
39564             }else{
39565                 this.el.setLocation(-20000,-20000);
39566                 this.el.hide();
39567                 this.collapsedEl.show();
39568                 this.fireEvent("collapsed", this);
39569                 this.fireEvent("invalidated", this);
39570             }
39571         }
39572         
39573     },
39574 */
39575     animateCollapse : function(){
39576         // overridden
39577     },
39578
39579     /**
39580      * Expands this region if it was previously collapsed.
39581      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39582      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39583      */
39584     /*
39585     expand : function(e, skipAnim){
39586         if(e) {
39587             e.stopPropagation();
39588         }
39589         if(!this.collapsed || this.el.hasActiveFx()) {
39590             return;
39591         }
39592         if(this.isSlid){
39593             this.afterSlideIn();
39594             skipAnim = true;
39595         }
39596         this.collapsed = false;
39597         if(this.config.animate && skipAnim !== true){
39598             this.animateExpand();
39599         }else{
39600             this.el.show();
39601             if(this.split){
39602                 this.split.el.show();
39603             }
39604             this.collapsedEl.setLocation(-2000,-2000);
39605             this.collapsedEl.hide();
39606             this.fireEvent("invalidated", this);
39607             this.fireEvent("expanded", this);
39608         }
39609     },
39610 */
39611     animateExpand : function(){
39612         // overridden
39613     },
39614
39615     initTabs : function()
39616     {
39617         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39618         
39619         var ts = new Roo.bootstrap.panel.Tabs({
39620             el: this.bodyEl.dom,
39621             region : this,
39622             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39623             disableTooltips: this.config.disableTabTips,
39624             toolbar : this.config.toolbar
39625         });
39626         
39627         if(this.config.hideTabs){
39628             ts.stripWrap.setDisplayed(false);
39629         }
39630         this.tabs = ts;
39631         ts.resizeTabs = this.config.resizeTabs === true;
39632         ts.minTabWidth = this.config.minTabWidth || 40;
39633         ts.maxTabWidth = this.config.maxTabWidth || 250;
39634         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39635         ts.monitorResize = false;
39636         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39637         ts.bodyEl.addClass('roo-layout-tabs-body');
39638         this.panels.each(this.initPanelAsTab, this);
39639     },
39640
39641     initPanelAsTab : function(panel){
39642         var ti = this.tabs.addTab(
39643             panel.getEl().id,
39644             panel.getTitle(),
39645             null,
39646             this.config.closeOnTab && panel.isClosable(),
39647             panel.tpl
39648         );
39649         if(panel.tabTip !== undefined){
39650             ti.setTooltip(panel.tabTip);
39651         }
39652         ti.on("activate", function(){
39653               this.setActivePanel(panel);
39654         }, this);
39655         
39656         if(this.config.closeOnTab){
39657             ti.on("beforeclose", function(t, e){
39658                 e.cancel = true;
39659                 this.remove(panel);
39660             }, this);
39661         }
39662         
39663         panel.tabItem = ti;
39664         
39665         return ti;
39666     },
39667
39668     updatePanelTitle : function(panel, title)
39669     {
39670         if(this.activePanel == panel){
39671             this.updateTitle(title);
39672         }
39673         if(this.tabs){
39674             var ti = this.tabs.getTab(panel.getEl().id);
39675             ti.setText(title);
39676             if(panel.tabTip !== undefined){
39677                 ti.setTooltip(panel.tabTip);
39678             }
39679         }
39680     },
39681
39682     updateTitle : function(title){
39683         if(this.titleTextEl && !this.config.title){
39684             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39685         }
39686     },
39687
39688     setActivePanel : function(panel)
39689     {
39690         panel = this.getPanel(panel);
39691         if(this.activePanel && this.activePanel != panel){
39692             if(this.activePanel.setActiveState(false) === false){
39693                 return;
39694             }
39695         }
39696         this.activePanel = panel;
39697         panel.setActiveState(true);
39698         if(this.panelSize){
39699             panel.setSize(this.panelSize.width, this.panelSize.height);
39700         }
39701         if(this.closeBtn){
39702             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39703         }
39704         this.updateTitle(panel.getTitle());
39705         if(this.tabs){
39706             this.fireEvent("invalidated", this);
39707         }
39708         this.fireEvent("panelactivated", this, panel);
39709     },
39710
39711     /**
39712      * Shows the specified panel.
39713      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39714      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39715      */
39716     showPanel : function(panel)
39717     {
39718         panel = this.getPanel(panel);
39719         if(panel){
39720             if(this.tabs){
39721                 var tab = this.tabs.getTab(panel.getEl().id);
39722                 if(tab.isHidden()){
39723                     this.tabs.unhideTab(tab.id);
39724                 }
39725                 tab.activate();
39726             }else{
39727                 this.setActivePanel(panel);
39728             }
39729         }
39730         return panel;
39731     },
39732
39733     /**
39734      * Get the active panel for this region.
39735      * @return {Roo.ContentPanel} The active panel or null
39736      */
39737     getActivePanel : function(){
39738         return this.activePanel;
39739     },
39740
39741     validateVisibility : function(){
39742         if(this.panels.getCount() < 1){
39743             this.updateTitle("&#160;");
39744             this.closeBtn.hide();
39745             this.hide();
39746         }else{
39747             if(!this.isVisible()){
39748                 this.show();
39749             }
39750         }
39751     },
39752
39753     /**
39754      * Adds the passed ContentPanel(s) to this region.
39755      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39756      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39757      */
39758     add : function(panel)
39759     {
39760         if(arguments.length > 1){
39761             for(var i = 0, len = arguments.length; i < len; i++) {
39762                 this.add(arguments[i]);
39763             }
39764             return null;
39765         }
39766         
39767         // if we have not been rendered yet, then we can not really do much of this..
39768         if (!this.bodyEl) {
39769             this.unrendered_panels.push(panel);
39770             return panel;
39771         }
39772         
39773         
39774         
39775         
39776         if(this.hasPanel(panel)){
39777             this.showPanel(panel);
39778             return panel;
39779         }
39780         panel.setRegion(this);
39781         this.panels.add(panel);
39782        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39783             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39784             // and hide them... ???
39785             this.bodyEl.dom.appendChild(panel.getEl().dom);
39786             if(panel.background !== true){
39787                 this.setActivePanel(panel);
39788             }
39789             this.fireEvent("paneladded", this, panel);
39790             return panel;
39791         }
39792         */
39793         if(!this.tabs){
39794             this.initTabs();
39795         }else{
39796             this.initPanelAsTab(panel);
39797         }
39798         
39799         
39800         if(panel.background !== true){
39801             this.tabs.activate(panel.getEl().id);
39802         }
39803         this.fireEvent("paneladded", this, panel);
39804         return panel;
39805     },
39806
39807     /**
39808      * Hides the tab for the specified panel.
39809      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39810      */
39811     hidePanel : function(panel){
39812         if(this.tabs && (panel = this.getPanel(panel))){
39813             this.tabs.hideTab(panel.getEl().id);
39814         }
39815     },
39816
39817     /**
39818      * Unhides the tab for a previously hidden panel.
39819      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39820      */
39821     unhidePanel : function(panel){
39822         if(this.tabs && (panel = this.getPanel(panel))){
39823             this.tabs.unhideTab(panel.getEl().id);
39824         }
39825     },
39826
39827     clearPanels : function(){
39828         while(this.panels.getCount() > 0){
39829              this.remove(this.panels.first());
39830         }
39831     },
39832
39833     /**
39834      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39835      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39836      * @param {Boolean} preservePanel Overrides the config preservePanel option
39837      * @return {Roo.ContentPanel} The panel that was removed
39838      */
39839     remove : function(panel, preservePanel)
39840     {
39841         panel = this.getPanel(panel);
39842         if(!panel){
39843             return null;
39844         }
39845         var e = {};
39846         this.fireEvent("beforeremove", this, panel, e);
39847         if(e.cancel === true){
39848             return null;
39849         }
39850         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39851         var panelId = panel.getId();
39852         this.panels.removeKey(panelId);
39853         if(preservePanel){
39854             document.body.appendChild(panel.getEl().dom);
39855         }
39856         if(this.tabs){
39857             this.tabs.removeTab(panel.getEl().id);
39858         }else if (!preservePanel){
39859             this.bodyEl.dom.removeChild(panel.getEl().dom);
39860         }
39861         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39862             var p = this.panels.first();
39863             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39864             tempEl.appendChild(p.getEl().dom);
39865             this.bodyEl.update("");
39866             this.bodyEl.dom.appendChild(p.getEl().dom);
39867             tempEl = null;
39868             this.updateTitle(p.getTitle());
39869             this.tabs = null;
39870             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39871             this.setActivePanel(p);
39872         }
39873         panel.setRegion(null);
39874         if(this.activePanel == panel){
39875             this.activePanel = null;
39876         }
39877         if(this.config.autoDestroy !== false && preservePanel !== true){
39878             try{panel.destroy();}catch(e){}
39879         }
39880         this.fireEvent("panelremoved", this, panel);
39881         return panel;
39882     },
39883
39884     /**
39885      * Returns the TabPanel component used by this region
39886      * @return {Roo.TabPanel}
39887      */
39888     getTabs : function(){
39889         return this.tabs;
39890     },
39891
39892     createTool : function(parentEl, className){
39893         var btn = Roo.DomHelper.append(parentEl, {
39894             tag: "div",
39895             cls: "x-layout-tools-button",
39896             children: [ {
39897                 tag: "div",
39898                 cls: "roo-layout-tools-button-inner " + className,
39899                 html: "&#160;"
39900             }]
39901         }, true);
39902         btn.addClassOnOver("roo-layout-tools-button-over");
39903         return btn;
39904     }
39905 });/*
39906  * Based on:
39907  * Ext JS Library 1.1.1
39908  * Copyright(c) 2006-2007, Ext JS, LLC.
39909  *
39910  * Originally Released Under LGPL - original licence link has changed is not relivant.
39911  *
39912  * Fork - LGPL
39913  * <script type="text/javascript">
39914  */
39915  
39916
39917
39918 /**
39919  * @class Roo.SplitLayoutRegion
39920  * @extends Roo.LayoutRegion
39921  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39922  */
39923 Roo.bootstrap.layout.Split = function(config){
39924     this.cursor = config.cursor;
39925     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39926 };
39927
39928 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39929 {
39930     splitTip : "Drag to resize.",
39931     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39932     useSplitTips : false,
39933
39934     applyConfig : function(config){
39935         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39936     },
39937     
39938     onRender : function(ctr,pos) {
39939         
39940         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39941         if(!this.config.split){
39942             return;
39943         }
39944         if(!this.split){
39945             
39946             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39947                             tag: "div",
39948                             id: this.el.id + "-split",
39949                             cls: "roo-layout-split roo-layout-split-"+this.position,
39950                             html: "&#160;"
39951             });
39952             /** The SplitBar for this region 
39953             * @type Roo.SplitBar */
39954             // does not exist yet...
39955             Roo.log([this.position, this.orientation]);
39956             
39957             this.split = new Roo.bootstrap.SplitBar({
39958                 dragElement : splitEl,
39959                 resizingElement: this.el,
39960                 orientation : this.orientation
39961             });
39962             
39963             this.split.on("moved", this.onSplitMove, this);
39964             this.split.useShim = this.config.useShim === true;
39965             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39966             if(this.useSplitTips){
39967                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39968             }
39969             //if(config.collapsible){
39970             //    this.split.el.on("dblclick", this.collapse,  this);
39971             //}
39972         }
39973         if(typeof this.config.minSize != "undefined"){
39974             this.split.minSize = this.config.minSize;
39975         }
39976         if(typeof this.config.maxSize != "undefined"){
39977             this.split.maxSize = this.config.maxSize;
39978         }
39979         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39980             this.hideSplitter();
39981         }
39982         
39983     },
39984
39985     getHMaxSize : function(){
39986          var cmax = this.config.maxSize || 10000;
39987          var center = this.mgr.getRegion("center");
39988          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39989     },
39990
39991     getVMaxSize : function(){
39992          var cmax = this.config.maxSize || 10000;
39993          var center = this.mgr.getRegion("center");
39994          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39995     },
39996
39997     onSplitMove : function(split, newSize){
39998         this.fireEvent("resized", this, newSize);
39999     },
40000     
40001     /** 
40002      * Returns the {@link Roo.SplitBar} for this region.
40003      * @return {Roo.SplitBar}
40004      */
40005     getSplitBar : function(){
40006         return this.split;
40007     },
40008     
40009     hide : function(){
40010         this.hideSplitter();
40011         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40012     },
40013
40014     hideSplitter : function(){
40015         if(this.split){
40016             this.split.el.setLocation(-2000,-2000);
40017             this.split.el.hide();
40018         }
40019     },
40020
40021     show : function(){
40022         if(this.split){
40023             this.split.el.show();
40024         }
40025         Roo.bootstrap.layout.Split.superclass.show.call(this);
40026     },
40027     
40028     beforeSlide: function(){
40029         if(Roo.isGecko){// firefox overflow auto bug workaround
40030             this.bodyEl.clip();
40031             if(this.tabs) {
40032                 this.tabs.bodyEl.clip();
40033             }
40034             if(this.activePanel){
40035                 this.activePanel.getEl().clip();
40036                 
40037                 if(this.activePanel.beforeSlide){
40038                     this.activePanel.beforeSlide();
40039                 }
40040             }
40041         }
40042     },
40043     
40044     afterSlide : function(){
40045         if(Roo.isGecko){// firefox overflow auto bug workaround
40046             this.bodyEl.unclip();
40047             if(this.tabs) {
40048                 this.tabs.bodyEl.unclip();
40049             }
40050             if(this.activePanel){
40051                 this.activePanel.getEl().unclip();
40052                 if(this.activePanel.afterSlide){
40053                     this.activePanel.afterSlide();
40054                 }
40055             }
40056         }
40057     },
40058
40059     initAutoHide : function(){
40060         if(this.autoHide !== false){
40061             if(!this.autoHideHd){
40062                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40063                 this.autoHideHd = {
40064                     "mouseout": function(e){
40065                         if(!e.within(this.el, true)){
40066                             st.delay(500);
40067                         }
40068                     },
40069                     "mouseover" : function(e){
40070                         st.cancel();
40071                     },
40072                     scope : this
40073                 };
40074             }
40075             this.el.on(this.autoHideHd);
40076         }
40077     },
40078
40079     clearAutoHide : function(){
40080         if(this.autoHide !== false){
40081             this.el.un("mouseout", this.autoHideHd.mouseout);
40082             this.el.un("mouseover", this.autoHideHd.mouseover);
40083         }
40084     },
40085
40086     clearMonitor : function(){
40087         Roo.get(document).un("click", this.slideInIf, this);
40088     },
40089
40090     // these names are backwards but not changed for compat
40091     slideOut : function(){
40092         if(this.isSlid || this.el.hasActiveFx()){
40093             return;
40094         }
40095         this.isSlid = true;
40096         if(this.collapseBtn){
40097             this.collapseBtn.hide();
40098         }
40099         this.closeBtnState = this.closeBtn.getStyle('display');
40100         this.closeBtn.hide();
40101         if(this.stickBtn){
40102             this.stickBtn.show();
40103         }
40104         this.el.show();
40105         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40106         this.beforeSlide();
40107         this.el.setStyle("z-index", 10001);
40108         this.el.slideIn(this.getSlideAnchor(), {
40109             callback: function(){
40110                 this.afterSlide();
40111                 this.initAutoHide();
40112                 Roo.get(document).on("click", this.slideInIf, this);
40113                 this.fireEvent("slideshow", this);
40114             },
40115             scope: this,
40116             block: true
40117         });
40118     },
40119
40120     afterSlideIn : function(){
40121         this.clearAutoHide();
40122         this.isSlid = false;
40123         this.clearMonitor();
40124         this.el.setStyle("z-index", "");
40125         if(this.collapseBtn){
40126             this.collapseBtn.show();
40127         }
40128         this.closeBtn.setStyle('display', this.closeBtnState);
40129         if(this.stickBtn){
40130             this.stickBtn.hide();
40131         }
40132         this.fireEvent("slidehide", this);
40133     },
40134
40135     slideIn : function(cb){
40136         if(!this.isSlid || this.el.hasActiveFx()){
40137             Roo.callback(cb);
40138             return;
40139         }
40140         this.isSlid = false;
40141         this.beforeSlide();
40142         this.el.slideOut(this.getSlideAnchor(), {
40143             callback: function(){
40144                 this.el.setLeftTop(-10000, -10000);
40145                 this.afterSlide();
40146                 this.afterSlideIn();
40147                 Roo.callback(cb);
40148             },
40149             scope: this,
40150             block: true
40151         });
40152     },
40153     
40154     slideInIf : function(e){
40155         if(!e.within(this.el)){
40156             this.slideIn();
40157         }
40158     },
40159
40160     animateCollapse : function(){
40161         this.beforeSlide();
40162         this.el.setStyle("z-index", 20000);
40163         var anchor = this.getSlideAnchor();
40164         this.el.slideOut(anchor, {
40165             callback : function(){
40166                 this.el.setStyle("z-index", "");
40167                 this.collapsedEl.slideIn(anchor, {duration:.3});
40168                 this.afterSlide();
40169                 this.el.setLocation(-10000,-10000);
40170                 this.el.hide();
40171                 this.fireEvent("collapsed", this);
40172             },
40173             scope: this,
40174             block: true
40175         });
40176     },
40177
40178     animateExpand : function(){
40179         this.beforeSlide();
40180         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40181         this.el.setStyle("z-index", 20000);
40182         this.collapsedEl.hide({
40183             duration:.1
40184         });
40185         this.el.slideIn(this.getSlideAnchor(), {
40186             callback : function(){
40187                 this.el.setStyle("z-index", "");
40188                 this.afterSlide();
40189                 if(this.split){
40190                     this.split.el.show();
40191                 }
40192                 this.fireEvent("invalidated", this);
40193                 this.fireEvent("expanded", this);
40194             },
40195             scope: this,
40196             block: true
40197         });
40198     },
40199
40200     anchors : {
40201         "west" : "left",
40202         "east" : "right",
40203         "north" : "top",
40204         "south" : "bottom"
40205     },
40206
40207     sanchors : {
40208         "west" : "l",
40209         "east" : "r",
40210         "north" : "t",
40211         "south" : "b"
40212     },
40213
40214     canchors : {
40215         "west" : "tl-tr",
40216         "east" : "tr-tl",
40217         "north" : "tl-bl",
40218         "south" : "bl-tl"
40219     },
40220
40221     getAnchor : function(){
40222         return this.anchors[this.position];
40223     },
40224
40225     getCollapseAnchor : function(){
40226         return this.canchors[this.position];
40227     },
40228
40229     getSlideAnchor : function(){
40230         return this.sanchors[this.position];
40231     },
40232
40233     getAlignAdj : function(){
40234         var cm = this.cmargins;
40235         switch(this.position){
40236             case "west":
40237                 return [0, 0];
40238             break;
40239             case "east":
40240                 return [0, 0];
40241             break;
40242             case "north":
40243                 return [0, 0];
40244             break;
40245             case "south":
40246                 return [0, 0];
40247             break;
40248         }
40249     },
40250
40251     getExpandAdj : function(){
40252         var c = this.collapsedEl, cm = this.cmargins;
40253         switch(this.position){
40254             case "west":
40255                 return [-(cm.right+c.getWidth()+cm.left), 0];
40256             break;
40257             case "east":
40258                 return [cm.right+c.getWidth()+cm.left, 0];
40259             break;
40260             case "north":
40261                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40262             break;
40263             case "south":
40264                 return [0, cm.top+cm.bottom+c.getHeight()];
40265             break;
40266         }
40267     }
40268 });/*
40269  * Based on:
40270  * Ext JS Library 1.1.1
40271  * Copyright(c) 2006-2007, Ext JS, LLC.
40272  *
40273  * Originally Released Under LGPL - original licence link has changed is not relivant.
40274  *
40275  * Fork - LGPL
40276  * <script type="text/javascript">
40277  */
40278 /*
40279  * These classes are private internal classes
40280  */
40281 Roo.bootstrap.layout.Center = function(config){
40282     config.region = "center";
40283     Roo.bootstrap.layout.Region.call(this, config);
40284     this.visible = true;
40285     this.minWidth = config.minWidth || 20;
40286     this.minHeight = config.minHeight || 20;
40287 };
40288
40289 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40290     hide : function(){
40291         // center panel can't be hidden
40292     },
40293     
40294     show : function(){
40295         // center panel can't be hidden
40296     },
40297     
40298     getMinWidth: function(){
40299         return this.minWidth;
40300     },
40301     
40302     getMinHeight: function(){
40303         return this.minHeight;
40304     }
40305 });
40306
40307
40308
40309
40310  
40311
40312
40313
40314
40315
40316
40317 Roo.bootstrap.layout.North = function(config)
40318 {
40319     config.region = 'north';
40320     config.cursor = 'n-resize';
40321     
40322     Roo.bootstrap.layout.Split.call(this, config);
40323     
40324     
40325     if(this.split){
40326         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40327         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40328         this.split.el.addClass("roo-layout-split-v");
40329     }
40330     //var size = config.initialSize || config.height;
40331     //if(this.el && typeof size != "undefined"){
40332     //    this.el.setHeight(size);
40333     //}
40334 };
40335 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40336 {
40337     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40338      
40339      
40340     onRender : function(ctr, pos)
40341     {
40342         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40343         var size = this.config.initialSize || this.config.height;
40344         if(this.el && typeof size != "undefined"){
40345             this.el.setHeight(size);
40346         }
40347     
40348     },
40349     
40350     getBox : function(){
40351         if(this.collapsed){
40352             return this.collapsedEl.getBox();
40353         }
40354         var box = this.el.getBox();
40355         if(this.split){
40356             box.height += this.split.el.getHeight();
40357         }
40358         return box;
40359     },
40360     
40361     updateBox : function(box){
40362         if(this.split && !this.collapsed){
40363             box.height -= this.split.el.getHeight();
40364             this.split.el.setLeft(box.x);
40365             this.split.el.setTop(box.y+box.height);
40366             this.split.el.setWidth(box.width);
40367         }
40368         if(this.collapsed){
40369             this.updateBody(box.width, null);
40370         }
40371         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40372     }
40373 });
40374
40375
40376
40377
40378
40379 Roo.bootstrap.layout.South = function(config){
40380     config.region = 'south';
40381     config.cursor = 's-resize';
40382     Roo.bootstrap.layout.Split.call(this, config);
40383     if(this.split){
40384         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40385         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40386         this.split.el.addClass("roo-layout-split-v");
40387     }
40388     
40389 };
40390
40391 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40392     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40393     
40394     onRender : function(ctr, pos)
40395     {
40396         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40397         var size = this.config.initialSize || this.config.height;
40398         if(this.el && typeof size != "undefined"){
40399             this.el.setHeight(size);
40400         }
40401     
40402     },
40403     
40404     getBox : function(){
40405         if(this.collapsed){
40406             return this.collapsedEl.getBox();
40407         }
40408         var box = this.el.getBox();
40409         if(this.split){
40410             var sh = this.split.el.getHeight();
40411             box.height += sh;
40412             box.y -= sh;
40413         }
40414         return box;
40415     },
40416     
40417     updateBox : function(box){
40418         if(this.split && !this.collapsed){
40419             var sh = this.split.el.getHeight();
40420             box.height -= sh;
40421             box.y += sh;
40422             this.split.el.setLeft(box.x);
40423             this.split.el.setTop(box.y-sh);
40424             this.split.el.setWidth(box.width);
40425         }
40426         if(this.collapsed){
40427             this.updateBody(box.width, null);
40428         }
40429         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40430     }
40431 });
40432
40433 Roo.bootstrap.layout.East = function(config){
40434     config.region = "east";
40435     config.cursor = "e-resize";
40436     Roo.bootstrap.layout.Split.call(this, config);
40437     if(this.split){
40438         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40439         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40440         this.split.el.addClass("roo-layout-split-h");
40441     }
40442     
40443 };
40444 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40445     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40446     
40447     onRender : function(ctr, pos)
40448     {
40449         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40450         var size = this.config.initialSize || this.config.width;
40451         if(this.el && typeof size != "undefined"){
40452             this.el.setWidth(size);
40453         }
40454     
40455     },
40456     
40457     getBox : function(){
40458         if(this.collapsed){
40459             return this.collapsedEl.getBox();
40460         }
40461         var box = this.el.getBox();
40462         if(this.split){
40463             var sw = this.split.el.getWidth();
40464             box.width += sw;
40465             box.x -= sw;
40466         }
40467         return box;
40468     },
40469
40470     updateBox : function(box){
40471         if(this.split && !this.collapsed){
40472             var sw = this.split.el.getWidth();
40473             box.width -= sw;
40474             this.split.el.setLeft(box.x);
40475             this.split.el.setTop(box.y);
40476             this.split.el.setHeight(box.height);
40477             box.x += sw;
40478         }
40479         if(this.collapsed){
40480             this.updateBody(null, box.height);
40481         }
40482         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40483     }
40484 });
40485
40486 Roo.bootstrap.layout.West = function(config){
40487     config.region = "west";
40488     config.cursor = "w-resize";
40489     
40490     Roo.bootstrap.layout.Split.call(this, config);
40491     if(this.split){
40492         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40493         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40494         this.split.el.addClass("roo-layout-split-h");
40495     }
40496     
40497 };
40498 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40499     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40500     
40501     onRender: function(ctr, pos)
40502     {
40503         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40504         var size = this.config.initialSize || this.config.width;
40505         if(typeof size != "undefined"){
40506             this.el.setWidth(size);
40507         }
40508     },
40509     
40510     getBox : function(){
40511         if(this.collapsed){
40512             return this.collapsedEl.getBox();
40513         }
40514         var box = this.el.getBox();
40515         if (box.width == 0) {
40516             box.width = this.config.width; // kludge?
40517         }
40518         if(this.split){
40519             box.width += this.split.el.getWidth();
40520         }
40521         return box;
40522     },
40523     
40524     updateBox : function(box){
40525         if(this.split && !this.collapsed){
40526             var sw = this.split.el.getWidth();
40527             box.width -= sw;
40528             this.split.el.setLeft(box.x+box.width);
40529             this.split.el.setTop(box.y);
40530             this.split.el.setHeight(box.height);
40531         }
40532         if(this.collapsed){
40533             this.updateBody(null, box.height);
40534         }
40535         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40536     }
40537 });Roo.namespace("Roo.bootstrap.panel");/*
40538  * Based on:
40539  * Ext JS Library 1.1.1
40540  * Copyright(c) 2006-2007, Ext JS, LLC.
40541  *
40542  * Originally Released Under LGPL - original licence link has changed is not relivant.
40543  *
40544  * Fork - LGPL
40545  * <script type="text/javascript">
40546  */
40547 /**
40548  * @class Roo.ContentPanel
40549  * @extends Roo.util.Observable
40550  * A basic ContentPanel element.
40551  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40552  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40553  * @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
40554  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40555  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40556  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40557  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40558  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40559  * @cfg {String} title          The title for this panel
40560  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40561  * @cfg {String} url            Calls {@link #setUrl} with this value
40562  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40563  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40564  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40565  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40566  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40567  * @cfg {Boolean} badges render the badges
40568  * @cfg {String} cls  extra classes to use  
40569  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40570
40571  * @constructor
40572  * Create a new ContentPanel.
40573  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40574  * @param {String/Object} config A string to set only the title or a config object
40575  * @param {String} content (optional) Set the HTML content for this panel
40576  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40577  */
40578 Roo.bootstrap.panel.Content = function( config){
40579     
40580     this.tpl = config.tpl || false;
40581     
40582     var el = config.el;
40583     var content = config.content;
40584
40585     if(config.autoCreate){ // xtype is available if this is called from factory
40586         el = Roo.id();
40587     }
40588     this.el = Roo.get(el);
40589     if(!this.el && config && config.autoCreate){
40590         if(typeof config.autoCreate == "object"){
40591             if(!config.autoCreate.id){
40592                 config.autoCreate.id = config.id||el;
40593             }
40594             this.el = Roo.DomHelper.append(document.body,
40595                         config.autoCreate, true);
40596         }else{
40597             var elcfg =  {
40598                 tag: "div",
40599                 cls: (config.cls || '') +
40600                     (config.background ? ' bg-' + config.background : '') +
40601                     " roo-layout-inactive-content",
40602                 id: config.id||el
40603             };
40604             if (config.iframe) {
40605                 elcfg.cn = [
40606                     {
40607                         tag : 'iframe',
40608                         style : 'border: 0px',
40609                         src : 'about:blank'
40610                     }
40611                 ];
40612             }
40613               
40614             if (config.html) {
40615                 elcfg.html = config.html;
40616                 
40617             }
40618                         
40619             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40620             if (config.iframe) {
40621                 this.iframeEl = this.el.select('iframe',true).first();
40622             }
40623             
40624         }
40625     } 
40626     this.closable = false;
40627     this.loaded = false;
40628     this.active = false;
40629    
40630       
40631     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40632         
40633         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40634         
40635         this.wrapEl = this.el; //this.el.wrap();
40636         var ti = [];
40637         if (config.toolbar.items) {
40638             ti = config.toolbar.items ;
40639             delete config.toolbar.items ;
40640         }
40641         
40642         var nitems = [];
40643         this.toolbar.render(this.wrapEl, 'before');
40644         for(var i =0;i < ti.length;i++) {
40645           //  Roo.log(['add child', items[i]]);
40646             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40647         }
40648         this.toolbar.items = nitems;
40649         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40650         delete config.toolbar;
40651         
40652     }
40653     /*
40654     // xtype created footer. - not sure if will work as we normally have to render first..
40655     if (this.footer && !this.footer.el && this.footer.xtype) {
40656         if (!this.wrapEl) {
40657             this.wrapEl = this.el.wrap();
40658         }
40659     
40660         this.footer.container = this.wrapEl.createChild();
40661          
40662         this.footer = Roo.factory(this.footer, Roo);
40663         
40664     }
40665     */
40666     
40667      if(typeof config == "string"){
40668         this.title = config;
40669     }else{
40670         Roo.apply(this, config);
40671     }
40672     
40673     if(this.resizeEl){
40674         this.resizeEl = Roo.get(this.resizeEl, true);
40675     }else{
40676         this.resizeEl = this.el;
40677     }
40678     // handle view.xtype
40679     
40680  
40681     
40682     
40683     this.addEvents({
40684         /**
40685          * @event activate
40686          * Fires when this panel is activated. 
40687          * @param {Roo.ContentPanel} this
40688          */
40689         "activate" : true,
40690         /**
40691          * @event deactivate
40692          * Fires when this panel is activated. 
40693          * @param {Roo.ContentPanel} this
40694          */
40695         "deactivate" : true,
40696
40697         /**
40698          * @event resize
40699          * Fires when this panel is resized if fitToFrame is true.
40700          * @param {Roo.ContentPanel} this
40701          * @param {Number} width The width after any component adjustments
40702          * @param {Number} height The height after any component adjustments
40703          */
40704         "resize" : true,
40705         
40706          /**
40707          * @event render
40708          * Fires when this tab is created
40709          * @param {Roo.ContentPanel} this
40710          */
40711         "render" : true,
40712         
40713           /**
40714          * @event scroll
40715          * Fires when this content is scrolled
40716          * @param {Roo.ContentPanel} this
40717          * @param {Event} scrollEvent
40718          */
40719         "scroll" : true
40720         
40721         
40722         
40723     });
40724     
40725
40726     
40727     
40728     if(this.autoScroll && !this.iframe){
40729         this.resizeEl.setStyle("overflow", "auto");
40730         this.resizeEl.on('scroll', this.onScroll, this);
40731     } else {
40732         // fix randome scrolling
40733         //this.el.on('scroll', function() {
40734         //    Roo.log('fix random scolling');
40735         //    this.scrollTo('top',0); 
40736         //});
40737     }
40738     content = content || this.content;
40739     if(content){
40740         this.setContent(content);
40741     }
40742     if(config && config.url){
40743         this.setUrl(this.url, this.params, this.loadOnce);
40744     }
40745     
40746     
40747     
40748     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40749     
40750     if (this.view && typeof(this.view.xtype) != 'undefined') {
40751         this.view.el = this.el.appendChild(document.createElement("div"));
40752         this.view = Roo.factory(this.view); 
40753         this.view.render  &&  this.view.render(false, '');  
40754     }
40755     
40756     
40757     this.fireEvent('render', this);
40758 };
40759
40760 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40761     
40762     cls : '',
40763     background : '',
40764     
40765     tabTip : '',
40766     
40767     iframe : false,
40768     iframeEl : false,
40769     
40770     /* Resize Element - use this to work out scroll etc. */
40771     resizeEl : false,
40772     
40773     setRegion : function(region){
40774         this.region = region;
40775         this.setActiveClass(region && !this.background);
40776     },
40777     
40778     
40779     setActiveClass: function(state)
40780     {
40781         if(state){
40782            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40783            this.el.setStyle('position','relative');
40784         }else{
40785            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40786            this.el.setStyle('position', 'absolute');
40787         } 
40788     },
40789     
40790     /**
40791      * Returns the toolbar for this Panel if one was configured. 
40792      * @return {Roo.Toolbar} 
40793      */
40794     getToolbar : function(){
40795         return this.toolbar;
40796     },
40797     
40798     setActiveState : function(active)
40799     {
40800         this.active = active;
40801         this.setActiveClass(active);
40802         if(!active){
40803             if(this.fireEvent("deactivate", this) === false){
40804                 return false;
40805             }
40806             return true;
40807         }
40808         this.fireEvent("activate", this);
40809         return true;
40810     },
40811     /**
40812      * Updates this panel's element (not for iframe)
40813      * @param {String} content The new content
40814      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40815     */
40816     setContent : function(content, loadScripts){
40817         if (this.iframe) {
40818             return;
40819         }
40820         
40821         this.el.update(content, loadScripts);
40822     },
40823
40824     ignoreResize : function(w, h){
40825         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40826             return true;
40827         }else{
40828             this.lastSize = {width: w, height: h};
40829             return false;
40830         }
40831     },
40832     /**
40833      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40834      * @return {Roo.UpdateManager} The UpdateManager
40835      */
40836     getUpdateManager : function(){
40837         if (this.iframe) {
40838             return false;
40839         }
40840         return this.el.getUpdateManager();
40841     },
40842      /**
40843      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40844      * Does not work with IFRAME contents
40845      * @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:
40846 <pre><code>
40847 panel.load({
40848     url: "your-url.php",
40849     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40850     callback: yourFunction,
40851     scope: yourObject, //(optional scope)
40852     discardUrl: false,
40853     nocache: false,
40854     text: "Loading...",
40855     timeout: 30,
40856     scripts: false
40857 });
40858 </code></pre>
40859      
40860      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40861      * 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.
40862      * @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}
40863      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40864      * @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.
40865      * @return {Roo.ContentPanel} this
40866      */
40867     load : function(){
40868         
40869         if (this.iframe) {
40870             return this;
40871         }
40872         
40873         var um = this.el.getUpdateManager();
40874         um.update.apply(um, arguments);
40875         return this;
40876     },
40877
40878
40879     /**
40880      * 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.
40881      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40882      * @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)
40883      * @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)
40884      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40885      */
40886     setUrl : function(url, params, loadOnce){
40887         if (this.iframe) {
40888             this.iframeEl.dom.src = url;
40889             return false;
40890         }
40891         
40892         if(this.refreshDelegate){
40893             this.removeListener("activate", this.refreshDelegate);
40894         }
40895         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40896         this.on("activate", this.refreshDelegate);
40897         return this.el.getUpdateManager();
40898     },
40899     
40900     _handleRefresh : function(url, params, loadOnce){
40901         if(!loadOnce || !this.loaded){
40902             var updater = this.el.getUpdateManager();
40903             updater.update(url, params, this._setLoaded.createDelegate(this));
40904         }
40905     },
40906     
40907     _setLoaded : function(){
40908         this.loaded = true;
40909     }, 
40910     
40911     /**
40912      * Returns this panel's id
40913      * @return {String} 
40914      */
40915     getId : function(){
40916         return this.el.id;
40917     },
40918     
40919     /** 
40920      * Returns this panel's element - used by regiosn to add.
40921      * @return {Roo.Element} 
40922      */
40923     getEl : function(){
40924         return this.wrapEl || this.el;
40925     },
40926     
40927    
40928     
40929     adjustForComponents : function(width, height)
40930     {
40931         //Roo.log('adjustForComponents ');
40932         if(this.resizeEl != this.el){
40933             width -= this.el.getFrameWidth('lr');
40934             height -= this.el.getFrameWidth('tb');
40935         }
40936         if(this.toolbar){
40937             var te = this.toolbar.getEl();
40938             te.setWidth(width);
40939             height -= te.getHeight();
40940         }
40941         if(this.footer){
40942             var te = this.footer.getEl();
40943             te.setWidth(width);
40944             height -= te.getHeight();
40945         }
40946         
40947         
40948         if(this.adjustments){
40949             width += this.adjustments[0];
40950             height += this.adjustments[1];
40951         }
40952         return {"width": width, "height": height};
40953     },
40954     
40955     setSize : function(width, height){
40956         if(this.fitToFrame && !this.ignoreResize(width, height)){
40957             if(this.fitContainer && this.resizeEl != this.el){
40958                 this.el.setSize(width, height);
40959             }
40960             var size = this.adjustForComponents(width, height);
40961             if (this.iframe) {
40962                 this.iframeEl.setSize(width,height);
40963             }
40964             
40965             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40966             this.fireEvent('resize', this, size.width, size.height);
40967             
40968             
40969         }
40970     },
40971     
40972     /**
40973      * Returns this panel's title
40974      * @return {String} 
40975      */
40976     getTitle : function(){
40977         
40978         if (typeof(this.title) != 'object') {
40979             return this.title;
40980         }
40981         
40982         var t = '';
40983         for (var k in this.title) {
40984             if (!this.title.hasOwnProperty(k)) {
40985                 continue;
40986             }
40987             
40988             if (k.indexOf('-') >= 0) {
40989                 var s = k.split('-');
40990                 for (var i = 0; i<s.length; i++) {
40991                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40992                 }
40993             } else {
40994                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40995             }
40996         }
40997         return t;
40998     },
40999     
41000     /**
41001      * Set this panel's title
41002      * @param {String} title
41003      */
41004     setTitle : function(title){
41005         this.title = title;
41006         if(this.region){
41007             this.region.updatePanelTitle(this, title);
41008         }
41009     },
41010     
41011     /**
41012      * Returns true is this panel was configured to be closable
41013      * @return {Boolean} 
41014      */
41015     isClosable : function(){
41016         return this.closable;
41017     },
41018     
41019     beforeSlide : function(){
41020         this.el.clip();
41021         this.resizeEl.clip();
41022     },
41023     
41024     afterSlide : function(){
41025         this.el.unclip();
41026         this.resizeEl.unclip();
41027     },
41028     
41029     /**
41030      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41031      *   Will fail silently if the {@link #setUrl} method has not been called.
41032      *   This does not activate the panel, just updates its content.
41033      */
41034     refresh : function(){
41035         if(this.refreshDelegate){
41036            this.loaded = false;
41037            this.refreshDelegate();
41038         }
41039     },
41040     
41041     /**
41042      * Destroys this panel
41043      */
41044     destroy : function(){
41045         this.el.removeAllListeners();
41046         var tempEl = document.createElement("span");
41047         tempEl.appendChild(this.el.dom);
41048         tempEl.innerHTML = "";
41049         this.el.remove();
41050         this.el = null;
41051     },
41052     
41053     /**
41054      * form - if the content panel contains a form - this is a reference to it.
41055      * @type {Roo.form.Form}
41056      */
41057     form : false,
41058     /**
41059      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41060      *    This contains a reference to it.
41061      * @type {Roo.View}
41062      */
41063     view : false,
41064     
41065       /**
41066      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41067      * <pre><code>
41068
41069 layout.addxtype({
41070        xtype : 'Form',
41071        items: [ .... ]
41072    }
41073 );
41074
41075 </code></pre>
41076      * @param {Object} cfg Xtype definition of item to add.
41077      */
41078     
41079     
41080     getChildContainer: function () {
41081         return this.getEl();
41082     },
41083     
41084     
41085     onScroll : function(e)
41086     {
41087         this.fireEvent('scroll', this, e);
41088     }
41089     
41090     
41091     /*
41092         var  ret = new Roo.factory(cfg);
41093         return ret;
41094         
41095         
41096         // add form..
41097         if (cfg.xtype.match(/^Form$/)) {
41098             
41099             var el;
41100             //if (this.footer) {
41101             //    el = this.footer.container.insertSibling(false, 'before');
41102             //} else {
41103                 el = this.el.createChild();
41104             //}
41105
41106             this.form = new  Roo.form.Form(cfg);
41107             
41108             
41109             if ( this.form.allItems.length) {
41110                 this.form.render(el.dom);
41111             }
41112             return this.form;
41113         }
41114         // should only have one of theses..
41115         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41116             // views.. should not be just added - used named prop 'view''
41117             
41118             cfg.el = this.el.appendChild(document.createElement("div"));
41119             // factory?
41120             
41121             var ret = new Roo.factory(cfg);
41122              
41123              ret.render && ret.render(false, ''); // render blank..
41124             this.view = ret;
41125             return ret;
41126         }
41127         return false;
41128     }
41129     \*/
41130 });
41131  
41132 /**
41133  * @class Roo.bootstrap.panel.Grid
41134  * @extends Roo.bootstrap.panel.Content
41135  * @constructor
41136  * Create a new GridPanel.
41137  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41138  * @param {Object} config A the config object
41139   
41140  */
41141
41142
41143
41144 Roo.bootstrap.panel.Grid = function(config)
41145 {
41146     
41147       
41148     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41149         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41150
41151     config.el = this.wrapper;
41152     //this.el = this.wrapper;
41153     
41154       if (config.container) {
41155         // ctor'ed from a Border/panel.grid
41156         
41157         
41158         this.wrapper.setStyle("overflow", "hidden");
41159         this.wrapper.addClass('roo-grid-container');
41160
41161     }
41162     
41163     
41164     if(config.toolbar){
41165         var tool_el = this.wrapper.createChild();    
41166         this.toolbar = Roo.factory(config.toolbar);
41167         var ti = [];
41168         if (config.toolbar.items) {
41169             ti = config.toolbar.items ;
41170             delete config.toolbar.items ;
41171         }
41172         
41173         var nitems = [];
41174         this.toolbar.render(tool_el);
41175         for(var i =0;i < ti.length;i++) {
41176           //  Roo.log(['add child', items[i]]);
41177             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41178         }
41179         this.toolbar.items = nitems;
41180         
41181         delete config.toolbar;
41182     }
41183     
41184     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41185     config.grid.scrollBody = true;;
41186     config.grid.monitorWindowResize = false; // turn off autosizing
41187     config.grid.autoHeight = false;
41188     config.grid.autoWidth = false;
41189     
41190     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41191     
41192     if (config.background) {
41193         // render grid on panel activation (if panel background)
41194         this.on('activate', function(gp) {
41195             if (!gp.grid.rendered) {
41196                 gp.grid.render(this.wrapper);
41197                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41198             }
41199         });
41200             
41201     } else {
41202         this.grid.render(this.wrapper);
41203         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41204
41205     }
41206     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41207     // ??? needed ??? config.el = this.wrapper;
41208     
41209     
41210     
41211   
41212     // xtype created footer. - not sure if will work as we normally have to render first..
41213     if (this.footer && !this.footer.el && this.footer.xtype) {
41214         
41215         var ctr = this.grid.getView().getFooterPanel(true);
41216         this.footer.dataSource = this.grid.dataSource;
41217         this.footer = Roo.factory(this.footer, Roo);
41218         this.footer.render(ctr);
41219         
41220     }
41221     
41222     
41223     
41224     
41225      
41226 };
41227
41228 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41229     getId : function(){
41230         return this.grid.id;
41231     },
41232     
41233     /**
41234      * Returns the grid for this panel
41235      * @return {Roo.bootstrap.Table} 
41236      */
41237     getGrid : function(){
41238         return this.grid;    
41239     },
41240     
41241     setSize : function(width, height){
41242         if(!this.ignoreResize(width, height)){
41243             var grid = this.grid;
41244             var size = this.adjustForComponents(width, height);
41245             // tfoot is not a footer?
41246           
41247             
41248             var gridel = grid.getGridEl();
41249             gridel.setSize(size.width, size.height);
41250             
41251             var tbd = grid.getGridEl().select('tbody', true).first();
41252             var thd = grid.getGridEl().select('thead',true).first();
41253             var tbf= grid.getGridEl().select('tfoot', true).first();
41254
41255             if (tbf) {
41256                 size.height -= tbf.getHeight();
41257             }
41258             if (thd) {
41259                 size.height -= thd.getHeight();
41260             }
41261             
41262             tbd.setSize(size.width, size.height );
41263             // this is for the account management tab -seems to work there.
41264             var thd = grid.getGridEl().select('thead',true).first();
41265             //if (tbd) {
41266             //    tbd.setSize(size.width, size.height - thd.getHeight());
41267             //}
41268              
41269             grid.autoSize();
41270         }
41271     },
41272      
41273     
41274     
41275     beforeSlide : function(){
41276         this.grid.getView().scroller.clip();
41277     },
41278     
41279     afterSlide : function(){
41280         this.grid.getView().scroller.unclip();
41281     },
41282     
41283     destroy : function(){
41284         this.grid.destroy();
41285         delete this.grid;
41286         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41287     }
41288 });
41289
41290 /**
41291  * @class Roo.bootstrap.panel.Nest
41292  * @extends Roo.bootstrap.panel.Content
41293  * @constructor
41294  * Create a new Panel, that can contain a layout.Border.
41295  * 
41296  * 
41297  * @param {Roo.BorderLayout} layout The layout for this panel
41298  * @param {String/Object} config A string to set only the title or a config object
41299  */
41300 Roo.bootstrap.panel.Nest = function(config)
41301 {
41302     // construct with only one argument..
41303     /* FIXME - implement nicer consturctors
41304     if (layout.layout) {
41305         config = layout;
41306         layout = config.layout;
41307         delete config.layout;
41308     }
41309     if (layout.xtype && !layout.getEl) {
41310         // then layout needs constructing..
41311         layout = Roo.factory(layout, Roo);
41312     }
41313     */
41314     
41315     config.el =  config.layout.getEl();
41316     
41317     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41318     
41319     config.layout.monitorWindowResize = false; // turn off autosizing
41320     this.layout = config.layout;
41321     this.layout.getEl().addClass("roo-layout-nested-layout");
41322     this.layout.parent = this;
41323     
41324     
41325     
41326     
41327 };
41328
41329 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41330
41331     setSize : function(width, height){
41332         if(!this.ignoreResize(width, height)){
41333             var size = this.adjustForComponents(width, height);
41334             var el = this.layout.getEl();
41335             if (size.height < 1) {
41336                 el.setWidth(size.width);   
41337             } else {
41338                 el.setSize(size.width, size.height);
41339             }
41340             var touch = el.dom.offsetWidth;
41341             this.layout.layout();
41342             // ie requires a double layout on the first pass
41343             if(Roo.isIE && !this.initialized){
41344                 this.initialized = true;
41345                 this.layout.layout();
41346             }
41347         }
41348     },
41349     
41350     // activate all subpanels if not currently active..
41351     
41352     setActiveState : function(active){
41353         this.active = active;
41354         this.setActiveClass(active);
41355         
41356         if(!active){
41357             this.fireEvent("deactivate", this);
41358             return;
41359         }
41360         
41361         this.fireEvent("activate", this);
41362         // not sure if this should happen before or after..
41363         if (!this.layout) {
41364             return; // should not happen..
41365         }
41366         var reg = false;
41367         for (var r in this.layout.regions) {
41368             reg = this.layout.getRegion(r);
41369             if (reg.getActivePanel()) {
41370                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41371                 reg.setActivePanel(reg.getActivePanel());
41372                 continue;
41373             }
41374             if (!reg.panels.length) {
41375                 continue;
41376             }
41377             reg.showPanel(reg.getPanel(0));
41378         }
41379         
41380         
41381         
41382         
41383     },
41384     
41385     /**
41386      * Returns the nested BorderLayout for this panel
41387      * @return {Roo.BorderLayout} 
41388      */
41389     getLayout : function(){
41390         return this.layout;
41391     },
41392     
41393      /**
41394      * Adds a xtype elements to the layout of the nested panel
41395      * <pre><code>
41396
41397 panel.addxtype({
41398        xtype : 'ContentPanel',
41399        region: 'west',
41400        items: [ .... ]
41401    }
41402 );
41403
41404 panel.addxtype({
41405         xtype : 'NestedLayoutPanel',
41406         region: 'west',
41407         layout: {
41408            center: { },
41409            west: { }   
41410         },
41411         items : [ ... list of content panels or nested layout panels.. ]
41412    }
41413 );
41414 </code></pre>
41415      * @param {Object} cfg Xtype definition of item to add.
41416      */
41417     addxtype : function(cfg) {
41418         return this.layout.addxtype(cfg);
41419     
41420     }
41421 });/*
41422  * Based on:
41423  * Ext JS Library 1.1.1
41424  * Copyright(c) 2006-2007, Ext JS, LLC.
41425  *
41426  * Originally Released Under LGPL - original licence link has changed is not relivant.
41427  *
41428  * Fork - LGPL
41429  * <script type="text/javascript">
41430  */
41431 /**
41432  * @class Roo.TabPanel
41433  * @extends Roo.util.Observable
41434  * A lightweight tab container.
41435  * <br><br>
41436  * Usage:
41437  * <pre><code>
41438 // basic tabs 1, built from existing content
41439 var tabs = new Roo.TabPanel("tabs1");
41440 tabs.addTab("script", "View Script");
41441 tabs.addTab("markup", "View Markup");
41442 tabs.activate("script");
41443
41444 // more advanced tabs, built from javascript
41445 var jtabs = new Roo.TabPanel("jtabs");
41446 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41447
41448 // set up the UpdateManager
41449 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41450 var updater = tab2.getUpdateManager();
41451 updater.setDefaultUrl("ajax1.htm");
41452 tab2.on('activate', updater.refresh, updater, true);
41453
41454 // Use setUrl for Ajax loading
41455 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41456 tab3.setUrl("ajax2.htm", null, true);
41457
41458 // Disabled tab
41459 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41460 tab4.disable();
41461
41462 jtabs.activate("jtabs-1");
41463  * </code></pre>
41464  * @constructor
41465  * Create a new TabPanel.
41466  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41467  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41468  */
41469 Roo.bootstrap.panel.Tabs = function(config){
41470     /**
41471     * The container element for this TabPanel.
41472     * @type Roo.Element
41473     */
41474     this.el = Roo.get(config.el);
41475     delete config.el;
41476     if(config){
41477         if(typeof config == "boolean"){
41478             this.tabPosition = config ? "bottom" : "top";
41479         }else{
41480             Roo.apply(this, config);
41481         }
41482     }
41483     
41484     if(this.tabPosition == "bottom"){
41485         // if tabs are at the bottom = create the body first.
41486         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41487         this.el.addClass("roo-tabs-bottom");
41488     }
41489     // next create the tabs holders
41490     
41491     if (this.tabPosition == "west"){
41492         
41493         var reg = this.region; // fake it..
41494         while (reg) {
41495             if (!reg.mgr.parent) {
41496                 break;
41497             }
41498             reg = reg.mgr.parent.region;
41499         }
41500         Roo.log("got nest?");
41501         Roo.log(reg);
41502         if (reg.mgr.getRegion('west')) {
41503             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41504             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41505             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41506             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41507             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41508         
41509             
41510         }
41511         
41512         
41513     } else {
41514      
41515         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41516         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41517         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41518         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41519     }
41520     
41521     
41522     if(Roo.isIE){
41523         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41524     }
41525     
41526     // finally - if tabs are at the top, then create the body last..
41527     if(this.tabPosition != "bottom"){
41528         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41529          * @type Roo.Element
41530          */
41531         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41532         this.el.addClass("roo-tabs-top");
41533     }
41534     this.items = [];
41535
41536     this.bodyEl.setStyle("position", "relative");
41537
41538     this.active = null;
41539     this.activateDelegate = this.activate.createDelegate(this);
41540
41541     this.addEvents({
41542         /**
41543          * @event tabchange
41544          * Fires when the active tab changes
41545          * @param {Roo.TabPanel} this
41546          * @param {Roo.TabPanelItem} activePanel The new active tab
41547          */
41548         "tabchange": true,
41549         /**
41550          * @event beforetabchange
41551          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41552          * @param {Roo.TabPanel} this
41553          * @param {Object} e Set cancel to true on this object to cancel the tab change
41554          * @param {Roo.TabPanelItem} tab The tab being changed to
41555          */
41556         "beforetabchange" : true
41557     });
41558
41559     Roo.EventManager.onWindowResize(this.onResize, this);
41560     this.cpad = this.el.getPadding("lr");
41561     this.hiddenCount = 0;
41562
41563
41564     // toolbar on the tabbar support...
41565     if (this.toolbar) {
41566         alert("no toolbar support yet");
41567         this.toolbar  = false;
41568         /*
41569         var tcfg = this.toolbar;
41570         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41571         this.toolbar = new Roo.Toolbar(tcfg);
41572         if (Roo.isSafari) {
41573             var tbl = tcfg.container.child('table', true);
41574             tbl.setAttribute('width', '100%');
41575         }
41576         */
41577         
41578     }
41579    
41580
41581
41582     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41583 };
41584
41585 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41586     /*
41587      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41588      */
41589     tabPosition : "top",
41590     /*
41591      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41592      */
41593     currentTabWidth : 0,
41594     /*
41595      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41596      */
41597     minTabWidth : 40,
41598     /*
41599      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41600      */
41601     maxTabWidth : 250,
41602     /*
41603      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41604      */
41605     preferredTabWidth : 175,
41606     /*
41607      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41608      */
41609     resizeTabs : false,
41610     /*
41611      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41612      */
41613     monitorResize : true,
41614     /*
41615      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41616      */
41617     toolbar : false,  // set by caller..
41618     
41619     region : false, /// set by caller
41620     
41621     disableTooltips : true, // not used yet...
41622
41623     /**
41624      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41625      * @param {String} id The id of the div to use <b>or create</b>
41626      * @param {String} text The text for the tab
41627      * @param {String} content (optional) Content to put in the TabPanelItem body
41628      * @param {Boolean} closable (optional) True to create a close icon on the tab
41629      * @return {Roo.TabPanelItem} The created TabPanelItem
41630      */
41631     addTab : function(id, text, content, closable, tpl)
41632     {
41633         var item = new Roo.bootstrap.panel.TabItem({
41634             panel: this,
41635             id : id,
41636             text : text,
41637             closable : closable,
41638             tpl : tpl
41639         });
41640         this.addTabItem(item);
41641         if(content){
41642             item.setContent(content);
41643         }
41644         return item;
41645     },
41646
41647     /**
41648      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41649      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41650      * @return {Roo.TabPanelItem}
41651      */
41652     getTab : function(id){
41653         return this.items[id];
41654     },
41655
41656     /**
41657      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41658      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41659      */
41660     hideTab : function(id){
41661         var t = this.items[id];
41662         if(!t.isHidden()){
41663            t.setHidden(true);
41664            this.hiddenCount++;
41665            this.autoSizeTabs();
41666         }
41667     },
41668
41669     /**
41670      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41671      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41672      */
41673     unhideTab : function(id){
41674         var t = this.items[id];
41675         if(t.isHidden()){
41676            t.setHidden(false);
41677            this.hiddenCount--;
41678            this.autoSizeTabs();
41679         }
41680     },
41681
41682     /**
41683      * Adds an existing {@link Roo.TabPanelItem}.
41684      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41685      */
41686     addTabItem : function(item)
41687     {
41688         this.items[item.id] = item;
41689         this.items.push(item);
41690         this.autoSizeTabs();
41691       //  if(this.resizeTabs){
41692     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41693   //         this.autoSizeTabs();
41694 //        }else{
41695 //            item.autoSize();
41696        // }
41697     },
41698
41699     /**
41700      * Removes a {@link Roo.TabPanelItem}.
41701      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41702      */
41703     removeTab : function(id){
41704         var items = this.items;
41705         var tab = items[id];
41706         if(!tab) { return; }
41707         var index = items.indexOf(tab);
41708         if(this.active == tab && items.length > 1){
41709             var newTab = this.getNextAvailable(index);
41710             if(newTab) {
41711                 newTab.activate();
41712             }
41713         }
41714         this.stripEl.dom.removeChild(tab.pnode.dom);
41715         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41716             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41717         }
41718         items.splice(index, 1);
41719         delete this.items[tab.id];
41720         tab.fireEvent("close", tab);
41721         tab.purgeListeners();
41722         this.autoSizeTabs();
41723     },
41724
41725     getNextAvailable : function(start){
41726         var items = this.items;
41727         var index = start;
41728         // look for a next tab that will slide over to
41729         // replace the one being removed
41730         while(index < items.length){
41731             var item = items[++index];
41732             if(item && !item.isHidden()){
41733                 return item;
41734             }
41735         }
41736         // if one isn't found select the previous tab (on the left)
41737         index = start;
41738         while(index >= 0){
41739             var item = items[--index];
41740             if(item && !item.isHidden()){
41741                 return item;
41742             }
41743         }
41744         return null;
41745     },
41746
41747     /**
41748      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41749      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41750      */
41751     disableTab : function(id){
41752         var tab = this.items[id];
41753         if(tab && this.active != tab){
41754             tab.disable();
41755         }
41756     },
41757
41758     /**
41759      * Enables a {@link Roo.TabPanelItem} that is disabled.
41760      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41761      */
41762     enableTab : function(id){
41763         var tab = this.items[id];
41764         tab.enable();
41765     },
41766
41767     /**
41768      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41769      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41770      * @return {Roo.TabPanelItem} The TabPanelItem.
41771      */
41772     activate : function(id)
41773     {
41774         //Roo.log('activite:'  + id);
41775         
41776         var tab = this.items[id];
41777         if(!tab){
41778             return null;
41779         }
41780         if(tab == this.active || tab.disabled){
41781             return tab;
41782         }
41783         var e = {};
41784         this.fireEvent("beforetabchange", this, e, tab);
41785         if(e.cancel !== true && !tab.disabled){
41786             if(this.active){
41787                 this.active.hide();
41788             }
41789             this.active = this.items[id];
41790             this.active.show();
41791             this.fireEvent("tabchange", this, this.active);
41792         }
41793         return tab;
41794     },
41795
41796     /**
41797      * Gets the active {@link Roo.TabPanelItem}.
41798      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41799      */
41800     getActiveTab : function(){
41801         return this.active;
41802     },
41803
41804     /**
41805      * Updates the tab body element to fit the height of the container element
41806      * for overflow scrolling
41807      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41808      */
41809     syncHeight : function(targetHeight){
41810         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41811         var bm = this.bodyEl.getMargins();
41812         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41813         this.bodyEl.setHeight(newHeight);
41814         return newHeight;
41815     },
41816
41817     onResize : function(){
41818         if(this.monitorResize){
41819             this.autoSizeTabs();
41820         }
41821     },
41822
41823     /**
41824      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41825      */
41826     beginUpdate : function(){
41827         this.updating = true;
41828     },
41829
41830     /**
41831      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41832      */
41833     endUpdate : function(){
41834         this.updating = false;
41835         this.autoSizeTabs();
41836     },
41837
41838     /**
41839      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41840      */
41841     autoSizeTabs : function()
41842     {
41843         var count = this.items.length;
41844         var vcount = count - this.hiddenCount;
41845         
41846         if (vcount < 2) {
41847             this.stripEl.hide();
41848         } else {
41849             this.stripEl.show();
41850         }
41851         
41852         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41853             return;
41854         }
41855         
41856         
41857         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41858         var availWidth = Math.floor(w / vcount);
41859         var b = this.stripBody;
41860         if(b.getWidth() > w){
41861             var tabs = this.items;
41862             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41863             if(availWidth < this.minTabWidth){
41864                 /*if(!this.sleft){    // incomplete scrolling code
41865                     this.createScrollButtons();
41866                 }
41867                 this.showScroll();
41868                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41869             }
41870         }else{
41871             if(this.currentTabWidth < this.preferredTabWidth){
41872                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41873             }
41874         }
41875     },
41876
41877     /**
41878      * Returns the number of tabs in this TabPanel.
41879      * @return {Number}
41880      */
41881      getCount : function(){
41882          return this.items.length;
41883      },
41884
41885     /**
41886      * Resizes all the tabs to the passed width
41887      * @param {Number} The new width
41888      */
41889     setTabWidth : function(width){
41890         this.currentTabWidth = width;
41891         for(var i = 0, len = this.items.length; i < len; i++) {
41892                 if(!this.items[i].isHidden()) {
41893                 this.items[i].setWidth(width);
41894             }
41895         }
41896     },
41897
41898     /**
41899      * Destroys this TabPanel
41900      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41901      */
41902     destroy : function(removeEl){
41903         Roo.EventManager.removeResizeListener(this.onResize, this);
41904         for(var i = 0, len = this.items.length; i < len; i++){
41905             this.items[i].purgeListeners();
41906         }
41907         if(removeEl === true){
41908             this.el.update("");
41909             this.el.remove();
41910         }
41911     },
41912     
41913     createStrip : function(container)
41914     {
41915         var strip = document.createElement("nav");
41916         strip.className = Roo.bootstrap.version == 4 ?
41917             "navbar-light bg-light" : 
41918             "navbar navbar-default"; //"x-tabs-wrap";
41919         container.appendChild(strip);
41920         return strip;
41921     },
41922     
41923     createStripList : function(strip)
41924     {
41925         // div wrapper for retard IE
41926         // returns the "tr" element.
41927         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41928         //'<div class="x-tabs-strip-wrap">'+
41929           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41930           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41931         return strip.firstChild; //.firstChild.firstChild.firstChild;
41932     },
41933     createBody : function(container)
41934     {
41935         var body = document.createElement("div");
41936         Roo.id(body, "tab-body");
41937         //Roo.fly(body).addClass("x-tabs-body");
41938         Roo.fly(body).addClass("tab-content");
41939         container.appendChild(body);
41940         return body;
41941     },
41942     createItemBody :function(bodyEl, id){
41943         var body = Roo.getDom(id);
41944         if(!body){
41945             body = document.createElement("div");
41946             body.id = id;
41947         }
41948         //Roo.fly(body).addClass("x-tabs-item-body");
41949         Roo.fly(body).addClass("tab-pane");
41950          bodyEl.insertBefore(body, bodyEl.firstChild);
41951         return body;
41952     },
41953     /** @private */
41954     createStripElements :  function(stripEl, text, closable, tpl)
41955     {
41956         var td = document.createElement("li"); // was td..
41957         td.className = 'nav-item';
41958         
41959         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41960         
41961         
41962         stripEl.appendChild(td);
41963         /*if(closable){
41964             td.className = "x-tabs-closable";
41965             if(!this.closeTpl){
41966                 this.closeTpl = new Roo.Template(
41967                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41968                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41969                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41970                 );
41971             }
41972             var el = this.closeTpl.overwrite(td, {"text": text});
41973             var close = el.getElementsByTagName("div")[0];
41974             var inner = el.getElementsByTagName("em")[0];
41975             return {"el": el, "close": close, "inner": inner};
41976         } else {
41977         */
41978         // not sure what this is..
41979 //            if(!this.tabTpl){
41980                 //this.tabTpl = new Roo.Template(
41981                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41982                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41983                 //);
41984 //                this.tabTpl = new Roo.Template(
41985 //                   '<a href="#">' +
41986 //                   '<span unselectable="on"' +
41987 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41988 //                            ' >{text}</span></a>'
41989 //                );
41990 //                
41991 //            }
41992
41993
41994             var template = tpl || this.tabTpl || false;
41995             
41996             if(!template){
41997                 template =  new Roo.Template(
41998                         Roo.bootstrap.version == 4 ? 
41999                             (
42000                                 '<a class="nav-link" href="#" unselectable="on"' +
42001                                      (this.disableTooltips ? '' : ' title="{text}"') +
42002                                      ' >{text}</a>'
42003                             ) : (
42004                                 '<a class="nav-link" href="#">' +
42005                                 '<span unselectable="on"' +
42006                                          (this.disableTooltips ? '' : ' title="{text}"') +
42007                                     ' >{text}</span></a>'
42008                             )
42009                 );
42010             }
42011             
42012             switch (typeof(template)) {
42013                 case 'object' :
42014                     break;
42015                 case 'string' :
42016                     template = new Roo.Template(template);
42017                     break;
42018                 default :
42019                     break;
42020             }
42021             
42022             var el = template.overwrite(td, {"text": text});
42023             
42024             var inner = el.getElementsByTagName("span")[0];
42025             
42026             return {"el": el, "inner": inner};
42027             
42028     }
42029         
42030     
42031 });
42032
42033 /**
42034  * @class Roo.TabPanelItem
42035  * @extends Roo.util.Observable
42036  * Represents an individual item (tab plus body) in a TabPanel.
42037  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42038  * @param {String} id The id of this TabPanelItem
42039  * @param {String} text The text for the tab of this TabPanelItem
42040  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42041  */
42042 Roo.bootstrap.panel.TabItem = function(config){
42043     /**
42044      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42045      * @type Roo.TabPanel
42046      */
42047     this.tabPanel = config.panel;
42048     /**
42049      * The id for this TabPanelItem
42050      * @type String
42051      */
42052     this.id = config.id;
42053     /** @private */
42054     this.disabled = false;
42055     /** @private */
42056     this.text = config.text;
42057     /** @private */
42058     this.loaded = false;
42059     this.closable = config.closable;
42060
42061     /**
42062      * The body element for this TabPanelItem.
42063      * @type Roo.Element
42064      */
42065     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42066     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42067     this.bodyEl.setStyle("display", "block");
42068     this.bodyEl.setStyle("zoom", "1");
42069     //this.hideAction();
42070
42071     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42072     /** @private */
42073     this.el = Roo.get(els.el);
42074     this.inner = Roo.get(els.inner, true);
42075      this.textEl = Roo.bootstrap.version == 4 ?
42076         this.el : Roo.get(this.el.dom.firstChild, true);
42077
42078     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42079     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42080
42081     
42082 //    this.el.on("mousedown", this.onTabMouseDown, this);
42083     this.el.on("click", this.onTabClick, this);
42084     /** @private */
42085     if(config.closable){
42086         var c = Roo.get(els.close, true);
42087         c.dom.title = this.closeText;
42088         c.addClassOnOver("close-over");
42089         c.on("click", this.closeClick, this);
42090      }
42091
42092     this.addEvents({
42093          /**
42094          * @event activate
42095          * Fires when this tab becomes the active tab.
42096          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42097          * @param {Roo.TabPanelItem} this
42098          */
42099         "activate": true,
42100         /**
42101          * @event beforeclose
42102          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42103          * @param {Roo.TabPanelItem} this
42104          * @param {Object} e Set cancel to true on this object to cancel the close.
42105          */
42106         "beforeclose": true,
42107         /**
42108          * @event close
42109          * Fires when this tab is closed.
42110          * @param {Roo.TabPanelItem} this
42111          */
42112          "close": true,
42113         /**
42114          * @event deactivate
42115          * Fires when this tab is no longer the active tab.
42116          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42117          * @param {Roo.TabPanelItem} this
42118          */
42119          "deactivate" : true
42120     });
42121     this.hidden = false;
42122
42123     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42124 };
42125
42126 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42127            {
42128     purgeListeners : function(){
42129        Roo.util.Observable.prototype.purgeListeners.call(this);
42130        this.el.removeAllListeners();
42131     },
42132     /**
42133      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42134      */
42135     show : function(){
42136         this.status_node.addClass("active");
42137         this.showAction();
42138         if(Roo.isOpera){
42139             this.tabPanel.stripWrap.repaint();
42140         }
42141         this.fireEvent("activate", this.tabPanel, this);
42142     },
42143
42144     /**
42145      * Returns true if this tab is the active tab.
42146      * @return {Boolean}
42147      */
42148     isActive : function(){
42149         return this.tabPanel.getActiveTab() == this;
42150     },
42151
42152     /**
42153      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42154      */
42155     hide : function(){
42156         this.status_node.removeClass("active");
42157         this.hideAction();
42158         this.fireEvent("deactivate", this.tabPanel, this);
42159     },
42160
42161     hideAction : function(){
42162         this.bodyEl.hide();
42163         this.bodyEl.setStyle("position", "absolute");
42164         this.bodyEl.setLeft("-20000px");
42165         this.bodyEl.setTop("-20000px");
42166     },
42167
42168     showAction : function(){
42169         this.bodyEl.setStyle("position", "relative");
42170         this.bodyEl.setTop("");
42171         this.bodyEl.setLeft("");
42172         this.bodyEl.show();
42173     },
42174
42175     /**
42176      * Set the tooltip for the tab.
42177      * @param {String} tooltip The tab's tooltip
42178      */
42179     setTooltip : function(text){
42180         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42181             this.textEl.dom.qtip = text;
42182             this.textEl.dom.removeAttribute('title');
42183         }else{
42184             this.textEl.dom.title = text;
42185         }
42186     },
42187
42188     onTabClick : function(e){
42189         e.preventDefault();
42190         this.tabPanel.activate(this.id);
42191     },
42192
42193     onTabMouseDown : function(e){
42194         e.preventDefault();
42195         this.tabPanel.activate(this.id);
42196     },
42197 /*
42198     getWidth : function(){
42199         return this.inner.getWidth();
42200     },
42201
42202     setWidth : function(width){
42203         var iwidth = width - this.linode.getPadding("lr");
42204         this.inner.setWidth(iwidth);
42205         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42206         this.linode.setWidth(width);
42207     },
42208 */
42209     /**
42210      * Show or hide the tab
42211      * @param {Boolean} hidden True to hide or false to show.
42212      */
42213     setHidden : function(hidden){
42214         this.hidden = hidden;
42215         this.linode.setStyle("display", hidden ? "none" : "");
42216     },
42217
42218     /**
42219      * Returns true if this tab is "hidden"
42220      * @return {Boolean}
42221      */
42222     isHidden : function(){
42223         return this.hidden;
42224     },
42225
42226     /**
42227      * Returns the text for this tab
42228      * @return {String}
42229      */
42230     getText : function(){
42231         return this.text;
42232     },
42233     /*
42234     autoSize : function(){
42235         //this.el.beginMeasure();
42236         this.textEl.setWidth(1);
42237         /*
42238          *  #2804 [new] Tabs in Roojs
42239          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42240          */
42241         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42242         //this.el.endMeasure();
42243     //},
42244
42245     /**
42246      * Sets the text for the tab (Note: this also sets the tooltip text)
42247      * @param {String} text The tab's text and tooltip
42248      */
42249     setText : function(text){
42250         this.text = text;
42251         this.textEl.update(text);
42252         this.setTooltip(text);
42253         //if(!this.tabPanel.resizeTabs){
42254         //    this.autoSize();
42255         //}
42256     },
42257     /**
42258      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42259      */
42260     activate : function(){
42261         this.tabPanel.activate(this.id);
42262     },
42263
42264     /**
42265      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42266      */
42267     disable : function(){
42268         if(this.tabPanel.active != this){
42269             this.disabled = true;
42270             this.status_node.addClass("disabled");
42271         }
42272     },
42273
42274     /**
42275      * Enables this TabPanelItem if it was previously disabled.
42276      */
42277     enable : function(){
42278         this.disabled = false;
42279         this.status_node.removeClass("disabled");
42280     },
42281
42282     /**
42283      * Sets the content for this TabPanelItem.
42284      * @param {String} content The content
42285      * @param {Boolean} loadScripts true to look for and load scripts
42286      */
42287     setContent : function(content, loadScripts){
42288         this.bodyEl.update(content, loadScripts);
42289     },
42290
42291     /**
42292      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42293      * @return {Roo.UpdateManager} The UpdateManager
42294      */
42295     getUpdateManager : function(){
42296         return this.bodyEl.getUpdateManager();
42297     },
42298
42299     /**
42300      * Set a URL to be used to load the content for this TabPanelItem.
42301      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42302      * @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)
42303      * @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)
42304      * @return {Roo.UpdateManager} The UpdateManager
42305      */
42306     setUrl : function(url, params, loadOnce){
42307         if(this.refreshDelegate){
42308             this.un('activate', this.refreshDelegate);
42309         }
42310         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42311         this.on("activate", this.refreshDelegate);
42312         return this.bodyEl.getUpdateManager();
42313     },
42314
42315     /** @private */
42316     _handleRefresh : function(url, params, loadOnce){
42317         if(!loadOnce || !this.loaded){
42318             var updater = this.bodyEl.getUpdateManager();
42319             updater.update(url, params, this._setLoaded.createDelegate(this));
42320         }
42321     },
42322
42323     /**
42324      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42325      *   Will fail silently if the setUrl method has not been called.
42326      *   This does not activate the panel, just updates its content.
42327      */
42328     refresh : function(){
42329         if(this.refreshDelegate){
42330            this.loaded = false;
42331            this.refreshDelegate();
42332         }
42333     },
42334
42335     /** @private */
42336     _setLoaded : function(){
42337         this.loaded = true;
42338     },
42339
42340     /** @private */
42341     closeClick : function(e){
42342         var o = {};
42343         e.stopEvent();
42344         this.fireEvent("beforeclose", this, o);
42345         if(o.cancel !== true){
42346             this.tabPanel.removeTab(this.id);
42347         }
42348     },
42349     /**
42350      * The text displayed in the tooltip for the close icon.
42351      * @type String
42352      */
42353     closeText : "Close this tab"
42354 });
42355 /**
42356 *    This script refer to:
42357 *    Title: International Telephone Input
42358 *    Author: Jack O'Connor
42359 *    Code version:  v12.1.12
42360 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42361 **/
42362
42363 Roo.bootstrap.PhoneInputData = function() {
42364     var d = [
42365       [
42366         "Afghanistan (‫افغانستان‬‎)",
42367         "af",
42368         "93"
42369       ],
42370       [
42371         "Albania (Shqipëri)",
42372         "al",
42373         "355"
42374       ],
42375       [
42376         "Algeria (‫الجزائر‬‎)",
42377         "dz",
42378         "213"
42379       ],
42380       [
42381         "American Samoa",
42382         "as",
42383         "1684"
42384       ],
42385       [
42386         "Andorra",
42387         "ad",
42388         "376"
42389       ],
42390       [
42391         "Angola",
42392         "ao",
42393         "244"
42394       ],
42395       [
42396         "Anguilla",
42397         "ai",
42398         "1264"
42399       ],
42400       [
42401         "Antigua and Barbuda",
42402         "ag",
42403         "1268"
42404       ],
42405       [
42406         "Argentina",
42407         "ar",
42408         "54"
42409       ],
42410       [
42411         "Armenia (Հայաստան)",
42412         "am",
42413         "374"
42414       ],
42415       [
42416         "Aruba",
42417         "aw",
42418         "297"
42419       ],
42420       [
42421         "Australia",
42422         "au",
42423         "61",
42424         0
42425       ],
42426       [
42427         "Austria (Österreich)",
42428         "at",
42429         "43"
42430       ],
42431       [
42432         "Azerbaijan (Azərbaycan)",
42433         "az",
42434         "994"
42435       ],
42436       [
42437         "Bahamas",
42438         "bs",
42439         "1242"
42440       ],
42441       [
42442         "Bahrain (‫البحرين‬‎)",
42443         "bh",
42444         "973"
42445       ],
42446       [
42447         "Bangladesh (বাংলাদেশ)",
42448         "bd",
42449         "880"
42450       ],
42451       [
42452         "Barbados",
42453         "bb",
42454         "1246"
42455       ],
42456       [
42457         "Belarus (Беларусь)",
42458         "by",
42459         "375"
42460       ],
42461       [
42462         "Belgium (België)",
42463         "be",
42464         "32"
42465       ],
42466       [
42467         "Belize",
42468         "bz",
42469         "501"
42470       ],
42471       [
42472         "Benin (Bénin)",
42473         "bj",
42474         "229"
42475       ],
42476       [
42477         "Bermuda",
42478         "bm",
42479         "1441"
42480       ],
42481       [
42482         "Bhutan (འབྲུག)",
42483         "bt",
42484         "975"
42485       ],
42486       [
42487         "Bolivia",
42488         "bo",
42489         "591"
42490       ],
42491       [
42492         "Bosnia and Herzegovina (Босна и Херцеговина)",
42493         "ba",
42494         "387"
42495       ],
42496       [
42497         "Botswana",
42498         "bw",
42499         "267"
42500       ],
42501       [
42502         "Brazil (Brasil)",
42503         "br",
42504         "55"
42505       ],
42506       [
42507         "British Indian Ocean Territory",
42508         "io",
42509         "246"
42510       ],
42511       [
42512         "British Virgin Islands",
42513         "vg",
42514         "1284"
42515       ],
42516       [
42517         "Brunei",
42518         "bn",
42519         "673"
42520       ],
42521       [
42522         "Bulgaria (България)",
42523         "bg",
42524         "359"
42525       ],
42526       [
42527         "Burkina Faso",
42528         "bf",
42529         "226"
42530       ],
42531       [
42532         "Burundi (Uburundi)",
42533         "bi",
42534         "257"
42535       ],
42536       [
42537         "Cambodia (កម្ពុជា)",
42538         "kh",
42539         "855"
42540       ],
42541       [
42542         "Cameroon (Cameroun)",
42543         "cm",
42544         "237"
42545       ],
42546       [
42547         "Canada",
42548         "ca",
42549         "1",
42550         1,
42551         ["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"]
42552       ],
42553       [
42554         "Cape Verde (Kabu Verdi)",
42555         "cv",
42556         "238"
42557       ],
42558       [
42559         "Caribbean Netherlands",
42560         "bq",
42561         "599",
42562         1
42563       ],
42564       [
42565         "Cayman Islands",
42566         "ky",
42567         "1345"
42568       ],
42569       [
42570         "Central African Republic (République centrafricaine)",
42571         "cf",
42572         "236"
42573       ],
42574       [
42575         "Chad (Tchad)",
42576         "td",
42577         "235"
42578       ],
42579       [
42580         "Chile",
42581         "cl",
42582         "56"
42583       ],
42584       [
42585         "China (中国)",
42586         "cn",
42587         "86"
42588       ],
42589       [
42590         "Christmas Island",
42591         "cx",
42592         "61",
42593         2
42594       ],
42595       [
42596         "Cocos (Keeling) Islands",
42597         "cc",
42598         "61",
42599         1
42600       ],
42601       [
42602         "Colombia",
42603         "co",
42604         "57"
42605       ],
42606       [
42607         "Comoros (‫جزر القمر‬‎)",
42608         "km",
42609         "269"
42610       ],
42611       [
42612         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42613         "cd",
42614         "243"
42615       ],
42616       [
42617         "Congo (Republic) (Congo-Brazzaville)",
42618         "cg",
42619         "242"
42620       ],
42621       [
42622         "Cook Islands",
42623         "ck",
42624         "682"
42625       ],
42626       [
42627         "Costa Rica",
42628         "cr",
42629         "506"
42630       ],
42631       [
42632         "Côte d’Ivoire",
42633         "ci",
42634         "225"
42635       ],
42636       [
42637         "Croatia (Hrvatska)",
42638         "hr",
42639         "385"
42640       ],
42641       [
42642         "Cuba",
42643         "cu",
42644         "53"
42645       ],
42646       [
42647         "Curaçao",
42648         "cw",
42649         "599",
42650         0
42651       ],
42652       [
42653         "Cyprus (Κύπρος)",
42654         "cy",
42655         "357"
42656       ],
42657       [
42658         "Czech Republic (Česká republika)",
42659         "cz",
42660         "420"
42661       ],
42662       [
42663         "Denmark (Danmark)",
42664         "dk",
42665         "45"
42666       ],
42667       [
42668         "Djibouti",
42669         "dj",
42670         "253"
42671       ],
42672       [
42673         "Dominica",
42674         "dm",
42675         "1767"
42676       ],
42677       [
42678         "Dominican Republic (República Dominicana)",
42679         "do",
42680         "1",
42681         2,
42682         ["809", "829", "849"]
42683       ],
42684       [
42685         "Ecuador",
42686         "ec",
42687         "593"
42688       ],
42689       [
42690         "Egypt (‫مصر‬‎)",
42691         "eg",
42692         "20"
42693       ],
42694       [
42695         "El Salvador",
42696         "sv",
42697         "503"
42698       ],
42699       [
42700         "Equatorial Guinea (Guinea Ecuatorial)",
42701         "gq",
42702         "240"
42703       ],
42704       [
42705         "Eritrea",
42706         "er",
42707         "291"
42708       ],
42709       [
42710         "Estonia (Eesti)",
42711         "ee",
42712         "372"
42713       ],
42714       [
42715         "Ethiopia",
42716         "et",
42717         "251"
42718       ],
42719       [
42720         "Falkland Islands (Islas Malvinas)",
42721         "fk",
42722         "500"
42723       ],
42724       [
42725         "Faroe Islands (Føroyar)",
42726         "fo",
42727         "298"
42728       ],
42729       [
42730         "Fiji",
42731         "fj",
42732         "679"
42733       ],
42734       [
42735         "Finland (Suomi)",
42736         "fi",
42737         "358",
42738         0
42739       ],
42740       [
42741         "France",
42742         "fr",
42743         "33"
42744       ],
42745       [
42746         "French Guiana (Guyane française)",
42747         "gf",
42748         "594"
42749       ],
42750       [
42751         "French Polynesia (Polynésie française)",
42752         "pf",
42753         "689"
42754       ],
42755       [
42756         "Gabon",
42757         "ga",
42758         "241"
42759       ],
42760       [
42761         "Gambia",
42762         "gm",
42763         "220"
42764       ],
42765       [
42766         "Georgia (საქართველო)",
42767         "ge",
42768         "995"
42769       ],
42770       [
42771         "Germany (Deutschland)",
42772         "de",
42773         "49"
42774       ],
42775       [
42776         "Ghana (Gaana)",
42777         "gh",
42778         "233"
42779       ],
42780       [
42781         "Gibraltar",
42782         "gi",
42783         "350"
42784       ],
42785       [
42786         "Greece (Ελλάδα)",
42787         "gr",
42788         "30"
42789       ],
42790       [
42791         "Greenland (Kalaallit Nunaat)",
42792         "gl",
42793         "299"
42794       ],
42795       [
42796         "Grenada",
42797         "gd",
42798         "1473"
42799       ],
42800       [
42801         "Guadeloupe",
42802         "gp",
42803         "590",
42804         0
42805       ],
42806       [
42807         "Guam",
42808         "gu",
42809         "1671"
42810       ],
42811       [
42812         "Guatemala",
42813         "gt",
42814         "502"
42815       ],
42816       [
42817         "Guernsey",
42818         "gg",
42819         "44",
42820         1
42821       ],
42822       [
42823         "Guinea (Guinée)",
42824         "gn",
42825         "224"
42826       ],
42827       [
42828         "Guinea-Bissau (Guiné Bissau)",
42829         "gw",
42830         "245"
42831       ],
42832       [
42833         "Guyana",
42834         "gy",
42835         "592"
42836       ],
42837       [
42838         "Haiti",
42839         "ht",
42840         "509"
42841       ],
42842       [
42843         "Honduras",
42844         "hn",
42845         "504"
42846       ],
42847       [
42848         "Hong Kong (香港)",
42849         "hk",
42850         "852"
42851       ],
42852       [
42853         "Hungary (Magyarország)",
42854         "hu",
42855         "36"
42856       ],
42857       [
42858         "Iceland (Ísland)",
42859         "is",
42860         "354"
42861       ],
42862       [
42863         "India (भारत)",
42864         "in",
42865         "91"
42866       ],
42867       [
42868         "Indonesia",
42869         "id",
42870         "62"
42871       ],
42872       [
42873         "Iran (‫ایران‬‎)",
42874         "ir",
42875         "98"
42876       ],
42877       [
42878         "Iraq (‫العراق‬‎)",
42879         "iq",
42880         "964"
42881       ],
42882       [
42883         "Ireland",
42884         "ie",
42885         "353"
42886       ],
42887       [
42888         "Isle of Man",
42889         "im",
42890         "44",
42891         2
42892       ],
42893       [
42894         "Israel (‫ישראל‬‎)",
42895         "il",
42896         "972"
42897       ],
42898       [
42899         "Italy (Italia)",
42900         "it",
42901         "39",
42902         0
42903       ],
42904       [
42905         "Jamaica",
42906         "jm",
42907         "1876"
42908       ],
42909       [
42910         "Japan (日本)",
42911         "jp",
42912         "81"
42913       ],
42914       [
42915         "Jersey",
42916         "je",
42917         "44",
42918         3
42919       ],
42920       [
42921         "Jordan (‫الأردن‬‎)",
42922         "jo",
42923         "962"
42924       ],
42925       [
42926         "Kazakhstan (Казахстан)",
42927         "kz",
42928         "7",
42929         1
42930       ],
42931       [
42932         "Kenya",
42933         "ke",
42934         "254"
42935       ],
42936       [
42937         "Kiribati",
42938         "ki",
42939         "686"
42940       ],
42941       [
42942         "Kosovo",
42943         "xk",
42944         "383"
42945       ],
42946       [
42947         "Kuwait (‫الكويت‬‎)",
42948         "kw",
42949         "965"
42950       ],
42951       [
42952         "Kyrgyzstan (Кыргызстан)",
42953         "kg",
42954         "996"
42955       ],
42956       [
42957         "Laos (ລາວ)",
42958         "la",
42959         "856"
42960       ],
42961       [
42962         "Latvia (Latvija)",
42963         "lv",
42964         "371"
42965       ],
42966       [
42967         "Lebanon (‫لبنان‬‎)",
42968         "lb",
42969         "961"
42970       ],
42971       [
42972         "Lesotho",
42973         "ls",
42974         "266"
42975       ],
42976       [
42977         "Liberia",
42978         "lr",
42979         "231"
42980       ],
42981       [
42982         "Libya (‫ليبيا‬‎)",
42983         "ly",
42984         "218"
42985       ],
42986       [
42987         "Liechtenstein",
42988         "li",
42989         "423"
42990       ],
42991       [
42992         "Lithuania (Lietuva)",
42993         "lt",
42994         "370"
42995       ],
42996       [
42997         "Luxembourg",
42998         "lu",
42999         "352"
43000       ],
43001       [
43002         "Macau (澳門)",
43003         "mo",
43004         "853"
43005       ],
43006       [
43007         "Macedonia (FYROM) (Македонија)",
43008         "mk",
43009         "389"
43010       ],
43011       [
43012         "Madagascar (Madagasikara)",
43013         "mg",
43014         "261"
43015       ],
43016       [
43017         "Malawi",
43018         "mw",
43019         "265"
43020       ],
43021       [
43022         "Malaysia",
43023         "my",
43024         "60"
43025       ],
43026       [
43027         "Maldives",
43028         "mv",
43029         "960"
43030       ],
43031       [
43032         "Mali",
43033         "ml",
43034         "223"
43035       ],
43036       [
43037         "Malta",
43038         "mt",
43039         "356"
43040       ],
43041       [
43042         "Marshall Islands",
43043         "mh",
43044         "692"
43045       ],
43046       [
43047         "Martinique",
43048         "mq",
43049         "596"
43050       ],
43051       [
43052         "Mauritania (‫موريتانيا‬‎)",
43053         "mr",
43054         "222"
43055       ],
43056       [
43057         "Mauritius (Moris)",
43058         "mu",
43059         "230"
43060       ],
43061       [
43062         "Mayotte",
43063         "yt",
43064         "262",
43065         1
43066       ],
43067       [
43068         "Mexico (México)",
43069         "mx",
43070         "52"
43071       ],
43072       [
43073         "Micronesia",
43074         "fm",
43075         "691"
43076       ],
43077       [
43078         "Moldova (Republica Moldova)",
43079         "md",
43080         "373"
43081       ],
43082       [
43083         "Monaco",
43084         "mc",
43085         "377"
43086       ],
43087       [
43088         "Mongolia (Монгол)",
43089         "mn",
43090         "976"
43091       ],
43092       [
43093         "Montenegro (Crna Gora)",
43094         "me",
43095         "382"
43096       ],
43097       [
43098         "Montserrat",
43099         "ms",
43100         "1664"
43101       ],
43102       [
43103         "Morocco (‫المغرب‬‎)",
43104         "ma",
43105         "212",
43106         0
43107       ],
43108       [
43109         "Mozambique (Moçambique)",
43110         "mz",
43111         "258"
43112       ],
43113       [
43114         "Myanmar (Burma) (မြန်မာ)",
43115         "mm",
43116         "95"
43117       ],
43118       [
43119         "Namibia (Namibië)",
43120         "na",
43121         "264"
43122       ],
43123       [
43124         "Nauru",
43125         "nr",
43126         "674"
43127       ],
43128       [
43129         "Nepal (नेपाल)",
43130         "np",
43131         "977"
43132       ],
43133       [
43134         "Netherlands (Nederland)",
43135         "nl",
43136         "31"
43137       ],
43138       [
43139         "New Caledonia (Nouvelle-Calédonie)",
43140         "nc",
43141         "687"
43142       ],
43143       [
43144         "New Zealand",
43145         "nz",
43146         "64"
43147       ],
43148       [
43149         "Nicaragua",
43150         "ni",
43151         "505"
43152       ],
43153       [
43154         "Niger (Nijar)",
43155         "ne",
43156         "227"
43157       ],
43158       [
43159         "Nigeria",
43160         "ng",
43161         "234"
43162       ],
43163       [
43164         "Niue",
43165         "nu",
43166         "683"
43167       ],
43168       [
43169         "Norfolk Island",
43170         "nf",
43171         "672"
43172       ],
43173       [
43174         "North Korea (조선 민주주의 인민 공화국)",
43175         "kp",
43176         "850"
43177       ],
43178       [
43179         "Northern Mariana Islands",
43180         "mp",
43181         "1670"
43182       ],
43183       [
43184         "Norway (Norge)",
43185         "no",
43186         "47",
43187         0
43188       ],
43189       [
43190         "Oman (‫عُمان‬‎)",
43191         "om",
43192         "968"
43193       ],
43194       [
43195         "Pakistan (‫پاکستان‬‎)",
43196         "pk",
43197         "92"
43198       ],
43199       [
43200         "Palau",
43201         "pw",
43202         "680"
43203       ],
43204       [
43205         "Palestine (‫فلسطين‬‎)",
43206         "ps",
43207         "970"
43208       ],
43209       [
43210         "Panama (Panamá)",
43211         "pa",
43212         "507"
43213       ],
43214       [
43215         "Papua New Guinea",
43216         "pg",
43217         "675"
43218       ],
43219       [
43220         "Paraguay",
43221         "py",
43222         "595"
43223       ],
43224       [
43225         "Peru (Perú)",
43226         "pe",
43227         "51"
43228       ],
43229       [
43230         "Philippines",
43231         "ph",
43232         "63"
43233       ],
43234       [
43235         "Poland (Polska)",
43236         "pl",
43237         "48"
43238       ],
43239       [
43240         "Portugal",
43241         "pt",
43242         "351"
43243       ],
43244       [
43245         "Puerto Rico",
43246         "pr",
43247         "1",
43248         3,
43249         ["787", "939"]
43250       ],
43251       [
43252         "Qatar (‫قطر‬‎)",
43253         "qa",
43254         "974"
43255       ],
43256       [
43257         "Réunion (La Réunion)",
43258         "re",
43259         "262",
43260         0
43261       ],
43262       [
43263         "Romania (România)",
43264         "ro",
43265         "40"
43266       ],
43267       [
43268         "Russia (Россия)",
43269         "ru",
43270         "7",
43271         0
43272       ],
43273       [
43274         "Rwanda",
43275         "rw",
43276         "250"
43277       ],
43278       [
43279         "Saint Barthélemy",
43280         "bl",
43281         "590",
43282         1
43283       ],
43284       [
43285         "Saint Helena",
43286         "sh",
43287         "290"
43288       ],
43289       [
43290         "Saint Kitts and Nevis",
43291         "kn",
43292         "1869"
43293       ],
43294       [
43295         "Saint Lucia",
43296         "lc",
43297         "1758"
43298       ],
43299       [
43300         "Saint Martin (Saint-Martin (partie française))",
43301         "mf",
43302         "590",
43303         2
43304       ],
43305       [
43306         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43307         "pm",
43308         "508"
43309       ],
43310       [
43311         "Saint Vincent and the Grenadines",
43312         "vc",
43313         "1784"
43314       ],
43315       [
43316         "Samoa",
43317         "ws",
43318         "685"
43319       ],
43320       [
43321         "San Marino",
43322         "sm",
43323         "378"
43324       ],
43325       [
43326         "São Tomé and Príncipe (São Tomé e Príncipe)",
43327         "st",
43328         "239"
43329       ],
43330       [
43331         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43332         "sa",
43333         "966"
43334       ],
43335       [
43336         "Senegal (Sénégal)",
43337         "sn",
43338         "221"
43339       ],
43340       [
43341         "Serbia (Србија)",
43342         "rs",
43343         "381"
43344       ],
43345       [
43346         "Seychelles",
43347         "sc",
43348         "248"
43349       ],
43350       [
43351         "Sierra Leone",
43352         "sl",
43353         "232"
43354       ],
43355       [
43356         "Singapore",
43357         "sg",
43358         "65"
43359       ],
43360       [
43361         "Sint Maarten",
43362         "sx",
43363         "1721"
43364       ],
43365       [
43366         "Slovakia (Slovensko)",
43367         "sk",
43368         "421"
43369       ],
43370       [
43371         "Slovenia (Slovenija)",
43372         "si",
43373         "386"
43374       ],
43375       [
43376         "Solomon Islands",
43377         "sb",
43378         "677"
43379       ],
43380       [
43381         "Somalia (Soomaaliya)",
43382         "so",
43383         "252"
43384       ],
43385       [
43386         "South Africa",
43387         "za",
43388         "27"
43389       ],
43390       [
43391         "South Korea (대한민국)",
43392         "kr",
43393         "82"
43394       ],
43395       [
43396         "South Sudan (‫جنوب السودان‬‎)",
43397         "ss",
43398         "211"
43399       ],
43400       [
43401         "Spain (España)",
43402         "es",
43403         "34"
43404       ],
43405       [
43406         "Sri Lanka (ශ්‍රී ලංකාව)",
43407         "lk",
43408         "94"
43409       ],
43410       [
43411         "Sudan (‫السودان‬‎)",
43412         "sd",
43413         "249"
43414       ],
43415       [
43416         "Suriname",
43417         "sr",
43418         "597"
43419       ],
43420       [
43421         "Svalbard and Jan Mayen",
43422         "sj",
43423         "47",
43424         1
43425       ],
43426       [
43427         "Swaziland",
43428         "sz",
43429         "268"
43430       ],
43431       [
43432         "Sweden (Sverige)",
43433         "se",
43434         "46"
43435       ],
43436       [
43437         "Switzerland (Schweiz)",
43438         "ch",
43439         "41"
43440       ],
43441       [
43442         "Syria (‫سوريا‬‎)",
43443         "sy",
43444         "963"
43445       ],
43446       [
43447         "Taiwan (台灣)",
43448         "tw",
43449         "886"
43450       ],
43451       [
43452         "Tajikistan",
43453         "tj",
43454         "992"
43455       ],
43456       [
43457         "Tanzania",
43458         "tz",
43459         "255"
43460       ],
43461       [
43462         "Thailand (ไทย)",
43463         "th",
43464         "66"
43465       ],
43466       [
43467         "Timor-Leste",
43468         "tl",
43469         "670"
43470       ],
43471       [
43472         "Togo",
43473         "tg",
43474         "228"
43475       ],
43476       [
43477         "Tokelau",
43478         "tk",
43479         "690"
43480       ],
43481       [
43482         "Tonga",
43483         "to",
43484         "676"
43485       ],
43486       [
43487         "Trinidad and Tobago",
43488         "tt",
43489         "1868"
43490       ],
43491       [
43492         "Tunisia (‫تونس‬‎)",
43493         "tn",
43494         "216"
43495       ],
43496       [
43497         "Turkey (Türkiye)",
43498         "tr",
43499         "90"
43500       ],
43501       [
43502         "Turkmenistan",
43503         "tm",
43504         "993"
43505       ],
43506       [
43507         "Turks and Caicos Islands",
43508         "tc",
43509         "1649"
43510       ],
43511       [
43512         "Tuvalu",
43513         "tv",
43514         "688"
43515       ],
43516       [
43517         "U.S. Virgin Islands",
43518         "vi",
43519         "1340"
43520       ],
43521       [
43522         "Uganda",
43523         "ug",
43524         "256"
43525       ],
43526       [
43527         "Ukraine (Україна)",
43528         "ua",
43529         "380"
43530       ],
43531       [
43532         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43533         "ae",
43534         "971"
43535       ],
43536       [
43537         "United Kingdom",
43538         "gb",
43539         "44",
43540         0
43541       ],
43542       [
43543         "United States",
43544         "us",
43545         "1",
43546         0
43547       ],
43548       [
43549         "Uruguay",
43550         "uy",
43551         "598"
43552       ],
43553       [
43554         "Uzbekistan (Oʻzbekiston)",
43555         "uz",
43556         "998"
43557       ],
43558       [
43559         "Vanuatu",
43560         "vu",
43561         "678"
43562       ],
43563       [
43564         "Vatican City (Città del Vaticano)",
43565         "va",
43566         "39",
43567         1
43568       ],
43569       [
43570         "Venezuela",
43571         "ve",
43572         "58"
43573       ],
43574       [
43575         "Vietnam (Việt Nam)",
43576         "vn",
43577         "84"
43578       ],
43579       [
43580         "Wallis and Futuna (Wallis-et-Futuna)",
43581         "wf",
43582         "681"
43583       ],
43584       [
43585         "Western Sahara (‫الصحراء الغربية‬‎)",
43586         "eh",
43587         "212",
43588         1
43589       ],
43590       [
43591         "Yemen (‫اليمن‬‎)",
43592         "ye",
43593         "967"
43594       ],
43595       [
43596         "Zambia",
43597         "zm",
43598         "260"
43599       ],
43600       [
43601         "Zimbabwe",
43602         "zw",
43603         "263"
43604       ],
43605       [
43606         "Åland Islands",
43607         "ax",
43608         "358",
43609         1
43610       ]
43611   ];
43612   
43613   return d;
43614 }/**
43615 *    This script refer to:
43616 *    Title: International Telephone Input
43617 *    Author: Jack O'Connor
43618 *    Code version:  v12.1.12
43619 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43620 **/
43621
43622 /**
43623  * @class Roo.bootstrap.PhoneInput
43624  * @extends Roo.bootstrap.TriggerField
43625  * An input with International dial-code selection
43626  
43627  * @cfg {String} defaultDialCode default '+852'
43628  * @cfg {Array} preferedCountries default []
43629   
43630  * @constructor
43631  * Create a new PhoneInput.
43632  * @param {Object} config Configuration options
43633  */
43634
43635 Roo.bootstrap.PhoneInput = function(config) {
43636     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43637 };
43638
43639 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43640         /**
43641         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43642         */
43643         listWidth: undefined,
43644         
43645         selectedClass: 'active',
43646         
43647         invalidClass : "has-warning",
43648         
43649         validClass: 'has-success',
43650         
43651         allowed: '0123456789',
43652         
43653         max_length: 15,
43654         
43655         /**
43656          * @cfg {String} defaultDialCode The default dial code when initializing the input
43657          */
43658         defaultDialCode: '+852',
43659         
43660         /**
43661          * @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
43662          */
43663         preferedCountries: false,
43664         
43665         getAutoCreate : function()
43666         {
43667             var data = Roo.bootstrap.PhoneInputData();
43668             var align = this.labelAlign || this.parentLabelAlign();
43669             var id = Roo.id();
43670             
43671             this.allCountries = [];
43672             this.dialCodeMapping = [];
43673             
43674             for (var i = 0; i < data.length; i++) {
43675               var c = data[i];
43676               this.allCountries[i] = {
43677                 name: c[0],
43678                 iso2: c[1],
43679                 dialCode: c[2],
43680                 priority: c[3] || 0,
43681                 areaCodes: c[4] || null
43682               };
43683               this.dialCodeMapping[c[2]] = {
43684                   name: c[0],
43685                   iso2: c[1],
43686                   priority: c[3] || 0,
43687                   areaCodes: c[4] || null
43688               };
43689             }
43690             
43691             var cfg = {
43692                 cls: 'form-group',
43693                 cn: []
43694             };
43695             
43696             var input =  {
43697                 tag: 'input',
43698                 id : id,
43699                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43700                 maxlength: this.max_length,
43701                 cls : 'form-control tel-input',
43702                 autocomplete: 'new-password'
43703             };
43704             
43705             var hiddenInput = {
43706                 tag: 'input',
43707                 type: 'hidden',
43708                 cls: 'hidden-tel-input'
43709             };
43710             
43711             if (this.name) {
43712                 hiddenInput.name = this.name;
43713             }
43714             
43715             if (this.disabled) {
43716                 input.disabled = true;
43717             }
43718             
43719             var flag_container = {
43720                 tag: 'div',
43721                 cls: 'flag-box',
43722                 cn: [
43723                     {
43724                         tag: 'div',
43725                         cls: 'flag'
43726                     },
43727                     {
43728                         tag: 'div',
43729                         cls: 'caret'
43730                     }
43731                 ]
43732             };
43733             
43734             var box = {
43735                 tag: 'div',
43736                 cls: this.hasFeedback ? 'has-feedback' : '',
43737                 cn: [
43738                     hiddenInput,
43739                     input,
43740                     {
43741                         tag: 'input',
43742                         cls: 'dial-code-holder',
43743                         disabled: true
43744                     }
43745                 ]
43746             };
43747             
43748             var container = {
43749                 cls: 'roo-select2-container input-group',
43750                 cn: [
43751                     flag_container,
43752                     box
43753                 ]
43754             };
43755             
43756             if (this.fieldLabel.length) {
43757                 var indicator = {
43758                     tag: 'i',
43759                     tooltip: 'This field is required'
43760                 };
43761                 
43762                 var label = {
43763                     tag: 'label',
43764                     'for':  id,
43765                     cls: 'control-label',
43766                     cn: []
43767                 };
43768                 
43769                 var label_text = {
43770                     tag: 'span',
43771                     html: this.fieldLabel
43772                 };
43773                 
43774                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43775                 label.cn = [
43776                     indicator,
43777                     label_text
43778                 ];
43779                 
43780                 if(this.indicatorpos == 'right') {
43781                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43782                     label.cn = [
43783                         label_text,
43784                         indicator
43785                     ];
43786                 }
43787                 
43788                 if(align == 'left') {
43789                     container = {
43790                         tag: 'div',
43791                         cn: [
43792                             container
43793                         ]
43794                     };
43795                     
43796                     if(this.labelWidth > 12){
43797                         label.style = "width: " + this.labelWidth + 'px';
43798                     }
43799                     if(this.labelWidth < 13 && this.labelmd == 0){
43800                         this.labelmd = this.labelWidth;
43801                     }
43802                     if(this.labellg > 0){
43803                         label.cls += ' col-lg-' + this.labellg;
43804                         input.cls += ' col-lg-' + (12 - this.labellg);
43805                     }
43806                     if(this.labelmd > 0){
43807                         label.cls += ' col-md-' + this.labelmd;
43808                         container.cls += ' col-md-' + (12 - this.labelmd);
43809                     }
43810                     if(this.labelsm > 0){
43811                         label.cls += ' col-sm-' + this.labelsm;
43812                         container.cls += ' col-sm-' + (12 - this.labelsm);
43813                     }
43814                     if(this.labelxs > 0){
43815                         label.cls += ' col-xs-' + this.labelxs;
43816                         container.cls += ' col-xs-' + (12 - this.labelxs);
43817                     }
43818                 }
43819             }
43820             
43821             cfg.cn = [
43822                 label,
43823                 container
43824             ];
43825             
43826             var settings = this;
43827             
43828             ['xs','sm','md','lg'].map(function(size){
43829                 if (settings[size]) {
43830                     cfg.cls += ' col-' + size + '-' + settings[size];
43831                 }
43832             });
43833             
43834             this.store = new Roo.data.Store({
43835                 proxy : new Roo.data.MemoryProxy({}),
43836                 reader : new Roo.data.JsonReader({
43837                     fields : [
43838                         {
43839                             'name' : 'name',
43840                             'type' : 'string'
43841                         },
43842                         {
43843                             'name' : 'iso2',
43844                             'type' : 'string'
43845                         },
43846                         {
43847                             'name' : 'dialCode',
43848                             'type' : 'string'
43849                         },
43850                         {
43851                             'name' : 'priority',
43852                             'type' : 'string'
43853                         },
43854                         {
43855                             'name' : 'areaCodes',
43856                             'type' : 'string'
43857                         }
43858                     ]
43859                 })
43860             });
43861             
43862             if(!this.preferedCountries) {
43863                 this.preferedCountries = [
43864                     'hk',
43865                     'gb',
43866                     'us'
43867                 ];
43868             }
43869             
43870             var p = this.preferedCountries.reverse();
43871             
43872             if(p) {
43873                 for (var i = 0; i < p.length; i++) {
43874                     for (var j = 0; j < this.allCountries.length; j++) {
43875                         if(this.allCountries[j].iso2 == p[i]) {
43876                             var t = this.allCountries[j];
43877                             this.allCountries.splice(j,1);
43878                             this.allCountries.unshift(t);
43879                         }
43880                     } 
43881                 }
43882             }
43883             
43884             this.store.proxy.data = {
43885                 success: true,
43886                 data: this.allCountries
43887             };
43888             
43889             return cfg;
43890         },
43891         
43892         initEvents : function()
43893         {
43894             this.createList();
43895             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43896             
43897             this.indicator = this.indicatorEl();
43898             this.flag = this.flagEl();
43899             this.dialCodeHolder = this.dialCodeHolderEl();
43900             
43901             this.trigger = this.el.select('div.flag-box',true).first();
43902             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43903             
43904             var _this = this;
43905             
43906             (function(){
43907                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43908                 _this.list.setWidth(lw);
43909             }).defer(100);
43910             
43911             this.list.on('mouseover', this.onViewOver, this);
43912             this.list.on('mousemove', this.onViewMove, this);
43913             this.inputEl().on("keyup", this.onKeyUp, this);
43914             this.inputEl().on("keypress", this.onKeyPress, this);
43915             
43916             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43917
43918             this.view = new Roo.View(this.list, this.tpl, {
43919                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43920             });
43921             
43922             this.view.on('click', this.onViewClick, this);
43923             this.setValue(this.defaultDialCode);
43924         },
43925         
43926         onTriggerClick : function(e)
43927         {
43928             Roo.log('trigger click');
43929             if(this.disabled){
43930                 return;
43931             }
43932             
43933             if(this.isExpanded()){
43934                 this.collapse();
43935                 this.hasFocus = false;
43936             }else {
43937                 this.store.load({});
43938                 this.hasFocus = true;
43939                 this.expand();
43940             }
43941         },
43942         
43943         isExpanded : function()
43944         {
43945             return this.list.isVisible();
43946         },
43947         
43948         collapse : function()
43949         {
43950             if(!this.isExpanded()){
43951                 return;
43952             }
43953             this.list.hide();
43954             Roo.get(document).un('mousedown', this.collapseIf, this);
43955             Roo.get(document).un('mousewheel', this.collapseIf, this);
43956             this.fireEvent('collapse', this);
43957             this.validate();
43958         },
43959         
43960         expand : function()
43961         {
43962             Roo.log('expand');
43963
43964             if(this.isExpanded() || !this.hasFocus){
43965                 return;
43966             }
43967             
43968             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43969             this.list.setWidth(lw);
43970             
43971             this.list.show();
43972             this.restrictHeight();
43973             
43974             Roo.get(document).on('mousedown', this.collapseIf, this);
43975             Roo.get(document).on('mousewheel', this.collapseIf, this);
43976             
43977             this.fireEvent('expand', this);
43978         },
43979         
43980         restrictHeight : function()
43981         {
43982             this.list.alignTo(this.inputEl(), this.listAlign);
43983             this.list.alignTo(this.inputEl(), this.listAlign);
43984         },
43985         
43986         onViewOver : function(e, t)
43987         {
43988             if(this.inKeyMode){
43989                 return;
43990             }
43991             var item = this.view.findItemFromChild(t);
43992             
43993             if(item){
43994                 var index = this.view.indexOf(item);
43995                 this.select(index, false);
43996             }
43997         },
43998
43999         // private
44000         onViewClick : function(view, doFocus, el, e)
44001         {
44002             var index = this.view.getSelectedIndexes()[0];
44003             
44004             var r = this.store.getAt(index);
44005             
44006             if(r){
44007                 this.onSelect(r, index);
44008             }
44009             if(doFocus !== false && !this.blockFocus){
44010                 this.inputEl().focus();
44011             }
44012         },
44013         
44014         onViewMove : function(e, t)
44015         {
44016             this.inKeyMode = false;
44017         },
44018         
44019         select : function(index, scrollIntoView)
44020         {
44021             this.selectedIndex = index;
44022             this.view.select(index);
44023             if(scrollIntoView !== false){
44024                 var el = this.view.getNode(index);
44025                 if(el){
44026                     this.list.scrollChildIntoView(el, false);
44027                 }
44028             }
44029         },
44030         
44031         createList : function()
44032         {
44033             this.list = Roo.get(document.body).createChild({
44034                 tag: 'ul',
44035                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44036                 style: 'display:none'
44037             });
44038             
44039             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44040         },
44041         
44042         collapseIf : function(e)
44043         {
44044             var in_combo  = e.within(this.el);
44045             var in_list =  e.within(this.list);
44046             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44047             
44048             if (in_combo || in_list || is_list) {
44049                 return;
44050             }
44051             this.collapse();
44052         },
44053         
44054         onSelect : function(record, index)
44055         {
44056             if(this.fireEvent('beforeselect', this, record, index) !== false){
44057                 
44058                 this.setFlagClass(record.data.iso2);
44059                 this.setDialCode(record.data.dialCode);
44060                 this.hasFocus = false;
44061                 this.collapse();
44062                 this.fireEvent('select', this, record, index);
44063             }
44064         },
44065         
44066         flagEl : function()
44067         {
44068             var flag = this.el.select('div.flag',true).first();
44069             if(!flag){
44070                 return false;
44071             }
44072             return flag;
44073         },
44074         
44075         dialCodeHolderEl : function()
44076         {
44077             var d = this.el.select('input.dial-code-holder',true).first();
44078             if(!d){
44079                 return false;
44080             }
44081             return d;
44082         },
44083         
44084         setDialCode : function(v)
44085         {
44086             this.dialCodeHolder.dom.value = '+'+v;
44087         },
44088         
44089         setFlagClass : function(n)
44090         {
44091             this.flag.dom.className = 'flag '+n;
44092         },
44093         
44094         getValue : function()
44095         {
44096             var v = this.inputEl().getValue();
44097             if(this.dialCodeHolder) {
44098                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44099             }
44100             return v;
44101         },
44102         
44103         setValue : function(v)
44104         {
44105             var d = this.getDialCode(v);
44106             
44107             //invalid dial code
44108             if(v.length == 0 || !d || d.length == 0) {
44109                 if(this.rendered){
44110                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44111                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44112                 }
44113                 return;
44114             }
44115             
44116             //valid dial code
44117             this.setFlagClass(this.dialCodeMapping[d].iso2);
44118             this.setDialCode(d);
44119             this.inputEl().dom.value = v.replace('+'+d,'');
44120             this.hiddenEl().dom.value = this.getValue();
44121             
44122             this.validate();
44123         },
44124         
44125         getDialCode : function(v)
44126         {
44127             v = v ||  '';
44128             
44129             if (v.length == 0) {
44130                 return this.dialCodeHolder.dom.value;
44131             }
44132             
44133             var dialCode = "";
44134             if (v.charAt(0) != "+") {
44135                 return false;
44136             }
44137             var numericChars = "";
44138             for (var i = 1; i < v.length; i++) {
44139               var c = v.charAt(i);
44140               if (!isNaN(c)) {
44141                 numericChars += c;
44142                 if (this.dialCodeMapping[numericChars]) {
44143                   dialCode = v.substr(1, i);
44144                 }
44145                 if (numericChars.length == 4) {
44146                   break;
44147                 }
44148               }
44149             }
44150             return dialCode;
44151         },
44152         
44153         reset : function()
44154         {
44155             this.setValue(this.defaultDialCode);
44156             this.validate();
44157         },
44158         
44159         hiddenEl : function()
44160         {
44161             return this.el.select('input.hidden-tel-input',true).first();
44162         },
44163         
44164         // after setting val
44165         onKeyUp : function(e){
44166             this.setValue(this.getValue());
44167         },
44168         
44169         onKeyPress : function(e){
44170             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44171                 e.stopEvent();
44172             }
44173         }
44174         
44175 });
44176 /**
44177  * @class Roo.bootstrap.MoneyField
44178  * @extends Roo.bootstrap.ComboBox
44179  * Bootstrap MoneyField class
44180  * 
44181  * @constructor
44182  * Create a new MoneyField.
44183  * @param {Object} config Configuration options
44184  */
44185
44186 Roo.bootstrap.MoneyField = function(config) {
44187     
44188     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44189     
44190 };
44191
44192 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44193     
44194     /**
44195      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44196      */
44197     allowDecimals : true,
44198     /**
44199      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44200      */
44201     decimalSeparator : ".",
44202     /**
44203      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44204      */
44205     decimalPrecision : 0,
44206     /**
44207      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44208      */
44209     allowNegative : true,
44210     /**
44211      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44212      */
44213     allowZero: true,
44214     /**
44215      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44216      */
44217     minValue : Number.NEGATIVE_INFINITY,
44218     /**
44219      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44220      */
44221     maxValue : Number.MAX_VALUE,
44222     /**
44223      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44224      */
44225     minText : "The minimum value for this field is {0}",
44226     /**
44227      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44228      */
44229     maxText : "The maximum value for this field is {0}",
44230     /**
44231      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44232      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44233      */
44234     nanText : "{0} is not a valid number",
44235     /**
44236      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44237      */
44238     castInt : true,
44239     /**
44240      * @cfg {String} defaults currency of the MoneyField
44241      * value should be in lkey
44242      */
44243     defaultCurrency : false,
44244     /**
44245      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44246      */
44247     thousandsDelimiter : false,
44248     /**
44249      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44250      */
44251     max_length: false,
44252     
44253     inputlg : 9,
44254     inputmd : 9,
44255     inputsm : 9,
44256     inputxs : 6,
44257     
44258     store : false,
44259     
44260     getAutoCreate : function()
44261     {
44262         var align = this.labelAlign || this.parentLabelAlign();
44263         
44264         var id = Roo.id();
44265
44266         var cfg = {
44267             cls: 'form-group',
44268             cn: []
44269         };
44270
44271         var input =  {
44272             tag: 'input',
44273             id : id,
44274             cls : 'form-control roo-money-amount-input',
44275             autocomplete: 'new-password'
44276         };
44277         
44278         var hiddenInput = {
44279             tag: 'input',
44280             type: 'hidden',
44281             id: Roo.id(),
44282             cls: 'hidden-number-input'
44283         };
44284         
44285         if(this.max_length) {
44286             input.maxlength = this.max_length; 
44287         }
44288         
44289         if (this.name) {
44290             hiddenInput.name = this.name;
44291         }
44292
44293         if (this.disabled) {
44294             input.disabled = true;
44295         }
44296
44297         var clg = 12 - this.inputlg;
44298         var cmd = 12 - this.inputmd;
44299         var csm = 12 - this.inputsm;
44300         var cxs = 12 - this.inputxs;
44301         
44302         var container = {
44303             tag : 'div',
44304             cls : 'row roo-money-field',
44305             cn : [
44306                 {
44307                     tag : 'div',
44308                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44309                     cn : [
44310                         {
44311                             tag : 'div',
44312                             cls: 'roo-select2-container input-group',
44313                             cn: [
44314                                 {
44315                                     tag : 'input',
44316                                     cls : 'form-control roo-money-currency-input',
44317                                     autocomplete: 'new-password',
44318                                     readOnly : 1,
44319                                     name : this.currencyName
44320                                 },
44321                                 {
44322                                     tag :'span',
44323                                     cls : 'input-group-addon',
44324                                     cn : [
44325                                         {
44326                                             tag: 'span',
44327                                             cls: 'caret'
44328                                         }
44329                                     ]
44330                                 }
44331                             ]
44332                         }
44333                     ]
44334                 },
44335                 {
44336                     tag : 'div',
44337                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44338                     cn : [
44339                         {
44340                             tag: 'div',
44341                             cls: this.hasFeedback ? 'has-feedback' : '',
44342                             cn: [
44343                                 input
44344                             ]
44345                         }
44346                     ]
44347                 }
44348             ]
44349             
44350         };
44351         
44352         if (this.fieldLabel.length) {
44353             var indicator = {
44354                 tag: 'i',
44355                 tooltip: 'This field is required'
44356             };
44357
44358             var label = {
44359                 tag: 'label',
44360                 'for':  id,
44361                 cls: 'control-label',
44362                 cn: []
44363             };
44364
44365             var label_text = {
44366                 tag: 'span',
44367                 html: this.fieldLabel
44368             };
44369
44370             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44371             label.cn = [
44372                 indicator,
44373                 label_text
44374             ];
44375
44376             if(this.indicatorpos == 'right') {
44377                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44378                 label.cn = [
44379                     label_text,
44380                     indicator
44381                 ];
44382             }
44383
44384             if(align == 'left') {
44385                 container = {
44386                     tag: 'div',
44387                     cn: [
44388                         container
44389                     ]
44390                 };
44391
44392                 if(this.labelWidth > 12){
44393                     label.style = "width: " + this.labelWidth + 'px';
44394                 }
44395                 if(this.labelWidth < 13 && this.labelmd == 0){
44396                     this.labelmd = this.labelWidth;
44397                 }
44398                 if(this.labellg > 0){
44399                     label.cls += ' col-lg-' + this.labellg;
44400                     input.cls += ' col-lg-' + (12 - this.labellg);
44401                 }
44402                 if(this.labelmd > 0){
44403                     label.cls += ' col-md-' + this.labelmd;
44404                     container.cls += ' col-md-' + (12 - this.labelmd);
44405                 }
44406                 if(this.labelsm > 0){
44407                     label.cls += ' col-sm-' + this.labelsm;
44408                     container.cls += ' col-sm-' + (12 - this.labelsm);
44409                 }
44410                 if(this.labelxs > 0){
44411                     label.cls += ' col-xs-' + this.labelxs;
44412                     container.cls += ' col-xs-' + (12 - this.labelxs);
44413                 }
44414             }
44415         }
44416
44417         cfg.cn = [
44418             label,
44419             container,
44420             hiddenInput
44421         ];
44422         
44423         var settings = this;
44424
44425         ['xs','sm','md','lg'].map(function(size){
44426             if (settings[size]) {
44427                 cfg.cls += ' col-' + size + '-' + settings[size];
44428             }
44429         });
44430         
44431         return cfg;
44432     },
44433     
44434     initEvents : function()
44435     {
44436         this.indicator = this.indicatorEl();
44437         
44438         this.initCurrencyEvent();
44439         
44440         this.initNumberEvent();
44441     },
44442     
44443     initCurrencyEvent : function()
44444     {
44445         if (!this.store) {
44446             throw "can not find store for combo";
44447         }
44448         
44449         this.store = Roo.factory(this.store, Roo.data);
44450         this.store.parent = this;
44451         
44452         this.createList();
44453         
44454         this.triggerEl = this.el.select('.input-group-addon', true).first();
44455         
44456         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44457         
44458         var _this = this;
44459         
44460         (function(){
44461             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44462             _this.list.setWidth(lw);
44463         }).defer(100);
44464         
44465         this.list.on('mouseover', this.onViewOver, this);
44466         this.list.on('mousemove', this.onViewMove, this);
44467         this.list.on('scroll', this.onViewScroll, this);
44468         
44469         if(!this.tpl){
44470             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44471         }
44472         
44473         this.view = new Roo.View(this.list, this.tpl, {
44474             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44475         });
44476         
44477         this.view.on('click', this.onViewClick, this);
44478         
44479         this.store.on('beforeload', this.onBeforeLoad, this);
44480         this.store.on('load', this.onLoad, this);
44481         this.store.on('loadexception', this.onLoadException, this);
44482         
44483         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44484             "up" : function(e){
44485                 this.inKeyMode = true;
44486                 this.selectPrev();
44487             },
44488
44489             "down" : function(e){
44490                 if(!this.isExpanded()){
44491                     this.onTriggerClick();
44492                 }else{
44493                     this.inKeyMode = true;
44494                     this.selectNext();
44495                 }
44496             },
44497
44498             "enter" : function(e){
44499                 this.collapse();
44500                 
44501                 if(this.fireEvent("specialkey", this, e)){
44502                     this.onViewClick(false);
44503                 }
44504                 
44505                 return true;
44506             },
44507
44508             "esc" : function(e){
44509                 this.collapse();
44510             },
44511
44512             "tab" : function(e){
44513                 this.collapse();
44514                 
44515                 if(this.fireEvent("specialkey", this, e)){
44516                     this.onViewClick(false);
44517                 }
44518                 
44519                 return true;
44520             },
44521
44522             scope : this,
44523
44524             doRelay : function(foo, bar, hname){
44525                 if(hname == 'down' || this.scope.isExpanded()){
44526                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44527                 }
44528                 return true;
44529             },
44530
44531             forceKeyDown: true
44532         });
44533         
44534         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44535         
44536     },
44537     
44538     initNumberEvent : function(e)
44539     {
44540         this.inputEl().on("keydown" , this.fireKey,  this);
44541         this.inputEl().on("focus", this.onFocus,  this);
44542         this.inputEl().on("blur", this.onBlur,  this);
44543         
44544         this.inputEl().relayEvent('keyup', this);
44545         
44546         if(this.indicator){
44547             this.indicator.addClass('invisible');
44548         }
44549  
44550         this.originalValue = this.getValue();
44551         
44552         if(this.validationEvent == 'keyup'){
44553             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44554             this.inputEl().on('keyup', this.filterValidation, this);
44555         }
44556         else if(this.validationEvent !== false){
44557             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44558         }
44559         
44560         if(this.selectOnFocus){
44561             this.on("focus", this.preFocus, this);
44562             
44563         }
44564         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44565             this.inputEl().on("keypress", this.filterKeys, this);
44566         } else {
44567             this.inputEl().relayEvent('keypress', this);
44568         }
44569         
44570         var allowed = "0123456789";
44571         
44572         if(this.allowDecimals){
44573             allowed += this.decimalSeparator;
44574         }
44575         
44576         if(this.allowNegative){
44577             allowed += "-";
44578         }
44579         
44580         if(this.thousandsDelimiter) {
44581             allowed += ",";
44582         }
44583         
44584         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44585         
44586         var keyPress = function(e){
44587             
44588             var k = e.getKey();
44589             
44590             var c = e.getCharCode();
44591             
44592             if(
44593                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44594                     allowed.indexOf(String.fromCharCode(c)) === -1
44595             ){
44596                 e.stopEvent();
44597                 return;
44598             }
44599             
44600             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44601                 return;
44602             }
44603             
44604             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44605                 e.stopEvent();
44606             }
44607         };
44608         
44609         this.inputEl().on("keypress", keyPress, this);
44610         
44611     },
44612     
44613     onTriggerClick : function(e)
44614     {   
44615         if(this.disabled){
44616             return;
44617         }
44618         
44619         this.page = 0;
44620         this.loadNext = false;
44621         
44622         if(this.isExpanded()){
44623             this.collapse();
44624             return;
44625         }
44626         
44627         this.hasFocus = true;
44628         
44629         if(this.triggerAction == 'all') {
44630             this.doQuery(this.allQuery, true);
44631             return;
44632         }
44633         
44634         this.doQuery(this.getRawValue());
44635     },
44636     
44637     getCurrency : function()
44638     {   
44639         var v = this.currencyEl().getValue();
44640         
44641         return v;
44642     },
44643     
44644     restrictHeight : function()
44645     {
44646         this.list.alignTo(this.currencyEl(), this.listAlign);
44647         this.list.alignTo(this.currencyEl(), this.listAlign);
44648     },
44649     
44650     onViewClick : function(view, doFocus, el, e)
44651     {
44652         var index = this.view.getSelectedIndexes()[0];
44653         
44654         var r = this.store.getAt(index);
44655         
44656         if(r){
44657             this.onSelect(r, index);
44658         }
44659     },
44660     
44661     onSelect : function(record, index){
44662         
44663         if(this.fireEvent('beforeselect', this, record, index) !== false){
44664         
44665             this.setFromCurrencyData(index > -1 ? record.data : false);
44666             
44667             this.collapse();
44668             
44669             this.fireEvent('select', this, record, index);
44670         }
44671     },
44672     
44673     setFromCurrencyData : function(o)
44674     {
44675         var currency = '';
44676         
44677         this.lastCurrency = o;
44678         
44679         if (this.currencyField) {
44680             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44681         } else {
44682             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44683         }
44684         
44685         this.lastSelectionText = currency;
44686         
44687         //setting default currency
44688         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44689             this.setCurrency(this.defaultCurrency);
44690             return;
44691         }
44692         
44693         this.setCurrency(currency);
44694     },
44695     
44696     setFromData : function(o)
44697     {
44698         var c = {};
44699         
44700         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44701         
44702         this.setFromCurrencyData(c);
44703         
44704         var value = '';
44705         
44706         if (this.name) {
44707             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44708         } else {
44709             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44710         }
44711         
44712         this.setValue(value);
44713         
44714     },
44715     
44716     setCurrency : function(v)
44717     {   
44718         this.currencyValue = v;
44719         
44720         if(this.rendered){
44721             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44722             this.validate();
44723         }
44724     },
44725     
44726     setValue : function(v)
44727     {
44728         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44729         
44730         this.value = v;
44731         
44732         if(this.rendered){
44733             
44734             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44735             
44736             this.inputEl().dom.value = (v == '') ? '' :
44737                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44738             
44739             if(!this.allowZero && v === '0') {
44740                 this.hiddenEl().dom.value = '';
44741                 this.inputEl().dom.value = '';
44742             }
44743             
44744             this.validate();
44745         }
44746     },
44747     
44748     getRawValue : function()
44749     {
44750         var v = this.inputEl().getValue();
44751         
44752         return v;
44753     },
44754     
44755     getValue : function()
44756     {
44757         return this.fixPrecision(this.parseValue(this.getRawValue()));
44758     },
44759     
44760     parseValue : function(value)
44761     {
44762         if(this.thousandsDelimiter) {
44763             value += "";
44764             r = new RegExp(",", "g");
44765             value = value.replace(r, "");
44766         }
44767         
44768         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44769         return isNaN(value) ? '' : value;
44770         
44771     },
44772     
44773     fixPrecision : function(value)
44774     {
44775         if(this.thousandsDelimiter) {
44776             value += "";
44777             r = new RegExp(",", "g");
44778             value = value.replace(r, "");
44779         }
44780         
44781         var nan = isNaN(value);
44782         
44783         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44784             return nan ? '' : value;
44785         }
44786         return parseFloat(value).toFixed(this.decimalPrecision);
44787     },
44788     
44789     decimalPrecisionFcn : function(v)
44790     {
44791         return Math.floor(v);
44792     },
44793     
44794     validateValue : function(value)
44795     {
44796         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44797             return false;
44798         }
44799         
44800         var num = this.parseValue(value);
44801         
44802         if(isNaN(num)){
44803             this.markInvalid(String.format(this.nanText, value));
44804             return false;
44805         }
44806         
44807         if(num < this.minValue){
44808             this.markInvalid(String.format(this.minText, this.minValue));
44809             return false;
44810         }
44811         
44812         if(num > this.maxValue){
44813             this.markInvalid(String.format(this.maxText, this.maxValue));
44814             return false;
44815         }
44816         
44817         return true;
44818     },
44819     
44820     validate : function()
44821     {
44822         if(this.disabled || this.allowBlank){
44823             this.markValid();
44824             return true;
44825         }
44826         
44827         var currency = this.getCurrency();
44828         
44829         if(this.validateValue(this.getRawValue()) && currency.length){
44830             this.markValid();
44831             return true;
44832         }
44833         
44834         this.markInvalid();
44835         return false;
44836     },
44837     
44838     getName: function()
44839     {
44840         return this.name;
44841     },
44842     
44843     beforeBlur : function()
44844     {
44845         if(!this.castInt){
44846             return;
44847         }
44848         
44849         var v = this.parseValue(this.getRawValue());
44850         
44851         if(v || v == 0){
44852             this.setValue(v);
44853         }
44854     },
44855     
44856     onBlur : function()
44857     {
44858         this.beforeBlur();
44859         
44860         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44861             //this.el.removeClass(this.focusClass);
44862         }
44863         
44864         this.hasFocus = false;
44865         
44866         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44867             this.validate();
44868         }
44869         
44870         var v = this.getValue();
44871         
44872         if(String(v) !== String(this.startValue)){
44873             this.fireEvent('change', this, v, this.startValue);
44874         }
44875         
44876         this.fireEvent("blur", this);
44877     },
44878     
44879     inputEl : function()
44880     {
44881         return this.el.select('.roo-money-amount-input', true).first();
44882     },
44883     
44884     currencyEl : function()
44885     {
44886         return this.el.select('.roo-money-currency-input', true).first();
44887     },
44888     
44889     hiddenEl : function()
44890     {
44891         return this.el.select('input.hidden-number-input',true).first();
44892     }
44893     
44894 });/**
44895  * @class Roo.bootstrap.BezierSignature
44896  * @extends Roo.bootstrap.Component
44897  * Bootstrap BezierSignature class
44898  * This script refer to:
44899  *    Title: Signature Pad
44900  *    Author: szimek
44901  *    Availability: https://github.com/szimek/signature_pad
44902  *
44903  * @constructor
44904  * Create a new BezierSignature
44905  * @param {Object} config The config object
44906  */
44907
44908 Roo.bootstrap.BezierSignature = function(config){
44909     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44910     this.addEvents({
44911         "resize" : true
44912     });
44913 };
44914
44915 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44916 {
44917      
44918     curve_data: [],
44919     
44920     is_empty: true,
44921     
44922     mouse_btn_down: true,
44923     
44924     /**
44925      * @cfg {int} canvas height
44926      */
44927     canvas_height: '200px',
44928     
44929     /**
44930      * @cfg {float|function} Radius of a single dot.
44931      */ 
44932     dot_size: false,
44933     
44934     /**
44935      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44936      */
44937     min_width: 0.5,
44938     
44939     /**
44940      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44941      */
44942     max_width: 2.5,
44943     
44944     /**
44945      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44946      */
44947     throttle: 16,
44948     
44949     /**
44950      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44951      */
44952     min_distance: 5,
44953     
44954     /**
44955      * @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.
44956      */
44957     bg_color: 'rgba(0, 0, 0, 0)',
44958     
44959     /**
44960      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44961      */
44962     dot_color: 'black',
44963     
44964     /**
44965      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44966      */ 
44967     velocity_filter_weight: 0.7,
44968     
44969     /**
44970      * @cfg {function} Callback when stroke begin. 
44971      */
44972     onBegin: false,
44973     
44974     /**
44975      * @cfg {function} Callback when stroke end.
44976      */
44977     onEnd: false,
44978     
44979     getAutoCreate : function()
44980     {
44981         var cls = 'roo-signature column';
44982         
44983         if(this.cls){
44984             cls += ' ' + this.cls;
44985         }
44986         
44987         var col_sizes = [
44988             'lg',
44989             'md',
44990             'sm',
44991             'xs'
44992         ];
44993         
44994         for(var i = 0; i < col_sizes.length; i++) {
44995             if(this[col_sizes[i]]) {
44996                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44997             }
44998         }
44999         
45000         var cfg = {
45001             tag: 'div',
45002             cls: cls,
45003             cn: [
45004                 {
45005                     tag: 'div',
45006                     cls: 'roo-signature-body',
45007                     cn: [
45008                         {
45009                             tag: 'canvas',
45010                             cls: 'roo-signature-body-canvas',
45011                             height: this.canvas_height,
45012                             width: this.canvas_width
45013                         }
45014                     ]
45015                 },
45016                 {
45017                     tag: 'input',
45018                     type: 'file',
45019                     style: 'display: none'
45020                 }
45021             ]
45022         };
45023         
45024         return cfg;
45025     },
45026     
45027     initEvents: function() 
45028     {
45029         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45030         
45031         var canvas = this.canvasEl();
45032         
45033         // mouse && touch event swapping...
45034         canvas.dom.style.touchAction = 'none';
45035         canvas.dom.style.msTouchAction = 'none';
45036         
45037         this.mouse_btn_down = false;
45038         canvas.on('mousedown', this._handleMouseDown, this);
45039         canvas.on('mousemove', this._handleMouseMove, this);
45040         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45041         
45042         if (window.PointerEvent) {
45043             canvas.on('pointerdown', this._handleMouseDown, this);
45044             canvas.on('pointermove', this._handleMouseMove, this);
45045             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45046         }
45047         
45048         if ('ontouchstart' in window) {
45049             canvas.on('touchstart', this._handleTouchStart, this);
45050             canvas.on('touchmove', this._handleTouchMove, this);
45051             canvas.on('touchend', this._handleTouchEnd, this);
45052         }
45053         
45054         Roo.EventManager.onWindowResize(this.resize, this, true);
45055         
45056         // file input event
45057         this.fileEl().on('change', this.uploadImage, this);
45058         
45059         this.clear();
45060         
45061         this.resize();
45062     },
45063     
45064     resize: function(){
45065         
45066         var canvas = this.canvasEl().dom;
45067         var ctx = this.canvasElCtx();
45068         var img_data = false;
45069         
45070         if(canvas.width > 0) {
45071             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45072         }
45073         // setting canvas width will clean img data
45074         canvas.width = 0;
45075         
45076         var style = window.getComputedStyle ? 
45077             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45078             
45079         var padding_left = parseInt(style.paddingLeft) || 0;
45080         var padding_right = parseInt(style.paddingRight) || 0;
45081         
45082         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45083         
45084         if(img_data) {
45085             ctx.putImageData(img_data, 0, 0);
45086         }
45087     },
45088     
45089     _handleMouseDown: function(e)
45090     {
45091         if (e.browserEvent.which === 1) {
45092             this.mouse_btn_down = true;
45093             this.strokeBegin(e);
45094         }
45095     },
45096     
45097     _handleMouseMove: function (e)
45098     {
45099         if (this.mouse_btn_down) {
45100             this.strokeMoveUpdate(e);
45101         }
45102     },
45103     
45104     _handleMouseUp: function (e)
45105     {
45106         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45107             this.mouse_btn_down = false;
45108             this.strokeEnd(e);
45109         }
45110     },
45111     
45112     _handleTouchStart: function (e) {
45113         
45114         e.preventDefault();
45115         if (e.browserEvent.targetTouches.length === 1) {
45116             // var touch = e.browserEvent.changedTouches[0];
45117             // this.strokeBegin(touch);
45118             
45119              this.strokeBegin(e); // assume e catching the correct xy...
45120         }
45121     },
45122     
45123     _handleTouchMove: function (e) {
45124         e.preventDefault();
45125         // var touch = event.targetTouches[0];
45126         // _this._strokeMoveUpdate(touch);
45127         this.strokeMoveUpdate(e);
45128     },
45129     
45130     _handleTouchEnd: function (e) {
45131         var wasCanvasTouched = e.target === this.canvasEl().dom;
45132         if (wasCanvasTouched) {
45133             e.preventDefault();
45134             // var touch = event.changedTouches[0];
45135             // _this._strokeEnd(touch);
45136             this.strokeEnd(e);
45137         }
45138     },
45139     
45140     reset: function () {
45141         this._lastPoints = [];
45142         this._lastVelocity = 0;
45143         this._lastWidth = (this.min_width + this.max_width) / 2;
45144         this.canvasElCtx().fillStyle = this.dot_color;
45145     },
45146     
45147     strokeMoveUpdate: function(e)
45148     {
45149         this.strokeUpdate(e);
45150         
45151         if (this.throttle) {
45152             this.throttleStroke(this.strokeUpdate, this.throttle);
45153         }
45154         else {
45155             this.strokeUpdate(e);
45156         }
45157     },
45158     
45159     strokeBegin: function(e)
45160     {
45161         var newPointGroup = {
45162             color: this.dot_color,
45163             points: []
45164         };
45165         
45166         if (typeof this.onBegin === 'function') {
45167             this.onBegin(e);
45168         }
45169         
45170         this.curve_data.push(newPointGroup);
45171         this.reset();
45172         this.strokeUpdate(e);
45173     },
45174     
45175     strokeUpdate: function(e)
45176     {
45177         var rect = this.canvasEl().dom.getBoundingClientRect();
45178         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45179         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45180         var lastPoints = lastPointGroup.points;
45181         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45182         var isLastPointTooClose = lastPoint
45183             ? point.distanceTo(lastPoint) <= this.min_distance
45184             : false;
45185         var color = lastPointGroup.color;
45186         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45187             var curve = this.addPoint(point);
45188             if (!lastPoint) {
45189                 this.drawDot({color: color, point: point});
45190             }
45191             else if (curve) {
45192                 this.drawCurve({color: color, curve: curve});
45193             }
45194             lastPoints.push({
45195                 time: point.time,
45196                 x: point.x,
45197                 y: point.y
45198             });
45199         }
45200     },
45201     
45202     strokeEnd: function(e)
45203     {
45204         this.strokeUpdate(e);
45205         if (typeof this.onEnd === 'function') {
45206             this.onEnd(e);
45207         }
45208     },
45209     
45210     addPoint:  function (point) {
45211         var _lastPoints = this._lastPoints;
45212         _lastPoints.push(point);
45213         if (_lastPoints.length > 2) {
45214             if (_lastPoints.length === 3) {
45215                 _lastPoints.unshift(_lastPoints[0]);
45216             }
45217             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45218             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45219             _lastPoints.shift();
45220             return curve;
45221         }
45222         return null;
45223     },
45224     
45225     calculateCurveWidths: function (startPoint, endPoint) {
45226         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45227             (1 - this.velocity_filter_weight) * this._lastVelocity;
45228
45229         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45230         var widths = {
45231             end: newWidth,
45232             start: this._lastWidth
45233         };
45234         
45235         this._lastVelocity = velocity;
45236         this._lastWidth = newWidth;
45237         return widths;
45238     },
45239     
45240     drawDot: function (_a) {
45241         var color = _a.color, point = _a.point;
45242         var ctx = this.canvasElCtx();
45243         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45244         ctx.beginPath();
45245         this.drawCurveSegment(point.x, point.y, width);
45246         ctx.closePath();
45247         ctx.fillStyle = color;
45248         ctx.fill();
45249     },
45250     
45251     drawCurve: function (_a) {
45252         var color = _a.color, curve = _a.curve;
45253         var ctx = this.canvasElCtx();
45254         var widthDelta = curve.endWidth - curve.startWidth;
45255         var drawSteps = Math.floor(curve.length()) * 2;
45256         ctx.beginPath();
45257         ctx.fillStyle = color;
45258         for (var i = 0; i < drawSteps; i += 1) {
45259         var t = i / drawSteps;
45260         var tt = t * t;
45261         var ttt = tt * t;
45262         var u = 1 - t;
45263         var uu = u * u;
45264         var uuu = uu * u;
45265         var x = uuu * curve.startPoint.x;
45266         x += 3 * uu * t * curve.control1.x;
45267         x += 3 * u * tt * curve.control2.x;
45268         x += ttt * curve.endPoint.x;
45269         var y = uuu * curve.startPoint.y;
45270         y += 3 * uu * t * curve.control1.y;
45271         y += 3 * u * tt * curve.control2.y;
45272         y += ttt * curve.endPoint.y;
45273         var width = curve.startWidth + ttt * widthDelta;
45274         this.drawCurveSegment(x, y, width);
45275         }
45276         ctx.closePath();
45277         ctx.fill();
45278     },
45279     
45280     drawCurveSegment: function (x, y, width) {
45281         var ctx = this.canvasElCtx();
45282         ctx.moveTo(x, y);
45283         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45284         this.is_empty = false;
45285     },
45286     
45287     clear: function()
45288     {
45289         var ctx = this.canvasElCtx();
45290         var canvas = this.canvasEl().dom;
45291         ctx.fillStyle = this.bg_color;
45292         ctx.clearRect(0, 0, canvas.width, canvas.height);
45293         ctx.fillRect(0, 0, canvas.width, canvas.height);
45294         this.curve_data = [];
45295         this.reset();
45296         this.is_empty = true;
45297     },
45298     
45299     fileEl: function()
45300     {
45301         return  this.el.select('input',true).first();
45302     },
45303     
45304     canvasEl: function()
45305     {
45306         return this.el.select('canvas',true).first();
45307     },
45308     
45309     canvasElCtx: function()
45310     {
45311         return this.el.select('canvas',true).first().dom.getContext('2d');
45312     },
45313     
45314     getImage: function(type)
45315     {
45316         if(this.is_empty) {
45317             return false;
45318         }
45319         
45320         // encryption ?
45321         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45322     },
45323     
45324     drawFromImage: function(img_src)
45325     {
45326         var img = new Image();
45327         
45328         img.onload = function(){
45329             this.canvasElCtx().drawImage(img, 0, 0);
45330         }.bind(this);
45331         
45332         img.src = img_src;
45333         
45334         this.is_empty = false;
45335     },
45336     
45337     selectImage: function()
45338     {
45339         this.fileEl().dom.click();
45340     },
45341     
45342     uploadImage: function(e)
45343     {
45344         var reader = new FileReader();
45345         
45346         reader.onload = function(e){
45347             var img = new Image();
45348             img.onload = function(){
45349                 this.reset();
45350                 this.canvasElCtx().drawImage(img, 0, 0);
45351             }.bind(this);
45352             img.src = e.target.result;
45353         }.bind(this);
45354         
45355         reader.readAsDataURL(e.target.files[0]);
45356     },
45357     
45358     // Bezier Point Constructor
45359     Point: (function () {
45360         function Point(x, y, time) {
45361             this.x = x;
45362             this.y = y;
45363             this.time = time || Date.now();
45364         }
45365         Point.prototype.distanceTo = function (start) {
45366             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45367         };
45368         Point.prototype.equals = function (other) {
45369             return this.x === other.x && this.y === other.y && this.time === other.time;
45370         };
45371         Point.prototype.velocityFrom = function (start) {
45372             return this.time !== start.time
45373             ? this.distanceTo(start) / (this.time - start.time)
45374             : 0;
45375         };
45376         return Point;
45377     }()),
45378     
45379     
45380     // Bezier Constructor
45381     Bezier: (function () {
45382         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45383             this.startPoint = startPoint;
45384             this.control2 = control2;
45385             this.control1 = control1;
45386             this.endPoint = endPoint;
45387             this.startWidth = startWidth;
45388             this.endWidth = endWidth;
45389         }
45390         Bezier.fromPoints = function (points, widths, scope) {
45391             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45392             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45393             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45394         };
45395         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45396             var dx1 = s1.x - s2.x;
45397             var dy1 = s1.y - s2.y;
45398             var dx2 = s2.x - s3.x;
45399             var dy2 = s2.y - s3.y;
45400             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45401             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45402             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45403             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45404             var dxm = m1.x - m2.x;
45405             var dym = m1.y - m2.y;
45406             var k = l2 / (l1 + l2);
45407             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45408             var tx = s2.x - cm.x;
45409             var ty = s2.y - cm.y;
45410             return {
45411                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45412                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45413             };
45414         };
45415         Bezier.prototype.length = function () {
45416             var steps = 10;
45417             var length = 0;
45418             var px;
45419             var py;
45420             for (var i = 0; i <= steps; i += 1) {
45421                 var t = i / steps;
45422                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45423                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45424                 if (i > 0) {
45425                     var xdiff = cx - px;
45426                     var ydiff = cy - py;
45427                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45428                 }
45429                 px = cx;
45430                 py = cy;
45431             }
45432             return length;
45433         };
45434         Bezier.prototype.point = function (t, start, c1, c2, end) {
45435             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45436             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45437             + (3.0 * c2 * (1.0 - t) * t * t)
45438             + (end * t * t * t);
45439         };
45440         return Bezier;
45441     }()),
45442     
45443     throttleStroke: function(fn, wait) {
45444       if (wait === void 0) { wait = 250; }
45445       var previous = 0;
45446       var timeout = null;
45447       var result;
45448       var storedContext;
45449       var storedArgs;
45450       var later = function () {
45451           previous = Date.now();
45452           timeout = null;
45453           result = fn.apply(storedContext, storedArgs);
45454           if (!timeout) {
45455               storedContext = null;
45456               storedArgs = [];
45457           }
45458       };
45459       return function wrapper() {
45460           var args = [];
45461           for (var _i = 0; _i < arguments.length; _i++) {
45462               args[_i] = arguments[_i];
45463           }
45464           var now = Date.now();
45465           var remaining = wait - (now - previous);
45466           storedContext = this;
45467           storedArgs = args;
45468           if (remaining <= 0 || remaining > wait) {
45469               if (timeout) {
45470                   clearTimeout(timeout);
45471                   timeout = null;
45472               }
45473               previous = now;
45474               result = fn.apply(storedContext, storedArgs);
45475               if (!timeout) {
45476                   storedContext = null;
45477                   storedArgs = [];
45478               }
45479           }
45480           else if (!timeout) {
45481               timeout = window.setTimeout(later, remaining);
45482           }
45483           return result;
45484       };
45485   }
45486   
45487 });
45488
45489  
45490
45491