roojs-ui.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  * @constructor
15348  * @param {Object} config
15349  */
15350 Roo.data.SimpleStore = function(config)
15351 {
15352     Roo.data.SimpleStore.superclass.constructor.call(this, {
15353         isLocal : true,
15354         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15355                 id: config.id
15356             },
15357             Roo.data.Record.create(config.fields)
15358         ),
15359         proxy : new Roo.data.MemoryProxy(config.data)
15360     });
15361     this.load();
15362 };
15363 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15364  * Based on:
15365  * Ext JS Library 1.1.1
15366  * Copyright(c) 2006-2007, Ext JS, LLC.
15367  *
15368  * Originally Released Under LGPL - original licence link has changed is not relivant.
15369  *
15370  * Fork - LGPL
15371  * <script type="text/javascript">
15372  */
15373
15374 /**
15375 /**
15376  * @extends Roo.data.Store
15377  * @class Roo.data.JsonStore
15378  * Small helper class to make creating Stores for JSON data easier. <br/>
15379 <pre><code>
15380 var store = new Roo.data.JsonStore({
15381     url: 'get-images.php',
15382     root: 'images',
15383     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15384 });
15385 </code></pre>
15386  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15387  * JsonReader and HttpProxy (unless inline data is provided).</b>
15388  * @cfg {Array} fields An array of field definition objects, or field name strings.
15389  * @constructor
15390  * @param {Object} config
15391  */
15392 Roo.data.JsonStore = function(c){
15393     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15394         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15395         reader: new Roo.data.JsonReader(c, c.fields)
15396     }));
15397 };
15398 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15399  * Based on:
15400  * Ext JS Library 1.1.1
15401  * Copyright(c) 2006-2007, Ext JS, LLC.
15402  *
15403  * Originally Released Under LGPL - original licence link has changed is not relivant.
15404  *
15405  * Fork - LGPL
15406  * <script type="text/javascript">
15407  */
15408
15409  
15410 Roo.data.Field = function(config){
15411     if(typeof config == "string"){
15412         config = {name: config};
15413     }
15414     Roo.apply(this, config);
15415     
15416     if(!this.type){
15417         this.type = "auto";
15418     }
15419     
15420     var st = Roo.data.SortTypes;
15421     // named sortTypes are supported, here we look them up
15422     if(typeof this.sortType == "string"){
15423         this.sortType = st[this.sortType];
15424     }
15425     
15426     // set default sortType for strings and dates
15427     if(!this.sortType){
15428         switch(this.type){
15429             case "string":
15430                 this.sortType = st.asUCString;
15431                 break;
15432             case "date":
15433                 this.sortType = st.asDate;
15434                 break;
15435             default:
15436                 this.sortType = st.none;
15437         }
15438     }
15439
15440     // define once
15441     var stripRe = /[\$,%]/g;
15442
15443     // prebuilt conversion function for this field, instead of
15444     // switching every time we're reading a value
15445     if(!this.convert){
15446         var cv, dateFormat = this.dateFormat;
15447         switch(this.type){
15448             case "":
15449             case "auto":
15450             case undefined:
15451                 cv = function(v){ return v; };
15452                 break;
15453             case "string":
15454                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15455                 break;
15456             case "int":
15457                 cv = function(v){
15458                     return v !== undefined && v !== null && v !== '' ?
15459                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15460                     };
15461                 break;
15462             case "float":
15463                 cv = function(v){
15464                     return v !== undefined && v !== null && v !== '' ?
15465                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15466                     };
15467                 break;
15468             case "bool":
15469             case "boolean":
15470                 cv = function(v){ return v === true || v === "true" || v == 1; };
15471                 break;
15472             case "date":
15473                 cv = function(v){
15474                     if(!v){
15475                         return '';
15476                     }
15477                     if(v instanceof Date){
15478                         return v;
15479                     }
15480                     if(dateFormat){
15481                         if(dateFormat == "timestamp"){
15482                             return new Date(v*1000);
15483                         }
15484                         return Date.parseDate(v, dateFormat);
15485                     }
15486                     var parsed = Date.parse(v);
15487                     return parsed ? new Date(parsed) : null;
15488                 };
15489              break;
15490             
15491         }
15492         this.convert = cv;
15493     }
15494 };
15495
15496 Roo.data.Field.prototype = {
15497     dateFormat: null,
15498     defaultValue: "",
15499     mapping: null,
15500     sortType : null,
15501     sortDir : "ASC"
15502 };/*
15503  * Based on:
15504  * Ext JS Library 1.1.1
15505  * Copyright(c) 2006-2007, Ext JS, LLC.
15506  *
15507  * Originally Released Under LGPL - original licence link has changed is not relivant.
15508  *
15509  * Fork - LGPL
15510  * <script type="text/javascript">
15511  */
15512  
15513 // Base class for reading structured data from a data source.  This class is intended to be
15514 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15515
15516 /**
15517  * @class Roo.data.DataReader
15518  * Base class for reading structured data from a data source.  This class is intended to be
15519  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15520  */
15521
15522 Roo.data.DataReader = function(meta, recordType){
15523     
15524     this.meta = meta;
15525     
15526     this.recordType = recordType instanceof Array ? 
15527         Roo.data.Record.create(recordType) : recordType;
15528 };
15529
15530 Roo.data.DataReader.prototype = {
15531     
15532     
15533     readerType : 'Data',
15534      /**
15535      * Create an empty record
15536      * @param {Object} data (optional) - overlay some values
15537      * @return {Roo.data.Record} record created.
15538      */
15539     newRow :  function(d) {
15540         var da =  {};
15541         this.recordType.prototype.fields.each(function(c) {
15542             switch( c.type) {
15543                 case 'int' : da[c.name] = 0; break;
15544                 case 'date' : da[c.name] = new Date(); break;
15545                 case 'float' : da[c.name] = 0.0; break;
15546                 case 'boolean' : da[c.name] = false; break;
15547                 default : da[c.name] = ""; break;
15548             }
15549             
15550         });
15551         return new this.recordType(Roo.apply(da, d));
15552     }
15553     
15554     
15555 };/*
15556  * Based on:
15557  * Ext JS Library 1.1.1
15558  * Copyright(c) 2006-2007, Ext JS, LLC.
15559  *
15560  * Originally Released Under LGPL - original licence link has changed is not relivant.
15561  *
15562  * Fork - LGPL
15563  * <script type="text/javascript">
15564  */
15565
15566 /**
15567  * @class Roo.data.DataProxy
15568  * @extends Roo.data.Observable
15569  * This class is an abstract base class for implementations which provide retrieval of
15570  * unformatted data objects.<br>
15571  * <p>
15572  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15573  * (of the appropriate type which knows how to parse the data object) to provide a block of
15574  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15575  * <p>
15576  * Custom implementations must implement the load method as described in
15577  * {@link Roo.data.HttpProxy#load}.
15578  */
15579 Roo.data.DataProxy = function(){
15580     this.addEvents({
15581         /**
15582          * @event beforeload
15583          * Fires before a network request is made to retrieve a data object.
15584          * @param {Object} This DataProxy object.
15585          * @param {Object} params The params parameter to the load function.
15586          */
15587         beforeload : true,
15588         /**
15589          * @event load
15590          * Fires before the load method's callback is called.
15591          * @param {Object} This DataProxy object.
15592          * @param {Object} o The data object.
15593          * @param {Object} arg The callback argument object passed to the load function.
15594          */
15595         load : true,
15596         /**
15597          * @event loadexception
15598          * Fires if an Exception occurs during data retrieval.
15599          * @param {Object} This DataProxy object.
15600          * @param {Object} o The data object.
15601          * @param {Object} arg The callback argument object passed to the load function.
15602          * @param {Object} e The Exception.
15603          */
15604         loadexception : true
15605     });
15606     Roo.data.DataProxy.superclass.constructor.call(this);
15607 };
15608
15609 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15610
15611     /**
15612      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15613      */
15614 /*
15615  * Based on:
15616  * Ext JS Library 1.1.1
15617  * Copyright(c) 2006-2007, Ext JS, LLC.
15618  *
15619  * Originally Released Under LGPL - original licence link has changed is not relivant.
15620  *
15621  * Fork - LGPL
15622  * <script type="text/javascript">
15623  */
15624 /**
15625  * @class Roo.data.MemoryProxy
15626  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15627  * to the Reader when its load method is called.
15628  * @constructor
15629  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15630  */
15631 Roo.data.MemoryProxy = function(data){
15632     if (data.data) {
15633         data = data.data;
15634     }
15635     Roo.data.MemoryProxy.superclass.constructor.call(this);
15636     this.data = data;
15637 };
15638
15639 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15640     
15641     /**
15642      * Load data from the requested source (in this case an in-memory
15643      * data object passed to the constructor), read the data object into
15644      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15645      * process that block using the passed callback.
15646      * @param {Object} params This parameter is not used by the MemoryProxy class.
15647      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15648      * object into a block of Roo.data.Records.
15649      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15650      * The function must be passed <ul>
15651      * <li>The Record block object</li>
15652      * <li>The "arg" argument from the load function</li>
15653      * <li>A boolean success indicator</li>
15654      * </ul>
15655      * @param {Object} scope The scope in which to call the callback
15656      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15657      */
15658     load : function(params, reader, callback, scope, arg){
15659         params = params || {};
15660         var result;
15661         try {
15662             result = reader.readRecords(params.data ? params.data :this.data);
15663         }catch(e){
15664             this.fireEvent("loadexception", this, arg, null, e);
15665             callback.call(scope, null, arg, false);
15666             return;
15667         }
15668         callback.call(scope, result, arg, true);
15669     },
15670     
15671     // private
15672     update : function(params, records){
15673         
15674     }
15675 });/*
15676  * Based on:
15677  * Ext JS Library 1.1.1
15678  * Copyright(c) 2006-2007, Ext JS, LLC.
15679  *
15680  * Originally Released Under LGPL - original licence link has changed is not relivant.
15681  *
15682  * Fork - LGPL
15683  * <script type="text/javascript">
15684  */
15685 /**
15686  * @class Roo.data.HttpProxy
15687  * @extends Roo.data.DataProxy
15688  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15689  * configured to reference a certain URL.<br><br>
15690  * <p>
15691  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15692  * from which the running page was served.<br><br>
15693  * <p>
15694  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15695  * <p>
15696  * Be aware that to enable the browser to parse an XML document, the server must set
15697  * the Content-Type header in the HTTP response to "text/xml".
15698  * @constructor
15699  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15700  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15701  * will be used to make the request.
15702  */
15703 Roo.data.HttpProxy = function(conn){
15704     Roo.data.HttpProxy.superclass.constructor.call(this);
15705     // is conn a conn config or a real conn?
15706     this.conn = conn;
15707     this.useAjax = !conn || !conn.events;
15708   
15709 };
15710
15711 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15712     // thse are take from connection...
15713     
15714     /**
15715      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15716      */
15717     /**
15718      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15719      * extra parameters to each request made by this object. (defaults to undefined)
15720      */
15721     /**
15722      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15723      *  to each request made by this object. (defaults to undefined)
15724      */
15725     /**
15726      * @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)
15727      */
15728     /**
15729      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15730      */
15731      /**
15732      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15733      * @type Boolean
15734      */
15735   
15736
15737     /**
15738      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15739      * @type Boolean
15740      */
15741     /**
15742      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15743      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15744      * a finer-grained basis than the DataProxy events.
15745      */
15746     getConnection : function(){
15747         return this.useAjax ? Roo.Ajax : this.conn;
15748     },
15749
15750     /**
15751      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15752      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15753      * process that block using the passed callback.
15754      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15755      * for the request to the remote server.
15756      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15757      * object into a block of Roo.data.Records.
15758      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15759      * The function must be passed <ul>
15760      * <li>The Record block object</li>
15761      * <li>The "arg" argument from the load function</li>
15762      * <li>A boolean success indicator</li>
15763      * </ul>
15764      * @param {Object} scope The scope in which to call the callback
15765      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15766      */
15767     load : function(params, reader, callback, scope, arg){
15768         if(this.fireEvent("beforeload", this, params) !== false){
15769             var  o = {
15770                 params : params || {},
15771                 request: {
15772                     callback : callback,
15773                     scope : scope,
15774                     arg : arg
15775                 },
15776                 reader: reader,
15777                 callback : this.loadResponse,
15778                 scope: this
15779             };
15780             if(this.useAjax){
15781                 Roo.applyIf(o, this.conn);
15782                 if(this.activeRequest){
15783                     Roo.Ajax.abort(this.activeRequest);
15784                 }
15785                 this.activeRequest = Roo.Ajax.request(o);
15786             }else{
15787                 this.conn.request(o);
15788             }
15789         }else{
15790             callback.call(scope||this, null, arg, false);
15791         }
15792     },
15793
15794     // private
15795     loadResponse : function(o, success, response){
15796         delete this.activeRequest;
15797         if(!success){
15798             this.fireEvent("loadexception", this, o, response);
15799             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15800             return;
15801         }
15802         var result;
15803         try {
15804             result = o.reader.read(response);
15805         }catch(e){
15806             this.fireEvent("loadexception", this, o, response, e);
15807             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15808             return;
15809         }
15810         
15811         this.fireEvent("load", this, o, o.request.arg);
15812         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15813     },
15814
15815     // private
15816     update : function(dataSet){
15817
15818     },
15819
15820     // private
15821     updateResponse : function(dataSet){
15822
15823     }
15824 });/*
15825  * Based on:
15826  * Ext JS Library 1.1.1
15827  * Copyright(c) 2006-2007, Ext JS, LLC.
15828  *
15829  * Originally Released Under LGPL - original licence link has changed is not relivant.
15830  *
15831  * Fork - LGPL
15832  * <script type="text/javascript">
15833  */
15834
15835 /**
15836  * @class Roo.data.ScriptTagProxy
15837  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15838  * other than the originating domain of the running page.<br><br>
15839  * <p>
15840  * <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
15841  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15842  * <p>
15843  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15844  * source code that is used as the source inside a &lt;script> tag.<br><br>
15845  * <p>
15846  * In order for the browser to process the returned data, the server must wrap the data object
15847  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15848  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15849  * depending on whether the callback name was passed:
15850  * <p>
15851  * <pre><code>
15852 boolean scriptTag = false;
15853 String cb = request.getParameter("callback");
15854 if (cb != null) {
15855     scriptTag = true;
15856     response.setContentType("text/javascript");
15857 } else {
15858     response.setContentType("application/x-json");
15859 }
15860 Writer out = response.getWriter();
15861 if (scriptTag) {
15862     out.write(cb + "(");
15863 }
15864 out.print(dataBlock.toJsonString());
15865 if (scriptTag) {
15866     out.write(");");
15867 }
15868 </pre></code>
15869  *
15870  * @constructor
15871  * @param {Object} config A configuration object.
15872  */
15873 Roo.data.ScriptTagProxy = function(config){
15874     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15875     Roo.apply(this, config);
15876     this.head = document.getElementsByTagName("head")[0];
15877 };
15878
15879 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15880
15881 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15882     /**
15883      * @cfg {String} url The URL from which to request the data object.
15884      */
15885     /**
15886      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15887      */
15888     timeout : 30000,
15889     /**
15890      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15891      * the server the name of the callback function set up by the load call to process the returned data object.
15892      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15893      * javascript output which calls this named function passing the data object as its only parameter.
15894      */
15895     callbackParam : "callback",
15896     /**
15897      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15898      * name to the request.
15899      */
15900     nocache : true,
15901
15902     /**
15903      * Load data from the configured URL, read the data object into
15904      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15905      * process that block using the passed callback.
15906      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15907      * for the request to the remote server.
15908      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15909      * object into a block of Roo.data.Records.
15910      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15911      * The function must be passed <ul>
15912      * <li>The Record block object</li>
15913      * <li>The "arg" argument from the load function</li>
15914      * <li>A boolean success indicator</li>
15915      * </ul>
15916      * @param {Object} scope The scope in which to call the callback
15917      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15918      */
15919     load : function(params, reader, callback, scope, arg){
15920         if(this.fireEvent("beforeload", this, params) !== false){
15921
15922             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15923
15924             var url = this.url;
15925             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15926             if(this.nocache){
15927                 url += "&_dc=" + (new Date().getTime());
15928             }
15929             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15930             var trans = {
15931                 id : transId,
15932                 cb : "stcCallback"+transId,
15933                 scriptId : "stcScript"+transId,
15934                 params : params,
15935                 arg : arg,
15936                 url : url,
15937                 callback : callback,
15938                 scope : scope,
15939                 reader : reader
15940             };
15941             var conn = this;
15942
15943             window[trans.cb] = function(o){
15944                 conn.handleResponse(o, trans);
15945             };
15946
15947             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15948
15949             if(this.autoAbort !== false){
15950                 this.abort();
15951             }
15952
15953             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15954
15955             var script = document.createElement("script");
15956             script.setAttribute("src", url);
15957             script.setAttribute("type", "text/javascript");
15958             script.setAttribute("id", trans.scriptId);
15959             this.head.appendChild(script);
15960
15961             this.trans = trans;
15962         }else{
15963             callback.call(scope||this, null, arg, false);
15964         }
15965     },
15966
15967     // private
15968     isLoading : function(){
15969         return this.trans ? true : false;
15970     },
15971
15972     /**
15973      * Abort the current server request.
15974      */
15975     abort : function(){
15976         if(this.isLoading()){
15977             this.destroyTrans(this.trans);
15978         }
15979     },
15980
15981     // private
15982     destroyTrans : function(trans, isLoaded){
15983         this.head.removeChild(document.getElementById(trans.scriptId));
15984         clearTimeout(trans.timeoutId);
15985         if(isLoaded){
15986             window[trans.cb] = undefined;
15987             try{
15988                 delete window[trans.cb];
15989             }catch(e){}
15990         }else{
15991             // if hasn't been loaded, wait for load to remove it to prevent script error
15992             window[trans.cb] = function(){
15993                 window[trans.cb] = undefined;
15994                 try{
15995                     delete window[trans.cb];
15996                 }catch(e){}
15997             };
15998         }
15999     },
16000
16001     // private
16002     handleResponse : function(o, trans){
16003         this.trans = false;
16004         this.destroyTrans(trans, true);
16005         var result;
16006         try {
16007             result = trans.reader.readRecords(o);
16008         }catch(e){
16009             this.fireEvent("loadexception", this, o, trans.arg, e);
16010             trans.callback.call(trans.scope||window, null, trans.arg, false);
16011             return;
16012         }
16013         this.fireEvent("load", this, o, trans.arg);
16014         trans.callback.call(trans.scope||window, result, trans.arg, true);
16015     },
16016
16017     // private
16018     handleFailure : function(trans){
16019         this.trans = false;
16020         this.destroyTrans(trans, false);
16021         this.fireEvent("loadexception", this, null, trans.arg);
16022         trans.callback.call(trans.scope||window, null, trans.arg, false);
16023     }
16024 });/*
16025  * Based on:
16026  * Ext JS Library 1.1.1
16027  * Copyright(c) 2006-2007, Ext JS, LLC.
16028  *
16029  * Originally Released Under LGPL - original licence link has changed is not relivant.
16030  *
16031  * Fork - LGPL
16032  * <script type="text/javascript">
16033  */
16034
16035 /**
16036  * @class Roo.data.JsonReader
16037  * @extends Roo.data.DataReader
16038  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16039  * based on mappings in a provided Roo.data.Record constructor.
16040  * 
16041  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16042  * in the reply previously. 
16043  * 
16044  * <p>
16045  * Example code:
16046  * <pre><code>
16047 var RecordDef = Roo.data.Record.create([
16048     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16049     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16050 ]);
16051 var myReader = new Roo.data.JsonReader({
16052     totalProperty: "results",    // The property which contains the total dataset size (optional)
16053     root: "rows",                // The property which contains an Array of row objects
16054     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16055 }, RecordDef);
16056 </code></pre>
16057  * <p>
16058  * This would consume a JSON file like this:
16059  * <pre><code>
16060 { 'results': 2, 'rows': [
16061     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16062     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16063 }
16064 </code></pre>
16065  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16066  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16067  * paged from the remote server.
16068  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16069  * @cfg {String} root name of the property which contains the Array of row objects.
16070  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16071  * @cfg {Array} fields Array of field definition objects
16072  * @constructor
16073  * Create a new JsonReader
16074  * @param {Object} meta Metadata configuration options
16075  * @param {Object} recordType Either an Array of field definition objects,
16076  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16077  */
16078 Roo.data.JsonReader = function(meta, recordType){
16079     
16080     meta = meta || {};
16081     // set some defaults:
16082     Roo.applyIf(meta, {
16083         totalProperty: 'total',
16084         successProperty : 'success',
16085         root : 'data',
16086         id : 'id'
16087     });
16088     
16089     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16090 };
16091 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16092     
16093     readerType : 'Json',
16094     
16095     /**
16096      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16097      * Used by Store query builder to append _requestMeta to params.
16098      * 
16099      */
16100     metaFromRemote : false,
16101     /**
16102      * This method is only used by a DataProxy which has retrieved data from a remote server.
16103      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16104      * @return {Object} data A data block which is used by an Roo.data.Store object as
16105      * a cache of Roo.data.Records.
16106      */
16107     read : function(response){
16108         var json = response.responseText;
16109        
16110         var o = /* eval:var:o */ eval("("+json+")");
16111         if(!o) {
16112             throw {message: "JsonReader.read: Json object not found"};
16113         }
16114         
16115         if(o.metaData){
16116             
16117             delete this.ef;
16118             this.metaFromRemote = true;
16119             this.meta = o.metaData;
16120             this.recordType = Roo.data.Record.create(o.metaData.fields);
16121             this.onMetaChange(this.meta, this.recordType, o);
16122         }
16123         return this.readRecords(o);
16124     },
16125
16126     // private function a store will implement
16127     onMetaChange : function(meta, recordType, o){
16128
16129     },
16130
16131     /**
16132          * @ignore
16133          */
16134     simpleAccess: function(obj, subsc) {
16135         return obj[subsc];
16136     },
16137
16138         /**
16139          * @ignore
16140          */
16141     getJsonAccessor: function(){
16142         var re = /[\[\.]/;
16143         return function(expr) {
16144             try {
16145                 return(re.test(expr))
16146                     ? new Function("obj", "return obj." + expr)
16147                     : function(obj){
16148                         return obj[expr];
16149                     };
16150             } catch(e){}
16151             return Roo.emptyFn;
16152         };
16153     }(),
16154
16155     /**
16156      * Create a data block containing Roo.data.Records from an XML document.
16157      * @param {Object} o An object which contains an Array of row objects in the property specified
16158      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16159      * which contains the total size of the dataset.
16160      * @return {Object} data A data block which is used by an Roo.data.Store object as
16161      * a cache of Roo.data.Records.
16162      */
16163     readRecords : function(o){
16164         /**
16165          * After any data loads, the raw JSON data is available for further custom processing.
16166          * @type Object
16167          */
16168         this.o = o;
16169         var s = this.meta, Record = this.recordType,
16170             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16171
16172 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16173         if (!this.ef) {
16174             if(s.totalProperty) {
16175                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16176                 }
16177                 if(s.successProperty) {
16178                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16179                 }
16180                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16181                 if (s.id) {
16182                         var g = this.getJsonAccessor(s.id);
16183                         this.getId = function(rec) {
16184                                 var r = g(rec);  
16185                                 return (r === undefined || r === "") ? null : r;
16186                         };
16187                 } else {
16188                         this.getId = function(){return null;};
16189                 }
16190             this.ef = [];
16191             for(var jj = 0; jj < fl; jj++){
16192                 f = fi[jj];
16193                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16194                 this.ef[jj] = this.getJsonAccessor(map);
16195             }
16196         }
16197
16198         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16199         if(s.totalProperty){
16200             var vt = parseInt(this.getTotal(o), 10);
16201             if(!isNaN(vt)){
16202                 totalRecords = vt;
16203             }
16204         }
16205         if(s.successProperty){
16206             var vs = this.getSuccess(o);
16207             if(vs === false || vs === 'false'){
16208                 success = false;
16209             }
16210         }
16211         var records = [];
16212         for(var i = 0; i < c; i++){
16213                 var n = root[i];
16214             var values = {};
16215             var id = this.getId(n);
16216             for(var j = 0; j < fl; j++){
16217                 f = fi[j];
16218             var v = this.ef[j](n);
16219             if (!f.convert) {
16220                 Roo.log('missing convert for ' + f.name);
16221                 Roo.log(f);
16222                 continue;
16223             }
16224             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16225             }
16226             var record = new Record(values, id);
16227             record.json = n;
16228             records[i] = record;
16229         }
16230         return {
16231             raw : o,
16232             success : success,
16233             records : records,
16234             totalRecords : totalRecords
16235         };
16236     },
16237     // used when loading children.. @see loadDataFromChildren
16238     toLoadData: function(rec)
16239     {
16240         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16241         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16242         return { data : data, total : data.length };
16243         
16244     }
16245 });/*
16246  * Based on:
16247  * Ext JS Library 1.1.1
16248  * Copyright(c) 2006-2007, Ext JS, LLC.
16249  *
16250  * Originally Released Under LGPL - original licence link has changed is not relivant.
16251  *
16252  * Fork - LGPL
16253  * <script type="text/javascript">
16254  */
16255
16256 /**
16257  * @class Roo.data.ArrayReader
16258  * @extends Roo.data.DataReader
16259  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16260  * Each element of that Array represents a row of data fields. The
16261  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16262  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16263  * <p>
16264  * Example code:.
16265  * <pre><code>
16266 var RecordDef = Roo.data.Record.create([
16267     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16268     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16269 ]);
16270 var myReader = new Roo.data.ArrayReader({
16271     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16272 }, RecordDef);
16273 </code></pre>
16274  * <p>
16275  * This would consume an Array like this:
16276  * <pre><code>
16277 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16278   </code></pre>
16279  
16280  * @constructor
16281  * Create a new JsonReader
16282  * @param {Object} meta Metadata configuration options.
16283  * @param {Object|Array} recordType Either an Array of field definition objects
16284  * 
16285  * @cfg {Array} fields Array of field definition objects
16286  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16287  * as specified to {@link Roo.data.Record#create},
16288  * or an {@link Roo.data.Record} object
16289  *
16290  * 
16291  * created using {@link Roo.data.Record#create}.
16292  */
16293 Roo.data.ArrayReader = function(meta, recordType)
16294 {    
16295     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16296 };
16297
16298 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16299     
16300       /**
16301      * Create a data block containing Roo.data.Records from an XML document.
16302      * @param {Object} o An Array of row objects which represents the dataset.
16303      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16304      * a cache of Roo.data.Records.
16305      */
16306     readRecords : function(o)
16307     {
16308         var sid = this.meta ? this.meta.id : null;
16309         var recordType = this.recordType, fields = recordType.prototype.fields;
16310         var records = [];
16311         var root = o;
16312         for(var i = 0; i < root.length; i++){
16313             var n = root[i];
16314             var values = {};
16315             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16316             for(var j = 0, jlen = fields.length; j < jlen; j++){
16317                 var f = fields.items[j];
16318                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16319                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16320                 v = f.convert(v);
16321                 values[f.name] = v;
16322             }
16323             var record = new recordType(values, id);
16324             record.json = n;
16325             records[records.length] = record;
16326         }
16327         return {
16328             records : records,
16329             totalRecords : records.length
16330         };
16331     },
16332     // used when loading children.. @see loadDataFromChildren
16333     toLoadData: function(rec)
16334     {
16335         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16336         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16337         
16338     }
16339     
16340     
16341 });/*
16342  * - LGPL
16343  * * 
16344  */
16345
16346 /**
16347  * @class Roo.bootstrap.ComboBox
16348  * @extends Roo.bootstrap.TriggerField
16349  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16350  * @cfg {Boolean} append (true|false) default false
16351  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16352  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16353  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16354  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16355  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16356  * @cfg {Boolean} animate default true
16357  * @cfg {Boolean} emptyResultText only for touch device
16358  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16359  * @cfg {String} emptyTitle default ''
16360  * @cfg {Number} width fixed with? experimental
16361  * @constructor
16362  * Create a new ComboBox.
16363  * @param {Object} config Configuration options
16364  */
16365 Roo.bootstrap.ComboBox = function(config){
16366     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16367     this.addEvents({
16368         /**
16369          * @event expand
16370          * Fires when the dropdown list is expanded
16371         * @param {Roo.bootstrap.ComboBox} combo This combo box
16372         */
16373         'expand' : true,
16374         /**
16375          * @event collapse
16376          * Fires when the dropdown list is collapsed
16377         * @param {Roo.bootstrap.ComboBox} combo This combo box
16378         */
16379         'collapse' : true,
16380         /**
16381          * @event beforeselect
16382          * Fires before a list item is selected. Return false to cancel the selection.
16383         * @param {Roo.bootstrap.ComboBox} combo This combo box
16384         * @param {Roo.data.Record} record The data record returned from the underlying store
16385         * @param {Number} index The index of the selected item in the dropdown list
16386         */
16387         'beforeselect' : true,
16388         /**
16389          * @event select
16390          * Fires when a list item is selected
16391         * @param {Roo.bootstrap.ComboBox} combo This combo box
16392         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16393         * @param {Number} index The index of the selected item in the dropdown list
16394         */
16395         'select' : true,
16396         /**
16397          * @event beforequery
16398          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16399          * The event object passed has these properties:
16400         * @param {Roo.bootstrap.ComboBox} combo This combo box
16401         * @param {String} query The query
16402         * @param {Boolean} forceAll true to force "all" query
16403         * @param {Boolean} cancel true to cancel the query
16404         * @param {Object} e The query event object
16405         */
16406         'beforequery': true,
16407          /**
16408          * @event add
16409          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16410         * @param {Roo.bootstrap.ComboBox} combo This combo box
16411         */
16412         'add' : true,
16413         /**
16414          * @event edit
16415          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16416         * @param {Roo.bootstrap.ComboBox} combo This combo box
16417         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16418         */
16419         'edit' : true,
16420         /**
16421          * @event remove
16422          * Fires when the remove value from the combobox array
16423         * @param {Roo.bootstrap.ComboBox} combo This combo box
16424         */
16425         'remove' : true,
16426         /**
16427          * @event afterremove
16428          * Fires when the remove value from the combobox array
16429         * @param {Roo.bootstrap.ComboBox} combo This combo box
16430         */
16431         'afterremove' : true,
16432         /**
16433          * @event specialfilter
16434          * Fires when specialfilter
16435             * @param {Roo.bootstrap.ComboBox} combo This combo box
16436             */
16437         'specialfilter' : true,
16438         /**
16439          * @event tick
16440          * Fires when tick the element
16441             * @param {Roo.bootstrap.ComboBox} combo This combo box
16442             */
16443         'tick' : true,
16444         /**
16445          * @event touchviewdisplay
16446          * Fires when touch view require special display (default is using displayField)
16447             * @param {Roo.bootstrap.ComboBox} combo This combo box
16448             * @param {Object} cfg set html .
16449             */
16450         'touchviewdisplay' : true
16451         
16452     });
16453     
16454     this.item = [];
16455     this.tickItems = [];
16456     
16457     this.selectedIndex = -1;
16458     if(this.mode == 'local'){
16459         if(config.queryDelay === undefined){
16460             this.queryDelay = 10;
16461         }
16462         if(config.minChars === undefined){
16463             this.minChars = 0;
16464         }
16465     }
16466 };
16467
16468 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16469      
16470     /**
16471      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16472      * rendering into an Roo.Editor, defaults to false)
16473      */
16474     /**
16475      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16476      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16477      */
16478     /**
16479      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16480      */
16481     /**
16482      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16483      * the dropdown list (defaults to undefined, with no header element)
16484      */
16485
16486      /**
16487      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16488      */
16489      
16490      /**
16491      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16492      */
16493     listWidth: undefined,
16494     /**
16495      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16496      * mode = 'remote' or 'text' if mode = 'local')
16497      */
16498     displayField: undefined,
16499     
16500     /**
16501      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16502      * mode = 'remote' or 'value' if mode = 'local'). 
16503      * Note: use of a valueField requires the user make a selection
16504      * in order for a value to be mapped.
16505      */
16506     valueField: undefined,
16507     /**
16508      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16509      */
16510     modalTitle : '',
16511     
16512     /**
16513      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16514      * field's data value (defaults to the underlying DOM element's name)
16515      */
16516     hiddenName: undefined,
16517     /**
16518      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16519      */
16520     listClass: '',
16521     /**
16522      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16523      */
16524     selectedClass: 'active',
16525     
16526     /**
16527      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16528      */
16529     shadow:'sides',
16530     /**
16531      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16532      * anchor positions (defaults to 'tl-bl')
16533      */
16534     listAlign: 'tl-bl?',
16535     /**
16536      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16537      */
16538     maxHeight: 300,
16539     /**
16540      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16541      * query specified by the allQuery config option (defaults to 'query')
16542      */
16543     triggerAction: 'query',
16544     /**
16545      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16546      * (defaults to 4, does not apply if editable = false)
16547      */
16548     minChars : 4,
16549     /**
16550      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16551      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16552      */
16553     typeAhead: false,
16554     /**
16555      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16556      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16557      */
16558     queryDelay: 500,
16559     /**
16560      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16561      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16562      */
16563     pageSize: 0,
16564     /**
16565      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16566      * when editable = true (defaults to false)
16567      */
16568     selectOnFocus:false,
16569     /**
16570      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16571      */
16572     queryParam: 'query',
16573     /**
16574      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16575      * when mode = 'remote' (defaults to 'Loading...')
16576      */
16577     loadingText: 'Loading...',
16578     /**
16579      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16580      */
16581     resizable: false,
16582     /**
16583      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16584      */
16585     handleHeight : 8,
16586     /**
16587      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16588      * traditional select (defaults to true)
16589      */
16590     editable: true,
16591     /**
16592      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16593      */
16594     allQuery: '',
16595     /**
16596      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16597      */
16598     mode: 'remote',
16599     /**
16600      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16601      * listWidth has a higher value)
16602      */
16603     minListWidth : 70,
16604     /**
16605      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16606      * allow the user to set arbitrary text into the field (defaults to false)
16607      */
16608     forceSelection:false,
16609     /**
16610      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16611      * if typeAhead = true (defaults to 250)
16612      */
16613     typeAheadDelay : 250,
16614     /**
16615      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16616      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16617      */
16618     valueNotFoundText : undefined,
16619     /**
16620      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16621      */
16622     blockFocus : false,
16623     
16624     /**
16625      * @cfg {Boolean} disableClear Disable showing of clear button.
16626      */
16627     disableClear : false,
16628     /**
16629      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16630      */
16631     alwaysQuery : false,
16632     
16633     /**
16634      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16635      */
16636     multiple : false,
16637     
16638     /**
16639      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16640      */
16641     invalidClass : "has-warning",
16642     
16643     /**
16644      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16645      */
16646     validClass : "has-success",
16647     
16648     /**
16649      * @cfg {Boolean} specialFilter (true|false) special filter default false
16650      */
16651     specialFilter : false,
16652     
16653     /**
16654      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16655      */
16656     mobileTouchView : true,
16657     
16658     /**
16659      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16660      */
16661     useNativeIOS : false,
16662     
16663     /**
16664      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16665      */
16666     mobile_restrict_height : false,
16667     
16668     ios_options : false,
16669     
16670     //private
16671     addicon : false,
16672     editicon: false,
16673     
16674     page: 0,
16675     hasQuery: false,
16676     append: false,
16677     loadNext: false,
16678     autoFocus : true,
16679     tickable : false,
16680     btnPosition : 'right',
16681     triggerList : true,
16682     showToggleBtn : true,
16683     animate : true,
16684     emptyResultText: 'Empty',
16685     triggerText : 'Select',
16686     emptyTitle : '',
16687     width : false,
16688     
16689     // element that contains real text value.. (when hidden is used..)
16690     
16691     getAutoCreate : function()
16692     {   
16693         var cfg = false;
16694         //render
16695         /*
16696          * Render classic select for iso
16697          */
16698         
16699         if(Roo.isIOS && this.useNativeIOS){
16700             cfg = this.getAutoCreateNativeIOS();
16701             return cfg;
16702         }
16703         
16704         /*
16705          * Touch Devices
16706          */
16707         
16708         if(Roo.isTouch && this.mobileTouchView){
16709             cfg = this.getAutoCreateTouchView();
16710             return cfg;;
16711         }
16712         
16713         /*
16714          *  Normal ComboBox
16715          */
16716         if(!this.tickable){
16717             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16718             return cfg;
16719         }
16720         
16721         /*
16722          *  ComboBox with tickable selections
16723          */
16724              
16725         var align = this.labelAlign || this.parentLabelAlign();
16726         
16727         cfg = {
16728             cls : 'form-group roo-combobox-tickable' //input-group
16729         };
16730         
16731         var btn_text_select = '';
16732         var btn_text_done = '';
16733         var btn_text_cancel = '';
16734         
16735         if (this.btn_text_show) {
16736             btn_text_select = 'Select';
16737             btn_text_done = 'Done';
16738             btn_text_cancel = 'Cancel'; 
16739         }
16740         
16741         var buttons = {
16742             tag : 'div',
16743             cls : 'tickable-buttons',
16744             cn : [
16745                 {
16746                     tag : 'button',
16747                     type : 'button',
16748                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16749                     //html : this.triggerText
16750                     html: btn_text_select
16751                 },
16752                 {
16753                     tag : 'button',
16754                     type : 'button',
16755                     name : 'ok',
16756                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16757                     //html : 'Done'
16758                     html: btn_text_done
16759                 },
16760                 {
16761                     tag : 'button',
16762                     type : 'button',
16763                     name : 'cancel',
16764                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16765                     //html : 'Cancel'
16766                     html: btn_text_cancel
16767                 }
16768             ]
16769         };
16770         
16771         if(this.editable){
16772             buttons.cn.unshift({
16773                 tag: 'input',
16774                 cls: 'roo-select2-search-field-input'
16775             });
16776         }
16777         
16778         var _this = this;
16779         
16780         Roo.each(buttons.cn, function(c){
16781             if (_this.size) {
16782                 c.cls += ' btn-' + _this.size;
16783             }
16784
16785             if (_this.disabled) {
16786                 c.disabled = true;
16787             }
16788         });
16789         
16790         var box = {
16791             tag: 'div',
16792             style : 'display: contents',
16793             cn: [
16794                 {
16795                     tag: 'input',
16796                     type : 'hidden',
16797                     cls: 'form-hidden-field'
16798                 },
16799                 {
16800                     tag: 'ul',
16801                     cls: 'roo-select2-choices',
16802                     cn:[
16803                         {
16804                             tag: 'li',
16805                             cls: 'roo-select2-search-field',
16806                             cn: [
16807                                 buttons
16808                             ]
16809                         }
16810                     ]
16811                 }
16812             ]
16813         };
16814         
16815         var combobox = {
16816             cls: 'roo-select2-container input-group roo-select2-container-multi',
16817             cn: [
16818                 
16819                 box
16820 //                {
16821 //                    tag: 'ul',
16822 //                    cls: 'typeahead typeahead-long dropdown-menu',
16823 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16824 //                }
16825             ]
16826         };
16827         
16828         if(this.hasFeedback && !this.allowBlank){
16829             
16830             var feedback = {
16831                 tag: 'span',
16832                 cls: 'glyphicon form-control-feedback'
16833             };
16834
16835             combobox.cn.push(feedback);
16836         }
16837         
16838         
16839         
16840         var indicator = {
16841             tag : 'i',
16842             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16843             tooltip : 'This field is required'
16844         };
16845         if (Roo.bootstrap.version == 4) {
16846             indicator = {
16847                 tag : 'i',
16848                 style : 'display:none'
16849             };
16850         }
16851         if (align ==='left' && this.fieldLabel.length) {
16852             
16853             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16854             
16855             cfg.cn = [
16856                 indicator,
16857                 {
16858                     tag: 'label',
16859                     'for' :  id,
16860                     cls : 'control-label col-form-label',
16861                     html : this.fieldLabel
16862
16863                 },
16864                 {
16865                     cls : "", 
16866                     cn: [
16867                         combobox
16868                     ]
16869                 }
16870
16871             ];
16872             
16873             var labelCfg = cfg.cn[1];
16874             var contentCfg = cfg.cn[2];
16875             
16876
16877             if(this.indicatorpos == 'right'){
16878                 
16879                 cfg.cn = [
16880                     {
16881                         tag: 'label',
16882                         'for' :  id,
16883                         cls : 'control-label col-form-label',
16884                         cn : [
16885                             {
16886                                 tag : 'span',
16887                                 html : this.fieldLabel
16888                             },
16889                             indicator
16890                         ]
16891                     },
16892                     {
16893                         cls : "",
16894                         cn: [
16895                             combobox
16896                         ]
16897                     }
16898
16899                 ];
16900                 
16901                 
16902                 
16903                 labelCfg = cfg.cn[0];
16904                 contentCfg = cfg.cn[1];
16905             
16906             }
16907             
16908             if(this.labelWidth > 12){
16909                 labelCfg.style = "width: " + this.labelWidth + 'px';
16910             }
16911             if(this.width * 1 > 0){
16912                 contentCfg.style = "width: " + this.width + 'px';
16913             }
16914             if(this.labelWidth < 13 && this.labelmd == 0){
16915                 this.labelmd = this.labelWidth;
16916             }
16917             
16918             if(this.labellg > 0){
16919                 labelCfg.cls += ' col-lg-' + this.labellg;
16920                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16921             }
16922             
16923             if(this.labelmd > 0){
16924                 labelCfg.cls += ' col-md-' + this.labelmd;
16925                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16926             }
16927             
16928             if(this.labelsm > 0){
16929                 labelCfg.cls += ' col-sm-' + this.labelsm;
16930                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16931             }
16932             
16933             if(this.labelxs > 0){
16934                 labelCfg.cls += ' col-xs-' + this.labelxs;
16935                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16936             }
16937                 
16938                 
16939         } else if ( this.fieldLabel.length) {
16940 //                Roo.log(" label");
16941                  cfg.cn = [
16942                    indicator,
16943                     {
16944                         tag: 'label',
16945                         //cls : 'input-group-addon',
16946                         html : this.fieldLabel
16947                     },
16948                     combobox
16949                 ];
16950                 
16951                 if(this.indicatorpos == 'right'){
16952                     cfg.cn = [
16953                         {
16954                             tag: 'label',
16955                             //cls : 'input-group-addon',
16956                             html : this.fieldLabel
16957                         },
16958                         indicator,
16959                         combobox
16960                     ];
16961                     
16962                 }
16963
16964         } else {
16965             
16966 //                Roo.log(" no label && no align");
16967                 cfg = combobox
16968                      
16969                 
16970         }
16971          
16972         var settings=this;
16973         ['xs','sm','md','lg'].map(function(size){
16974             if (settings[size]) {
16975                 cfg.cls += ' col-' + size + '-' + settings[size];
16976             }
16977         });
16978         
16979         return cfg;
16980         
16981     },
16982     
16983     _initEventsCalled : false,
16984     
16985     // private
16986     initEvents: function()
16987     {   
16988         if (this._initEventsCalled) { // as we call render... prevent looping...
16989             return;
16990         }
16991         this._initEventsCalled = true;
16992         
16993         if (!this.store) {
16994             throw "can not find store for combo";
16995         }
16996         
16997         this.indicator = this.indicatorEl();
16998         
16999         this.store = Roo.factory(this.store, Roo.data);
17000         this.store.parent = this;
17001         
17002         // if we are building from html. then this element is so complex, that we can not really
17003         // use the rendered HTML.
17004         // so we have to trash and replace the previous code.
17005         if (Roo.XComponent.build_from_html) {
17006             // remove this element....
17007             var e = this.el.dom, k=0;
17008             while (e ) { e = e.previousSibling;  ++k;}
17009
17010             this.el.remove();
17011             
17012             this.el=false;
17013             this.rendered = false;
17014             
17015             this.render(this.parent().getChildContainer(true), k);
17016         }
17017         
17018         if(Roo.isIOS && this.useNativeIOS){
17019             this.initIOSView();
17020             return;
17021         }
17022         
17023         /*
17024          * Touch Devices
17025          */
17026         
17027         if(Roo.isTouch && this.mobileTouchView){
17028             this.initTouchView();
17029             return;
17030         }
17031         
17032         if(this.tickable){
17033             this.initTickableEvents();
17034             return;
17035         }
17036         
17037         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17038         
17039         if(this.hiddenName){
17040             
17041             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17042             
17043             this.hiddenField.dom.value =
17044                 this.hiddenValue !== undefined ? this.hiddenValue :
17045                 this.value !== undefined ? this.value : '';
17046
17047             // prevent input submission
17048             this.el.dom.removeAttribute('name');
17049             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17050              
17051              
17052         }
17053         //if(Roo.isGecko){
17054         //    this.el.dom.setAttribute('autocomplete', 'off');
17055         //}
17056         
17057         var cls = 'x-combo-list';
17058         
17059         //this.list = new Roo.Layer({
17060         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17061         //});
17062         
17063         var _this = this;
17064         
17065         (function(){
17066             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17067             _this.list.setWidth(lw);
17068         }).defer(100);
17069         
17070         this.list.on('mouseover', this.onViewOver, this);
17071         this.list.on('mousemove', this.onViewMove, this);
17072         this.list.on('scroll', this.onViewScroll, this);
17073         
17074         /*
17075         this.list.swallowEvent('mousewheel');
17076         this.assetHeight = 0;
17077
17078         if(this.title){
17079             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17080             this.assetHeight += this.header.getHeight();
17081         }
17082
17083         this.innerList = this.list.createChild({cls:cls+'-inner'});
17084         this.innerList.on('mouseover', this.onViewOver, this);
17085         this.innerList.on('mousemove', this.onViewMove, this);
17086         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17087         
17088         if(this.allowBlank && !this.pageSize && !this.disableClear){
17089             this.footer = this.list.createChild({cls:cls+'-ft'});
17090             this.pageTb = new Roo.Toolbar(this.footer);
17091            
17092         }
17093         if(this.pageSize){
17094             this.footer = this.list.createChild({cls:cls+'-ft'});
17095             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17096                     {pageSize: this.pageSize});
17097             
17098         }
17099         
17100         if (this.pageTb && this.allowBlank && !this.disableClear) {
17101             var _this = this;
17102             this.pageTb.add(new Roo.Toolbar.Fill(), {
17103                 cls: 'x-btn-icon x-btn-clear',
17104                 text: '&#160;',
17105                 handler: function()
17106                 {
17107                     _this.collapse();
17108                     _this.clearValue();
17109                     _this.onSelect(false, -1);
17110                 }
17111             });
17112         }
17113         if (this.footer) {
17114             this.assetHeight += this.footer.getHeight();
17115         }
17116         */
17117             
17118         if(!this.tpl){
17119             this.tpl = Roo.bootstrap.version == 4 ?
17120                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17121                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17122         }
17123
17124         this.view = new Roo.View(this.list, this.tpl, {
17125             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17126         });
17127         //this.view.wrapEl.setDisplayed(false);
17128         this.view.on('click', this.onViewClick, this);
17129         
17130         
17131         this.store.on('beforeload', this.onBeforeLoad, this);
17132         this.store.on('load', this.onLoad, this);
17133         this.store.on('loadexception', this.onLoadException, this);
17134         /*
17135         if(this.resizable){
17136             this.resizer = new Roo.Resizable(this.list,  {
17137                pinned:true, handles:'se'
17138             });
17139             this.resizer.on('resize', function(r, w, h){
17140                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17141                 this.listWidth = w;
17142                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17143                 this.restrictHeight();
17144             }, this);
17145             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17146         }
17147         */
17148         if(!this.editable){
17149             this.editable = true;
17150             this.setEditable(false);
17151         }
17152         
17153         /*
17154         
17155         if (typeof(this.events.add.listeners) != 'undefined') {
17156             
17157             this.addicon = this.wrap.createChild(
17158                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17159        
17160             this.addicon.on('click', function(e) {
17161                 this.fireEvent('add', this);
17162             }, this);
17163         }
17164         if (typeof(this.events.edit.listeners) != 'undefined') {
17165             
17166             this.editicon = this.wrap.createChild(
17167                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17168             if (this.addicon) {
17169                 this.editicon.setStyle('margin-left', '40px');
17170             }
17171             this.editicon.on('click', function(e) {
17172                 
17173                 // we fire even  if inothing is selected..
17174                 this.fireEvent('edit', this, this.lastData );
17175                 
17176             }, this);
17177         }
17178         */
17179         
17180         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17181             "up" : function(e){
17182                 this.inKeyMode = true;
17183                 this.selectPrev();
17184             },
17185
17186             "down" : function(e){
17187                 if(!this.isExpanded()){
17188                     this.onTriggerClick();
17189                 }else{
17190                     this.inKeyMode = true;
17191                     this.selectNext();
17192                 }
17193             },
17194
17195             "enter" : function(e){
17196 //                this.onViewClick();
17197                 //return true;
17198                 this.collapse();
17199                 
17200                 if(this.fireEvent("specialkey", this, e)){
17201                     this.onViewClick(false);
17202                 }
17203                 
17204                 return true;
17205             },
17206
17207             "esc" : function(e){
17208                 this.collapse();
17209             },
17210
17211             "tab" : function(e){
17212                 this.collapse();
17213                 
17214                 if(this.fireEvent("specialkey", this, e)){
17215                     this.onViewClick(false);
17216                 }
17217                 
17218                 return true;
17219             },
17220
17221             scope : this,
17222
17223             doRelay : function(foo, bar, hname){
17224                 if(hname == 'down' || this.scope.isExpanded()){
17225                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17226                 }
17227                 return true;
17228             },
17229
17230             forceKeyDown: true
17231         });
17232         
17233         
17234         this.queryDelay = Math.max(this.queryDelay || 10,
17235                 this.mode == 'local' ? 10 : 250);
17236         
17237         
17238         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17239         
17240         if(this.typeAhead){
17241             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17242         }
17243         if(this.editable !== false){
17244             this.inputEl().on("keyup", this.onKeyUp, this);
17245         }
17246         if(this.forceSelection){
17247             this.inputEl().on('blur', this.doForce, this);
17248         }
17249         
17250         if(this.multiple){
17251             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17252             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17253         }
17254     },
17255     
17256     initTickableEvents: function()
17257     {   
17258         this.createList();
17259         
17260         if(this.hiddenName){
17261             
17262             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17263             
17264             this.hiddenField.dom.value =
17265                 this.hiddenValue !== undefined ? this.hiddenValue :
17266                 this.value !== undefined ? this.value : '';
17267
17268             // prevent input submission
17269             this.el.dom.removeAttribute('name');
17270             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17271              
17272              
17273         }
17274         
17275 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17276         
17277         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17278         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17279         if(this.triggerList){
17280             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17281         }
17282          
17283         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17284         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17285         
17286         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17287         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17288         
17289         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17290         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17291         
17292         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17293         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17294         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17295         
17296         this.okBtn.hide();
17297         this.cancelBtn.hide();
17298         
17299         var _this = this;
17300         
17301         (function(){
17302             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17303             _this.list.setWidth(lw);
17304         }).defer(100);
17305         
17306         this.list.on('mouseover', this.onViewOver, this);
17307         this.list.on('mousemove', this.onViewMove, this);
17308         
17309         this.list.on('scroll', this.onViewScroll, this);
17310         
17311         if(!this.tpl){
17312             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17313                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17314         }
17315
17316         this.view = new Roo.View(this.list, this.tpl, {
17317             singleSelect:true,
17318             tickable:true,
17319             parent:this,
17320             store: this.store,
17321             selectedClass: this.selectedClass
17322         });
17323         
17324         //this.view.wrapEl.setDisplayed(false);
17325         this.view.on('click', this.onViewClick, this);
17326         
17327         
17328         
17329         this.store.on('beforeload', this.onBeforeLoad, this);
17330         this.store.on('load', this.onLoad, this);
17331         this.store.on('loadexception', this.onLoadException, this);
17332         
17333         if(this.editable){
17334             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17335                 "up" : function(e){
17336                     this.inKeyMode = true;
17337                     this.selectPrev();
17338                 },
17339
17340                 "down" : function(e){
17341                     this.inKeyMode = true;
17342                     this.selectNext();
17343                 },
17344
17345                 "enter" : function(e){
17346                     if(this.fireEvent("specialkey", this, e)){
17347                         this.onViewClick(false);
17348                     }
17349                     
17350                     return true;
17351                 },
17352
17353                 "esc" : function(e){
17354                     this.onTickableFooterButtonClick(e, false, false);
17355                 },
17356
17357                 "tab" : function(e){
17358                     this.fireEvent("specialkey", this, e);
17359                     
17360                     this.onTickableFooterButtonClick(e, false, false);
17361                     
17362                     return true;
17363                 },
17364
17365                 scope : this,
17366
17367                 doRelay : function(e, fn, key){
17368                     if(this.scope.isExpanded()){
17369                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17370                     }
17371                     return true;
17372                 },
17373
17374                 forceKeyDown: true
17375             });
17376         }
17377         
17378         this.queryDelay = Math.max(this.queryDelay || 10,
17379                 this.mode == 'local' ? 10 : 250);
17380         
17381         
17382         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17383         
17384         if(this.typeAhead){
17385             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17386         }
17387         
17388         if(this.editable !== false){
17389             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17390         }
17391         
17392         this.indicator = this.indicatorEl();
17393         
17394         if(this.indicator){
17395             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17396             this.indicator.hide();
17397         }
17398         
17399     },
17400
17401     onDestroy : function(){
17402         if(this.view){
17403             this.view.setStore(null);
17404             this.view.el.removeAllListeners();
17405             this.view.el.remove();
17406             this.view.purgeListeners();
17407         }
17408         if(this.list){
17409             this.list.dom.innerHTML  = '';
17410         }
17411         
17412         if(this.store){
17413             this.store.un('beforeload', this.onBeforeLoad, this);
17414             this.store.un('load', this.onLoad, this);
17415             this.store.un('loadexception', this.onLoadException, this);
17416         }
17417         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17418     },
17419
17420     // private
17421     fireKey : function(e){
17422         if(e.isNavKeyPress() && !this.list.isVisible()){
17423             this.fireEvent("specialkey", this, e);
17424         }
17425     },
17426
17427     // private
17428     onResize: function(w, h)
17429     {
17430         
17431         
17432 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17433 //        
17434 //        if(typeof w != 'number'){
17435 //            // we do not handle it!?!?
17436 //            return;
17437 //        }
17438 //        var tw = this.trigger.getWidth();
17439 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17440 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17441 //        var x = w - tw;
17442 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17443 //            
17444 //        //this.trigger.setStyle('left', x+'px');
17445 //        
17446 //        if(this.list && this.listWidth === undefined){
17447 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17448 //            this.list.setWidth(lw);
17449 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17450 //        }
17451         
17452     
17453         
17454     },
17455
17456     /**
17457      * Allow or prevent the user from directly editing the field text.  If false is passed,
17458      * the user will only be able to select from the items defined in the dropdown list.  This method
17459      * is the runtime equivalent of setting the 'editable' config option at config time.
17460      * @param {Boolean} value True to allow the user to directly edit the field text
17461      */
17462     setEditable : function(value){
17463         if(value == this.editable){
17464             return;
17465         }
17466         this.editable = value;
17467         if(!value){
17468             this.inputEl().dom.setAttribute('readOnly', true);
17469             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17470             this.inputEl().addClass('x-combo-noedit');
17471         }else{
17472             this.inputEl().dom.removeAttribute('readOnly');
17473             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17474             this.inputEl().removeClass('x-combo-noedit');
17475         }
17476     },
17477
17478     // private
17479     
17480     onBeforeLoad : function(combo,opts){
17481         if(!this.hasFocus){
17482             return;
17483         }
17484          if (!opts.add) {
17485             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17486          }
17487         this.restrictHeight();
17488         this.selectedIndex = -1;
17489     },
17490
17491     // private
17492     onLoad : function(){
17493         
17494         this.hasQuery = false;
17495         
17496         if(!this.hasFocus){
17497             return;
17498         }
17499         
17500         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17501             this.loading.hide();
17502         }
17503         
17504         if(this.store.getCount() > 0){
17505             
17506             this.expand();
17507             this.restrictHeight();
17508             if(this.lastQuery == this.allQuery){
17509                 if(this.editable && !this.tickable){
17510                     this.inputEl().dom.select();
17511                 }
17512                 
17513                 if(
17514                     !this.selectByValue(this.value, true) &&
17515                     this.autoFocus && 
17516                     (
17517                         !this.store.lastOptions ||
17518                         typeof(this.store.lastOptions.add) == 'undefined' || 
17519                         this.store.lastOptions.add != true
17520                     )
17521                 ){
17522                     this.select(0, true);
17523                 }
17524             }else{
17525                 if(this.autoFocus){
17526                     this.selectNext();
17527                 }
17528                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17529                     this.taTask.delay(this.typeAheadDelay);
17530                 }
17531             }
17532         }else{
17533             this.onEmptyResults();
17534         }
17535         
17536         //this.el.focus();
17537     },
17538     // private
17539     onLoadException : function()
17540     {
17541         this.hasQuery = false;
17542         
17543         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17544             this.loading.hide();
17545         }
17546         
17547         if(this.tickable && this.editable){
17548             return;
17549         }
17550         
17551         this.collapse();
17552         // only causes errors at present
17553         //Roo.log(this.store.reader.jsonData);
17554         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17555             // fixme
17556             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17557         //}
17558         
17559         
17560     },
17561     // private
17562     onTypeAhead : function(){
17563         if(this.store.getCount() > 0){
17564             var r = this.store.getAt(0);
17565             var newValue = r.data[this.displayField];
17566             var len = newValue.length;
17567             var selStart = this.getRawValue().length;
17568             
17569             if(selStart != len){
17570                 this.setRawValue(newValue);
17571                 this.selectText(selStart, newValue.length);
17572             }
17573         }
17574     },
17575
17576     // private
17577     onSelect : function(record, index){
17578         
17579         if(this.fireEvent('beforeselect', this, record, index) !== false){
17580         
17581             this.setFromData(index > -1 ? record.data : false);
17582             
17583             this.collapse();
17584             this.fireEvent('select', this, record, index);
17585         }
17586     },
17587
17588     /**
17589      * Returns the currently selected field value or empty string if no value is set.
17590      * @return {String} value The selected value
17591      */
17592     getValue : function()
17593     {
17594         if(Roo.isIOS && this.useNativeIOS){
17595             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17596         }
17597         
17598         if(this.multiple){
17599             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17600         }
17601         
17602         if(this.valueField){
17603             return typeof this.value != 'undefined' ? this.value : '';
17604         }else{
17605             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17606         }
17607     },
17608     
17609     getRawValue : function()
17610     {
17611         if(Roo.isIOS && this.useNativeIOS){
17612             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17613         }
17614         
17615         var v = this.inputEl().getValue();
17616         
17617         return v;
17618     },
17619
17620     /**
17621      * Clears any text/value currently set in the field
17622      */
17623     clearValue : function(){
17624         
17625         if(this.hiddenField){
17626             this.hiddenField.dom.value = '';
17627         }
17628         this.value = '';
17629         this.setRawValue('');
17630         this.lastSelectionText = '';
17631         this.lastData = false;
17632         
17633         var close = this.closeTriggerEl();
17634         
17635         if(close){
17636             close.hide();
17637         }
17638         
17639         this.validate();
17640         
17641     },
17642
17643     /**
17644      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17645      * will be displayed in the field.  If the value does not match the data value of an existing item,
17646      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17647      * Otherwise the field will be blank (although the value will still be set).
17648      * @param {String} value The value to match
17649      */
17650     setValue : function(v)
17651     {
17652         if(Roo.isIOS && this.useNativeIOS){
17653             this.setIOSValue(v);
17654             return;
17655         }
17656         
17657         if(this.multiple){
17658             this.syncValue();
17659             return;
17660         }
17661         
17662         var text = v;
17663         if(this.valueField){
17664             var r = this.findRecord(this.valueField, v);
17665             if(r){
17666                 text = r.data[this.displayField];
17667             }else if(this.valueNotFoundText !== undefined){
17668                 text = this.valueNotFoundText;
17669             }
17670         }
17671         this.lastSelectionText = text;
17672         if(this.hiddenField){
17673             this.hiddenField.dom.value = v;
17674         }
17675         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17676         this.value = v;
17677         
17678         var close = this.closeTriggerEl();
17679         
17680         if(close){
17681             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17682         }
17683         
17684         this.validate();
17685     },
17686     /**
17687      * @property {Object} the last set data for the element
17688      */
17689     
17690     lastData : false,
17691     /**
17692      * Sets the value of the field based on a object which is related to the record format for the store.
17693      * @param {Object} value the value to set as. or false on reset?
17694      */
17695     setFromData : function(o){
17696         
17697         if(this.multiple){
17698             this.addItem(o);
17699             return;
17700         }
17701             
17702         var dv = ''; // display value
17703         var vv = ''; // value value..
17704         this.lastData = o;
17705         if (this.displayField) {
17706             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17707         } else {
17708             // this is an error condition!!!
17709             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17710         }
17711         
17712         if(this.valueField){
17713             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17714         }
17715         
17716         var close = this.closeTriggerEl();
17717         
17718         if(close){
17719             if(dv.length || vv * 1 > 0){
17720                 close.show() ;
17721                 this.blockFocus=true;
17722             } else {
17723                 close.hide();
17724             }             
17725         }
17726         
17727         if(this.hiddenField){
17728             this.hiddenField.dom.value = vv;
17729             
17730             this.lastSelectionText = dv;
17731             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17732             this.value = vv;
17733             return;
17734         }
17735         // no hidden field.. - we store the value in 'value', but still display
17736         // display field!!!!
17737         this.lastSelectionText = dv;
17738         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17739         this.value = vv;
17740         
17741         
17742         
17743     },
17744     // private
17745     reset : function(){
17746         // overridden so that last data is reset..
17747         
17748         if(this.multiple){
17749             this.clearItem();
17750             return;
17751         }
17752         
17753         this.setValue(this.originalValue);
17754         //this.clearInvalid();
17755         this.lastData = false;
17756         if (this.view) {
17757             this.view.clearSelections();
17758         }
17759         
17760         this.validate();
17761     },
17762     // private
17763     findRecord : function(prop, value){
17764         var record;
17765         if(this.store.getCount() > 0){
17766             this.store.each(function(r){
17767                 if(r.data[prop] == value){
17768                     record = r;
17769                     return false;
17770                 }
17771                 return true;
17772             });
17773         }
17774         return record;
17775     },
17776     
17777     getName: function()
17778     {
17779         // returns hidden if it's set..
17780         if (!this.rendered) {return ''};
17781         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17782         
17783     },
17784     // private
17785     onViewMove : function(e, t){
17786         this.inKeyMode = false;
17787     },
17788
17789     // private
17790     onViewOver : function(e, t){
17791         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17792             return;
17793         }
17794         var item = this.view.findItemFromChild(t);
17795         
17796         if(item){
17797             var index = this.view.indexOf(item);
17798             this.select(index, false);
17799         }
17800     },
17801
17802     // private
17803     onViewClick : function(view, doFocus, el, e)
17804     {
17805         var index = this.view.getSelectedIndexes()[0];
17806         
17807         var r = this.store.getAt(index);
17808         
17809         if(this.tickable){
17810             
17811             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17812                 return;
17813             }
17814             
17815             var rm = false;
17816             var _this = this;
17817             
17818             Roo.each(this.tickItems, function(v,k){
17819                 
17820                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17821                     Roo.log(v);
17822                     _this.tickItems.splice(k, 1);
17823                     
17824                     if(typeof(e) == 'undefined' && view == false){
17825                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17826                     }
17827                     
17828                     rm = true;
17829                     return;
17830                 }
17831             });
17832             
17833             if(rm){
17834                 return;
17835             }
17836             
17837             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17838                 this.tickItems.push(r.data);
17839             }
17840             
17841             if(typeof(e) == 'undefined' && view == false){
17842                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17843             }
17844                     
17845             return;
17846         }
17847         
17848         if(r){
17849             this.onSelect(r, index);
17850         }
17851         if(doFocus !== false && !this.blockFocus){
17852             this.inputEl().focus();
17853         }
17854     },
17855
17856     // private
17857     restrictHeight : function(){
17858         //this.innerList.dom.style.height = '';
17859         //var inner = this.innerList.dom;
17860         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17861         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17862         //this.list.beginUpdate();
17863         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17864         this.list.alignTo(this.inputEl(), this.listAlign);
17865         this.list.alignTo(this.inputEl(), this.listAlign);
17866         //this.list.endUpdate();
17867     },
17868
17869     // private
17870     onEmptyResults : function(){
17871         
17872         if(this.tickable && this.editable){
17873             this.hasFocus = false;
17874             this.restrictHeight();
17875             return;
17876         }
17877         
17878         this.collapse();
17879     },
17880
17881     /**
17882      * Returns true if the dropdown list is expanded, else false.
17883      */
17884     isExpanded : function(){
17885         return this.list.isVisible();
17886     },
17887
17888     /**
17889      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17890      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17891      * @param {String} value The data value of the item to select
17892      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17893      * selected item if it is not currently in view (defaults to true)
17894      * @return {Boolean} True if the value matched an item in the list, else false
17895      */
17896     selectByValue : function(v, scrollIntoView){
17897         if(v !== undefined && v !== null){
17898             var r = this.findRecord(this.valueField || this.displayField, v);
17899             if(r){
17900                 this.select(this.store.indexOf(r), scrollIntoView);
17901                 return true;
17902             }
17903         }
17904         return false;
17905     },
17906
17907     /**
17908      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17909      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17910      * @param {Number} index The zero-based index of the list item to select
17911      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17912      * selected item if it is not currently in view (defaults to true)
17913      */
17914     select : function(index, scrollIntoView){
17915         this.selectedIndex = index;
17916         this.view.select(index);
17917         if(scrollIntoView !== false){
17918             var el = this.view.getNode(index);
17919             /*
17920              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17921              */
17922             if(el){
17923                 this.list.scrollChildIntoView(el, false);
17924             }
17925         }
17926     },
17927
17928     // private
17929     selectNext : function(){
17930         var ct = this.store.getCount();
17931         if(ct > 0){
17932             if(this.selectedIndex == -1){
17933                 this.select(0);
17934             }else if(this.selectedIndex < ct-1){
17935                 this.select(this.selectedIndex+1);
17936             }
17937         }
17938     },
17939
17940     // private
17941     selectPrev : function(){
17942         var ct = this.store.getCount();
17943         if(ct > 0){
17944             if(this.selectedIndex == -1){
17945                 this.select(0);
17946             }else if(this.selectedIndex != 0){
17947                 this.select(this.selectedIndex-1);
17948             }
17949         }
17950     },
17951
17952     // private
17953     onKeyUp : function(e){
17954         if(this.editable !== false && !e.isSpecialKey()){
17955             this.lastKey = e.getKey();
17956             this.dqTask.delay(this.queryDelay);
17957         }
17958     },
17959
17960     // private
17961     validateBlur : function(){
17962         return !this.list || !this.list.isVisible();   
17963     },
17964
17965     // private
17966     initQuery : function(){
17967         
17968         var v = this.getRawValue();
17969         
17970         if(this.tickable && this.editable){
17971             v = this.tickableInputEl().getValue();
17972         }
17973         
17974         this.doQuery(v);
17975     },
17976
17977     // private
17978     doForce : function(){
17979         if(this.inputEl().dom.value.length > 0){
17980             this.inputEl().dom.value =
17981                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17982              
17983         }
17984     },
17985
17986     /**
17987      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17988      * query allowing the query action to be canceled if needed.
17989      * @param {String} query The SQL query to execute
17990      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17991      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17992      * saved in the current store (defaults to false)
17993      */
17994     doQuery : function(q, forceAll){
17995         
17996         if(q === undefined || q === null){
17997             q = '';
17998         }
17999         var qe = {
18000             query: q,
18001             forceAll: forceAll,
18002             combo: this,
18003             cancel:false
18004         };
18005         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18006             return false;
18007         }
18008         q = qe.query;
18009         
18010         forceAll = qe.forceAll;
18011         if(forceAll === true || (q.length >= this.minChars)){
18012             
18013             this.hasQuery = true;
18014             
18015             if(this.lastQuery != q || this.alwaysQuery){
18016                 this.lastQuery = q;
18017                 if(this.mode == 'local'){
18018                     this.selectedIndex = -1;
18019                     if(forceAll){
18020                         this.store.clearFilter();
18021                     }else{
18022                         
18023                         if(this.specialFilter){
18024                             this.fireEvent('specialfilter', this);
18025                             this.onLoad();
18026                             return;
18027                         }
18028                         
18029                         this.store.filter(this.displayField, q);
18030                     }
18031                     
18032                     this.store.fireEvent("datachanged", this.store);
18033                     
18034                     this.onLoad();
18035                     
18036                     
18037                 }else{
18038                     
18039                     this.store.baseParams[this.queryParam] = q;
18040                     
18041                     var options = {params : this.getParams(q)};
18042                     
18043                     if(this.loadNext){
18044                         options.add = true;
18045                         options.params.start = this.page * this.pageSize;
18046                     }
18047                     
18048                     this.store.load(options);
18049                     
18050                     /*
18051                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18052                      *  we should expand the list on onLoad
18053                      *  so command out it
18054                      */
18055 //                    this.expand();
18056                 }
18057             }else{
18058                 this.selectedIndex = -1;
18059                 this.onLoad();   
18060             }
18061         }
18062         
18063         this.loadNext = false;
18064     },
18065     
18066     // private
18067     getParams : function(q){
18068         var p = {};
18069         //p[this.queryParam] = q;
18070         
18071         if(this.pageSize){
18072             p.start = 0;
18073             p.limit = this.pageSize;
18074         }
18075         return p;
18076     },
18077
18078     /**
18079      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18080      */
18081     collapse : function(){
18082         if(!this.isExpanded()){
18083             return;
18084         }
18085         
18086         this.list.hide();
18087         
18088         this.hasFocus = false;
18089         
18090         if(this.tickable){
18091             this.okBtn.hide();
18092             this.cancelBtn.hide();
18093             this.trigger.show();
18094             
18095             if(this.editable){
18096                 this.tickableInputEl().dom.value = '';
18097                 this.tickableInputEl().blur();
18098             }
18099             
18100         }
18101         
18102         Roo.get(document).un('mousedown', this.collapseIf, this);
18103         Roo.get(document).un('mousewheel', this.collapseIf, this);
18104         if (!this.editable) {
18105             Roo.get(document).un('keydown', this.listKeyPress, this);
18106         }
18107         this.fireEvent('collapse', this);
18108         
18109         this.validate();
18110     },
18111
18112     // private
18113     collapseIf : function(e){
18114         var in_combo  = e.within(this.el);
18115         var in_list =  e.within(this.list);
18116         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18117         
18118         if (in_combo || in_list || is_list) {
18119             //e.stopPropagation();
18120             return;
18121         }
18122         
18123         if(this.tickable){
18124             this.onTickableFooterButtonClick(e, false, false);
18125         }
18126
18127         this.collapse();
18128         
18129     },
18130
18131     /**
18132      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18133      */
18134     expand : function(){
18135        
18136         if(this.isExpanded() || !this.hasFocus){
18137             return;
18138         }
18139         
18140         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18141         this.list.setWidth(lw);
18142         
18143         Roo.log('expand');
18144         
18145         this.list.show();
18146         
18147         this.restrictHeight();
18148         
18149         if(this.tickable){
18150             
18151             this.tickItems = Roo.apply([], this.item);
18152             
18153             this.okBtn.show();
18154             this.cancelBtn.show();
18155             this.trigger.hide();
18156             
18157             if(this.editable){
18158                 this.tickableInputEl().focus();
18159             }
18160             
18161         }
18162         
18163         Roo.get(document).on('mousedown', this.collapseIf, this);
18164         Roo.get(document).on('mousewheel', this.collapseIf, this);
18165         if (!this.editable) {
18166             Roo.get(document).on('keydown', this.listKeyPress, this);
18167         }
18168         
18169         this.fireEvent('expand', this);
18170     },
18171
18172     // private
18173     // Implements the default empty TriggerField.onTriggerClick function
18174     onTriggerClick : function(e)
18175     {
18176         Roo.log('trigger click');
18177         
18178         if(this.disabled || !this.triggerList){
18179             return;
18180         }
18181         
18182         this.page = 0;
18183         this.loadNext = false;
18184         
18185         if(this.isExpanded()){
18186             this.collapse();
18187             if (!this.blockFocus) {
18188                 this.inputEl().focus();
18189             }
18190             
18191         }else {
18192             this.hasFocus = true;
18193             if(this.triggerAction == 'all') {
18194                 this.doQuery(this.allQuery, true);
18195             } else {
18196                 this.doQuery(this.getRawValue());
18197             }
18198             if (!this.blockFocus) {
18199                 this.inputEl().focus();
18200             }
18201         }
18202     },
18203     
18204     onTickableTriggerClick : function(e)
18205     {
18206         if(this.disabled){
18207             return;
18208         }
18209         
18210         this.page = 0;
18211         this.loadNext = false;
18212         this.hasFocus = true;
18213         
18214         if(this.triggerAction == 'all') {
18215             this.doQuery(this.allQuery, true);
18216         } else {
18217             this.doQuery(this.getRawValue());
18218         }
18219     },
18220     
18221     onSearchFieldClick : function(e)
18222     {
18223         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18224             this.onTickableFooterButtonClick(e, false, false);
18225             return;
18226         }
18227         
18228         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18229             return;
18230         }
18231         
18232         this.page = 0;
18233         this.loadNext = false;
18234         this.hasFocus = true;
18235         
18236         if(this.triggerAction == 'all') {
18237             this.doQuery(this.allQuery, true);
18238         } else {
18239             this.doQuery(this.getRawValue());
18240         }
18241     },
18242     
18243     listKeyPress : function(e)
18244     {
18245         //Roo.log('listkeypress');
18246         // scroll to first matching element based on key pres..
18247         if (e.isSpecialKey()) {
18248             return false;
18249         }
18250         var k = String.fromCharCode(e.getKey()).toUpperCase();
18251         //Roo.log(k);
18252         var match  = false;
18253         var csel = this.view.getSelectedNodes();
18254         var cselitem = false;
18255         if (csel.length) {
18256             var ix = this.view.indexOf(csel[0]);
18257             cselitem  = this.store.getAt(ix);
18258             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18259                 cselitem = false;
18260             }
18261             
18262         }
18263         
18264         this.store.each(function(v) { 
18265             if (cselitem) {
18266                 // start at existing selection.
18267                 if (cselitem.id == v.id) {
18268                     cselitem = false;
18269                 }
18270                 return true;
18271             }
18272                 
18273             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18274                 match = this.store.indexOf(v);
18275                 return false;
18276             }
18277             return true;
18278         }, this);
18279         
18280         if (match === false) {
18281             return true; // no more action?
18282         }
18283         // scroll to?
18284         this.view.select(match);
18285         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18286         sn.scrollIntoView(sn.dom.parentNode, false);
18287     },
18288     
18289     onViewScroll : function(e, t){
18290         
18291         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){
18292             return;
18293         }
18294         
18295         this.hasQuery = true;
18296         
18297         this.loading = this.list.select('.loading', true).first();
18298         
18299         if(this.loading === null){
18300             this.list.createChild({
18301                 tag: 'div',
18302                 cls: 'loading roo-select2-more-results roo-select2-active',
18303                 html: 'Loading more results...'
18304             });
18305             
18306             this.loading = this.list.select('.loading', true).first();
18307             
18308             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18309             
18310             this.loading.hide();
18311         }
18312         
18313         this.loading.show();
18314         
18315         var _combo = this;
18316         
18317         this.page++;
18318         this.loadNext = true;
18319         
18320         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18321         
18322         return;
18323     },
18324     
18325     addItem : function(o)
18326     {   
18327         var dv = ''; // display value
18328         
18329         if (this.displayField) {
18330             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18331         } else {
18332             // this is an error condition!!!
18333             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18334         }
18335         
18336         if(!dv.length){
18337             return;
18338         }
18339         
18340         var choice = this.choices.createChild({
18341             tag: 'li',
18342             cls: 'roo-select2-search-choice',
18343             cn: [
18344                 {
18345                     tag: 'div',
18346                     html: dv
18347                 },
18348                 {
18349                     tag: 'a',
18350                     href: '#',
18351                     cls: 'roo-select2-search-choice-close fa fa-times',
18352                     tabindex: '-1'
18353                 }
18354             ]
18355             
18356         }, this.searchField);
18357         
18358         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18359         
18360         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18361         
18362         this.item.push(o);
18363         
18364         this.lastData = o;
18365         
18366         this.syncValue();
18367         
18368         this.inputEl().dom.value = '';
18369         
18370         this.validate();
18371     },
18372     
18373     onRemoveItem : function(e, _self, o)
18374     {
18375         e.preventDefault();
18376         
18377         this.lastItem = Roo.apply([], this.item);
18378         
18379         var index = this.item.indexOf(o.data) * 1;
18380         
18381         if( index < 0){
18382             Roo.log('not this item?!');
18383             return;
18384         }
18385         
18386         this.item.splice(index, 1);
18387         o.item.remove();
18388         
18389         this.syncValue();
18390         
18391         this.fireEvent('remove', this, e);
18392         
18393         this.validate();
18394         
18395     },
18396     
18397     syncValue : function()
18398     {
18399         if(!this.item.length){
18400             this.clearValue();
18401             return;
18402         }
18403             
18404         var value = [];
18405         var _this = this;
18406         Roo.each(this.item, function(i){
18407             if(_this.valueField){
18408                 value.push(i[_this.valueField]);
18409                 return;
18410             }
18411
18412             value.push(i);
18413         });
18414
18415         this.value = value.join(',');
18416
18417         if(this.hiddenField){
18418             this.hiddenField.dom.value = this.value;
18419         }
18420         
18421         this.store.fireEvent("datachanged", this.store);
18422         
18423         this.validate();
18424     },
18425     
18426     clearItem : function()
18427     {
18428         if(!this.multiple){
18429             return;
18430         }
18431         
18432         this.item = [];
18433         
18434         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18435            c.remove();
18436         });
18437         
18438         this.syncValue();
18439         
18440         this.validate();
18441         
18442         if(this.tickable && !Roo.isTouch){
18443             this.view.refresh();
18444         }
18445     },
18446     
18447     inputEl: function ()
18448     {
18449         if(Roo.isIOS && this.useNativeIOS){
18450             return this.el.select('select.roo-ios-select', true).first();
18451         }
18452         
18453         if(Roo.isTouch && this.mobileTouchView){
18454             return this.el.select('input.form-control',true).first();
18455         }
18456         
18457         if(this.tickable){
18458             return this.searchField;
18459         }
18460         
18461         return this.el.select('input.form-control',true).first();
18462     },
18463     
18464     onTickableFooterButtonClick : function(e, btn, el)
18465     {
18466         e.preventDefault();
18467         
18468         this.lastItem = Roo.apply([], this.item);
18469         
18470         if(btn && btn.name == 'cancel'){
18471             this.tickItems = Roo.apply([], this.item);
18472             this.collapse();
18473             return;
18474         }
18475         
18476         this.clearItem();
18477         
18478         var _this = this;
18479         
18480         Roo.each(this.tickItems, function(o){
18481             _this.addItem(o);
18482         });
18483         
18484         this.collapse();
18485         
18486     },
18487     
18488     validate : function()
18489     {
18490         if(this.getVisibilityEl().hasClass('hidden')){
18491             return true;
18492         }
18493         
18494         var v = this.getRawValue();
18495         
18496         if(this.multiple){
18497             v = this.getValue();
18498         }
18499         
18500         if(this.disabled || this.allowBlank || v.length){
18501             this.markValid();
18502             return true;
18503         }
18504         
18505         this.markInvalid();
18506         return false;
18507     },
18508     
18509     tickableInputEl : function()
18510     {
18511         if(!this.tickable || !this.editable){
18512             return this.inputEl();
18513         }
18514         
18515         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18516     },
18517     
18518     
18519     getAutoCreateTouchView : function()
18520     {
18521         var id = Roo.id();
18522         
18523         var cfg = {
18524             cls: 'form-group' //input-group
18525         };
18526         
18527         var input =  {
18528             tag: 'input',
18529             id : id,
18530             type : this.inputType,
18531             cls : 'form-control x-combo-noedit',
18532             autocomplete: 'new-password',
18533             placeholder : this.placeholder || '',
18534             readonly : true
18535         };
18536         
18537         if (this.name) {
18538             input.name = this.name;
18539         }
18540         
18541         if (this.size) {
18542             input.cls += ' input-' + this.size;
18543         }
18544         
18545         if (this.disabled) {
18546             input.disabled = true;
18547         }
18548         
18549         var inputblock = {
18550             cls : 'roo-combobox-wrap',
18551             cn : [
18552                 input
18553             ]
18554         };
18555         
18556         if(this.before){
18557             inputblock.cls += ' input-group';
18558             
18559             inputblock.cn.unshift({
18560                 tag :'span',
18561                 cls : 'input-group-addon input-group-prepend input-group-text',
18562                 html : this.before
18563             });
18564         }
18565         
18566         if(this.removable && !this.multiple){
18567             inputblock.cls += ' roo-removable';
18568             
18569             inputblock.cn.push({
18570                 tag: 'button',
18571                 html : 'x',
18572                 cls : 'roo-combo-removable-btn close'
18573             });
18574         }
18575
18576         if(this.hasFeedback && !this.allowBlank){
18577             
18578             inputblock.cls += ' has-feedback';
18579             
18580             inputblock.cn.push({
18581                 tag: 'span',
18582                 cls: 'glyphicon form-control-feedback'
18583             });
18584             
18585         }
18586         
18587         if (this.after) {
18588             
18589             inputblock.cls += (this.before) ? '' : ' input-group';
18590             
18591             inputblock.cn.push({
18592                 tag :'span',
18593                 cls : 'input-group-addon input-group-append input-group-text',
18594                 html : this.after
18595             });
18596         }
18597
18598         
18599         var ibwrap = inputblock;
18600         
18601         if(this.multiple){
18602             ibwrap = {
18603                 tag: 'ul',
18604                 cls: 'roo-select2-choices',
18605                 cn:[
18606                     {
18607                         tag: 'li',
18608                         cls: 'roo-select2-search-field',
18609                         cn: [
18610
18611                             inputblock
18612                         ]
18613                     }
18614                 ]
18615             };
18616         
18617             
18618         }
18619         
18620         var combobox = {
18621             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18622             cn: [
18623                 {
18624                     tag: 'input',
18625                     type : 'hidden',
18626                     cls: 'form-hidden-field'
18627                 },
18628                 ibwrap
18629             ]
18630         };
18631         
18632         if(!this.multiple && this.showToggleBtn){
18633             
18634             var caret = {
18635                 cls: 'caret'
18636             };
18637             
18638             if (this.caret != false) {
18639                 caret = {
18640                      tag: 'i',
18641                      cls: 'fa fa-' + this.caret
18642                 };
18643                 
18644             }
18645             
18646             combobox.cn.push({
18647                 tag :'span',
18648                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18649                 cn : [
18650                     Roo.bootstrap.version == 3 ? caret : '',
18651                     {
18652                         tag: 'span',
18653                         cls: 'combobox-clear',
18654                         cn  : [
18655                             {
18656                                 tag : 'i',
18657                                 cls: 'icon-remove'
18658                             }
18659                         ]
18660                     }
18661                 ]
18662
18663             })
18664         }
18665         
18666         if(this.multiple){
18667             combobox.cls += ' roo-select2-container-multi';
18668         }
18669         
18670         var required =  this.allowBlank ?  {
18671                     tag : 'i',
18672                     style: 'display: none'
18673                 } : {
18674                    tag : 'i',
18675                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18676                    tooltip : 'This field is required'
18677                 };
18678         
18679         var align = this.labelAlign || this.parentLabelAlign();
18680         
18681         if (align ==='left' && this.fieldLabel.length) {
18682
18683             cfg.cn = [
18684                 required,
18685                 {
18686                     tag: 'label',
18687                     cls : 'control-label col-form-label',
18688                     html : this.fieldLabel
18689
18690                 },
18691                 {
18692                     cls : 'roo-combobox-wrap ', 
18693                     cn: [
18694                         combobox
18695                     ]
18696                 }
18697             ];
18698             
18699             var labelCfg = cfg.cn[1];
18700             var contentCfg = cfg.cn[2];
18701             
18702
18703             if(this.indicatorpos == 'right'){
18704                 cfg.cn = [
18705                     {
18706                         tag: 'label',
18707                         'for' :  id,
18708                         cls : 'control-label col-form-label',
18709                         cn : [
18710                             {
18711                                 tag : 'span',
18712                                 html : this.fieldLabel
18713                             },
18714                             required
18715                         ]
18716                     },
18717                     {
18718                         cls : "roo-combobox-wrap ",
18719                         cn: [
18720                             combobox
18721                         ]
18722                     }
18723
18724                 ];
18725                 
18726                 labelCfg = cfg.cn[0];
18727                 contentCfg = cfg.cn[1];
18728             }
18729             
18730            
18731             
18732             if(this.labelWidth > 12){
18733                 labelCfg.style = "width: " + this.labelWidth + 'px';
18734             }
18735            
18736             if(this.labelWidth < 13 && this.labelmd == 0){
18737                 this.labelmd = this.labelWidth;
18738             }
18739             
18740             if(this.labellg > 0){
18741                 labelCfg.cls += ' col-lg-' + this.labellg;
18742                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18743             }
18744             
18745             if(this.labelmd > 0){
18746                 labelCfg.cls += ' col-md-' + this.labelmd;
18747                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18748             }
18749             
18750             if(this.labelsm > 0){
18751                 labelCfg.cls += ' col-sm-' + this.labelsm;
18752                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18753             }
18754             
18755             if(this.labelxs > 0){
18756                 labelCfg.cls += ' col-xs-' + this.labelxs;
18757                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18758             }
18759                 
18760                 
18761         } else if ( this.fieldLabel.length) {
18762             cfg.cn = [
18763                required,
18764                 {
18765                     tag: 'label',
18766                     cls : 'control-label',
18767                     html : this.fieldLabel
18768
18769                 },
18770                 {
18771                     cls : '', 
18772                     cn: [
18773                         combobox
18774                     ]
18775                 }
18776             ];
18777             
18778             if(this.indicatorpos == 'right'){
18779                 cfg.cn = [
18780                     {
18781                         tag: 'label',
18782                         cls : 'control-label',
18783                         html : this.fieldLabel,
18784                         cn : [
18785                             required
18786                         ]
18787                     },
18788                     {
18789                         cls : '', 
18790                         cn: [
18791                             combobox
18792                         ]
18793                     }
18794                 ];
18795             }
18796         } else {
18797             cfg.cn = combobox;    
18798         }
18799         
18800         
18801         var settings = this;
18802         
18803         ['xs','sm','md','lg'].map(function(size){
18804             if (settings[size]) {
18805                 cfg.cls += ' col-' + size + '-' + settings[size];
18806             }
18807         });
18808         
18809         return cfg;
18810     },
18811     
18812     initTouchView : function()
18813     {
18814         this.renderTouchView();
18815         
18816         this.touchViewEl.on('scroll', function(){
18817             this.el.dom.scrollTop = 0;
18818         }, this);
18819         
18820         this.originalValue = this.getValue();
18821         
18822         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18823         
18824         this.inputEl().on("click", this.showTouchView, this);
18825         if (this.triggerEl) {
18826             this.triggerEl.on("click", this.showTouchView, this);
18827         }
18828         
18829         
18830         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18831         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18832         
18833         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18834         
18835         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18836         this.store.on('load', this.onTouchViewLoad, this);
18837         this.store.on('loadexception', this.onTouchViewLoadException, this);
18838         
18839         if(this.hiddenName){
18840             
18841             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18842             
18843             this.hiddenField.dom.value =
18844                 this.hiddenValue !== undefined ? this.hiddenValue :
18845                 this.value !== undefined ? this.value : '';
18846         
18847             this.el.dom.removeAttribute('name');
18848             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18849         }
18850         
18851         if(this.multiple){
18852             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18853             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18854         }
18855         
18856         if(this.removable && !this.multiple){
18857             var close = this.closeTriggerEl();
18858             if(close){
18859                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18860                 close.on('click', this.removeBtnClick, this, close);
18861             }
18862         }
18863         /*
18864          * fix the bug in Safari iOS8
18865          */
18866         this.inputEl().on("focus", function(e){
18867             document.activeElement.blur();
18868         }, this);
18869         
18870         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18871         
18872         return;
18873         
18874         
18875     },
18876     
18877     renderTouchView : function()
18878     {
18879         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18880         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18881         
18882         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18883         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18884         
18885         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18886         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18887         this.touchViewBodyEl.setStyle('overflow', 'auto');
18888         
18889         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18890         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18891         
18892         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18893         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18894         
18895     },
18896     
18897     showTouchView : function()
18898     {
18899         if(this.disabled){
18900             return;
18901         }
18902         
18903         this.touchViewHeaderEl.hide();
18904
18905         if(this.modalTitle.length){
18906             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18907             this.touchViewHeaderEl.show();
18908         }
18909
18910         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18911         this.touchViewEl.show();
18912
18913         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18914         
18915         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18916         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18917
18918         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18919
18920         if(this.modalTitle.length){
18921             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18922         }
18923         
18924         this.touchViewBodyEl.setHeight(bodyHeight);
18925
18926         if(this.animate){
18927             var _this = this;
18928             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18929         }else{
18930             this.touchViewEl.addClass(['in','show']);
18931         }
18932         
18933         if(this._touchViewMask){
18934             Roo.get(document.body).addClass("x-body-masked");
18935             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18936             this._touchViewMask.setStyle('z-index', 10000);
18937             this._touchViewMask.addClass('show');
18938         }
18939         
18940         this.doTouchViewQuery();
18941         
18942     },
18943     
18944     hideTouchView : function()
18945     {
18946         this.touchViewEl.removeClass(['in','show']);
18947
18948         if(this.animate){
18949             var _this = this;
18950             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18951         }else{
18952             this.touchViewEl.setStyle('display', 'none');
18953         }
18954         
18955         if(this._touchViewMask){
18956             this._touchViewMask.removeClass('show');
18957             Roo.get(document.body).removeClass("x-body-masked");
18958         }
18959     },
18960     
18961     setTouchViewValue : function()
18962     {
18963         if(this.multiple){
18964             this.clearItem();
18965         
18966             var _this = this;
18967
18968             Roo.each(this.tickItems, function(o){
18969                 this.addItem(o);
18970             }, this);
18971         }
18972         
18973         this.hideTouchView();
18974     },
18975     
18976     doTouchViewQuery : function()
18977     {
18978         var qe = {
18979             query: '',
18980             forceAll: true,
18981             combo: this,
18982             cancel:false
18983         };
18984         
18985         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18986             return false;
18987         }
18988         
18989         if(!this.alwaysQuery || this.mode == 'local'){
18990             this.onTouchViewLoad();
18991             return;
18992         }
18993         
18994         this.store.load();
18995     },
18996     
18997     onTouchViewBeforeLoad : function(combo,opts)
18998     {
18999         return;
19000     },
19001
19002     // private
19003     onTouchViewLoad : function()
19004     {
19005         if(this.store.getCount() < 1){
19006             this.onTouchViewEmptyResults();
19007             return;
19008         }
19009         
19010         this.clearTouchView();
19011         
19012         var rawValue = this.getRawValue();
19013         
19014         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19015         
19016         this.tickItems = [];
19017         
19018         this.store.data.each(function(d, rowIndex){
19019             var row = this.touchViewListGroup.createChild(template);
19020             
19021             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19022                 row.addClass(d.data.cls);
19023             }
19024             
19025             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19026                 var cfg = {
19027                     data : d.data,
19028                     html : d.data[this.displayField]
19029                 };
19030                 
19031                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19032                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19033                 }
19034             }
19035             row.removeClass('selected');
19036             if(!this.multiple && this.valueField &&
19037                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19038             {
19039                 // radio buttons..
19040                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19041                 row.addClass('selected');
19042             }
19043             
19044             if(this.multiple && this.valueField &&
19045                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19046             {
19047                 
19048                 // checkboxes...
19049                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19050                 this.tickItems.push(d.data);
19051             }
19052             
19053             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19054             
19055         }, this);
19056         
19057         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19058         
19059         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19060
19061         if(this.modalTitle.length){
19062             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19063         }
19064
19065         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19066         
19067         if(this.mobile_restrict_height && listHeight < bodyHeight){
19068             this.touchViewBodyEl.setHeight(listHeight);
19069         }
19070         
19071         var _this = this;
19072         
19073         if(firstChecked && listHeight > bodyHeight){
19074             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19075         }
19076         
19077     },
19078     
19079     onTouchViewLoadException : function()
19080     {
19081         this.hideTouchView();
19082     },
19083     
19084     onTouchViewEmptyResults : function()
19085     {
19086         this.clearTouchView();
19087         
19088         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19089         
19090         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19091         
19092     },
19093     
19094     clearTouchView : function()
19095     {
19096         this.touchViewListGroup.dom.innerHTML = '';
19097     },
19098     
19099     onTouchViewClick : function(e, el, o)
19100     {
19101         e.preventDefault();
19102         
19103         var row = o.row;
19104         var rowIndex = o.rowIndex;
19105         
19106         var r = this.store.getAt(rowIndex);
19107         
19108         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19109             
19110             if(!this.multiple){
19111                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19112                     c.dom.removeAttribute('checked');
19113                 }, this);
19114
19115                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19116
19117                 this.setFromData(r.data);
19118
19119                 var close = this.closeTriggerEl();
19120
19121                 if(close){
19122                     close.show();
19123                 }
19124
19125                 this.hideTouchView();
19126
19127                 this.fireEvent('select', this, r, rowIndex);
19128
19129                 return;
19130             }
19131
19132             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19133                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19134                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19135                 return;
19136             }
19137
19138             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19139             this.addItem(r.data);
19140             this.tickItems.push(r.data);
19141         }
19142     },
19143     
19144     getAutoCreateNativeIOS : function()
19145     {
19146         var cfg = {
19147             cls: 'form-group' //input-group,
19148         };
19149         
19150         var combobox =  {
19151             tag: 'select',
19152             cls : 'roo-ios-select'
19153         };
19154         
19155         if (this.name) {
19156             combobox.name = this.name;
19157         }
19158         
19159         if (this.disabled) {
19160             combobox.disabled = true;
19161         }
19162         
19163         var settings = this;
19164         
19165         ['xs','sm','md','lg'].map(function(size){
19166             if (settings[size]) {
19167                 cfg.cls += ' col-' + size + '-' + settings[size];
19168             }
19169         });
19170         
19171         cfg.cn = combobox;
19172         
19173         return cfg;
19174         
19175     },
19176     
19177     initIOSView : function()
19178     {
19179         this.store.on('load', this.onIOSViewLoad, this);
19180         
19181         return;
19182     },
19183     
19184     onIOSViewLoad : function()
19185     {
19186         if(this.store.getCount() < 1){
19187             return;
19188         }
19189         
19190         this.clearIOSView();
19191         
19192         if(this.allowBlank) {
19193             
19194             var default_text = '-- SELECT --';
19195             
19196             if(this.placeholder.length){
19197                 default_text = this.placeholder;
19198             }
19199             
19200             if(this.emptyTitle.length){
19201                 default_text += ' - ' + this.emptyTitle + ' -';
19202             }
19203             
19204             var opt = this.inputEl().createChild({
19205                 tag: 'option',
19206                 value : 0,
19207                 html : default_text
19208             });
19209             
19210             var o = {};
19211             o[this.valueField] = 0;
19212             o[this.displayField] = default_text;
19213             
19214             this.ios_options.push({
19215                 data : o,
19216                 el : opt
19217             });
19218             
19219         }
19220         
19221         this.store.data.each(function(d, rowIndex){
19222             
19223             var html = '';
19224             
19225             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19226                 html = d.data[this.displayField];
19227             }
19228             
19229             var value = '';
19230             
19231             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19232                 value = d.data[this.valueField];
19233             }
19234             
19235             var option = {
19236                 tag: 'option',
19237                 value : value,
19238                 html : html
19239             };
19240             
19241             if(this.value == d.data[this.valueField]){
19242                 option['selected'] = true;
19243             }
19244             
19245             var opt = this.inputEl().createChild(option);
19246             
19247             this.ios_options.push({
19248                 data : d.data,
19249                 el : opt
19250             });
19251             
19252         }, this);
19253         
19254         this.inputEl().on('change', function(){
19255            this.fireEvent('select', this);
19256         }, this);
19257         
19258     },
19259     
19260     clearIOSView: function()
19261     {
19262         this.inputEl().dom.innerHTML = '';
19263         
19264         this.ios_options = [];
19265     },
19266     
19267     setIOSValue: function(v)
19268     {
19269         this.value = v;
19270         
19271         if(!this.ios_options){
19272             return;
19273         }
19274         
19275         Roo.each(this.ios_options, function(opts){
19276            
19277            opts.el.dom.removeAttribute('selected');
19278            
19279            if(opts.data[this.valueField] != v){
19280                return;
19281            }
19282            
19283            opts.el.dom.setAttribute('selected', true);
19284            
19285         }, this);
19286     }
19287
19288     /** 
19289     * @cfg {Boolean} grow 
19290     * @hide 
19291     */
19292     /** 
19293     * @cfg {Number} growMin 
19294     * @hide 
19295     */
19296     /** 
19297     * @cfg {Number} growMax 
19298     * @hide 
19299     */
19300     /**
19301      * @hide
19302      * @method autoSize
19303      */
19304 });
19305
19306 Roo.apply(Roo.bootstrap.ComboBox,  {
19307     
19308     header : {
19309         tag: 'div',
19310         cls: 'modal-header',
19311         cn: [
19312             {
19313                 tag: 'h4',
19314                 cls: 'modal-title'
19315             }
19316         ]
19317     },
19318     
19319     body : {
19320         tag: 'div',
19321         cls: 'modal-body',
19322         cn: [
19323             {
19324                 tag: 'ul',
19325                 cls: 'list-group'
19326             }
19327         ]
19328     },
19329     
19330     listItemRadio : {
19331         tag: 'li',
19332         cls: 'list-group-item',
19333         cn: [
19334             {
19335                 tag: 'span',
19336                 cls: 'roo-combobox-list-group-item-value'
19337             },
19338             {
19339                 tag: 'div',
19340                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19341                 cn: [
19342                     {
19343                         tag: 'input',
19344                         type: 'radio'
19345                     },
19346                     {
19347                         tag: 'label'
19348                     }
19349                 ]
19350             }
19351         ]
19352     },
19353     
19354     listItemCheckbox : {
19355         tag: 'li',
19356         cls: 'list-group-item',
19357         cn: [
19358             {
19359                 tag: 'span',
19360                 cls: 'roo-combobox-list-group-item-value'
19361             },
19362             {
19363                 tag: 'div',
19364                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19365                 cn: [
19366                     {
19367                         tag: 'input',
19368                         type: 'checkbox'
19369                     },
19370                     {
19371                         tag: 'label'
19372                     }
19373                 ]
19374             }
19375         ]
19376     },
19377     
19378     emptyResult : {
19379         tag: 'div',
19380         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19381     },
19382     
19383     footer : {
19384         tag: 'div',
19385         cls: 'modal-footer',
19386         cn: [
19387             {
19388                 tag: 'div',
19389                 cls: 'row',
19390                 cn: [
19391                     {
19392                         tag: 'div',
19393                         cls: 'col-xs-6 text-left',
19394                         cn: {
19395                             tag: 'button',
19396                             cls: 'btn btn-danger roo-touch-view-cancel',
19397                             html: 'Cancel'
19398                         }
19399                     },
19400                     {
19401                         tag: 'div',
19402                         cls: 'col-xs-6 text-right',
19403                         cn: {
19404                             tag: 'button',
19405                             cls: 'btn btn-success roo-touch-view-ok',
19406                             html: 'OK'
19407                         }
19408                     }
19409                 ]
19410             }
19411         ]
19412         
19413     }
19414 });
19415
19416 Roo.apply(Roo.bootstrap.ComboBox,  {
19417     
19418     touchViewTemplate : {
19419         tag: 'div',
19420         cls: 'modal fade roo-combobox-touch-view',
19421         cn: [
19422             {
19423                 tag: 'div',
19424                 cls: 'modal-dialog',
19425                 style : 'position:fixed', // we have to fix position....
19426                 cn: [
19427                     {
19428                         tag: 'div',
19429                         cls: 'modal-content',
19430                         cn: [
19431                             Roo.bootstrap.ComboBox.header,
19432                             Roo.bootstrap.ComboBox.body,
19433                             Roo.bootstrap.ComboBox.footer
19434                         ]
19435                     }
19436                 ]
19437             }
19438         ]
19439     }
19440 });/*
19441  * Based on:
19442  * Ext JS Library 1.1.1
19443  * Copyright(c) 2006-2007, Ext JS, LLC.
19444  *
19445  * Originally Released Under LGPL - original licence link has changed is not relivant.
19446  *
19447  * Fork - LGPL
19448  * <script type="text/javascript">
19449  */
19450
19451 /**
19452  * @class Roo.View
19453  * @extends Roo.util.Observable
19454  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19455  * This class also supports single and multi selection modes. <br>
19456  * Create a data model bound view:
19457  <pre><code>
19458  var store = new Roo.data.Store(...);
19459
19460  var view = new Roo.View({
19461     el : "my-element",
19462     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19463  
19464     singleSelect: true,
19465     selectedClass: "ydataview-selected",
19466     store: store
19467  });
19468
19469  // listen for node click?
19470  view.on("click", function(vw, index, node, e){
19471  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19472  });
19473
19474  // load XML data
19475  dataModel.load("foobar.xml");
19476  </code></pre>
19477  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19478  * <br><br>
19479  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19480  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19481  * 
19482  * Note: old style constructor is still suported (container, template, config)
19483  * 
19484  * @constructor
19485  * Create a new View
19486  * @param {Object} config The config object
19487  * 
19488  */
19489 Roo.View = function(config, depreciated_tpl, depreciated_config){
19490     
19491     this.parent = false;
19492     
19493     if (typeof(depreciated_tpl) == 'undefined') {
19494         // new way.. - universal constructor.
19495         Roo.apply(this, config);
19496         this.el  = Roo.get(this.el);
19497     } else {
19498         // old format..
19499         this.el  = Roo.get(config);
19500         this.tpl = depreciated_tpl;
19501         Roo.apply(this, depreciated_config);
19502     }
19503     this.wrapEl  = this.el.wrap().wrap();
19504     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19505     
19506     
19507     if(typeof(this.tpl) == "string"){
19508         this.tpl = new Roo.Template(this.tpl);
19509     } else {
19510         // support xtype ctors..
19511         this.tpl = new Roo.factory(this.tpl, Roo);
19512     }
19513     
19514     
19515     this.tpl.compile();
19516     
19517     /** @private */
19518     this.addEvents({
19519         /**
19520          * @event beforeclick
19521          * Fires before a click is processed. Returns false to cancel the default action.
19522          * @param {Roo.View} this
19523          * @param {Number} index The index of the target node
19524          * @param {HTMLElement} node The target node
19525          * @param {Roo.EventObject} e The raw event object
19526          */
19527             "beforeclick" : true,
19528         /**
19529          * @event click
19530          * Fires when a template node is clicked.
19531          * @param {Roo.View} this
19532          * @param {Number} index The index of the target node
19533          * @param {HTMLElement} node The target node
19534          * @param {Roo.EventObject} e The raw event object
19535          */
19536             "click" : true,
19537         /**
19538          * @event dblclick
19539          * Fires when a template node is double clicked.
19540          * @param {Roo.View} this
19541          * @param {Number} index The index of the target node
19542          * @param {HTMLElement} node The target node
19543          * @param {Roo.EventObject} e The raw event object
19544          */
19545             "dblclick" : true,
19546         /**
19547          * @event contextmenu
19548          * Fires when a template node is right clicked.
19549          * @param {Roo.View} this
19550          * @param {Number} index The index of the target node
19551          * @param {HTMLElement} node The target node
19552          * @param {Roo.EventObject} e The raw event object
19553          */
19554             "contextmenu" : true,
19555         /**
19556          * @event selectionchange
19557          * Fires when the selected nodes change.
19558          * @param {Roo.View} this
19559          * @param {Array} selections Array of the selected nodes
19560          */
19561             "selectionchange" : true,
19562     
19563         /**
19564          * @event beforeselect
19565          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19566          * @param {Roo.View} this
19567          * @param {HTMLElement} node The node to be selected
19568          * @param {Array} selections Array of currently selected nodes
19569          */
19570             "beforeselect" : true,
19571         /**
19572          * @event preparedata
19573          * Fires on every row to render, to allow you to change the data.
19574          * @param {Roo.View} this
19575          * @param {Object} data to be rendered (change this)
19576          */
19577           "preparedata" : true
19578           
19579           
19580         });
19581
19582
19583
19584     this.el.on({
19585         "click": this.onClick,
19586         "dblclick": this.onDblClick,
19587         "contextmenu": this.onContextMenu,
19588         scope:this
19589     });
19590
19591     this.selections = [];
19592     this.nodes = [];
19593     this.cmp = new Roo.CompositeElementLite([]);
19594     if(this.store){
19595         this.store = Roo.factory(this.store, Roo.data);
19596         this.setStore(this.store, true);
19597     }
19598     
19599     if ( this.footer && this.footer.xtype) {
19600            
19601          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19602         
19603         this.footer.dataSource = this.store;
19604         this.footer.container = fctr;
19605         this.footer = Roo.factory(this.footer, Roo);
19606         fctr.insertFirst(this.el);
19607         
19608         // this is a bit insane - as the paging toolbar seems to detach the el..
19609 //        dom.parentNode.parentNode.parentNode
19610          // they get detached?
19611     }
19612     
19613     
19614     Roo.View.superclass.constructor.call(this);
19615     
19616     
19617 };
19618
19619 Roo.extend(Roo.View, Roo.util.Observable, {
19620     
19621      /**
19622      * @cfg {Roo.data.Store} store Data store to load data from.
19623      */
19624     store : false,
19625     
19626     /**
19627      * @cfg {String|Roo.Element} el The container element.
19628      */
19629     el : '',
19630     
19631     /**
19632      * @cfg {String|Roo.Template} tpl The template used by this View 
19633      */
19634     tpl : false,
19635     /**
19636      * @cfg {String} dataName the named area of the template to use as the data area
19637      *                          Works with domtemplates roo-name="name"
19638      */
19639     dataName: false,
19640     /**
19641      * @cfg {String} selectedClass The css class to add to selected nodes
19642      */
19643     selectedClass : "x-view-selected",
19644      /**
19645      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19646      */
19647     emptyText : "",
19648     
19649     /**
19650      * @cfg {String} text to display on mask (default Loading)
19651      */
19652     mask : false,
19653     /**
19654      * @cfg {Boolean} multiSelect Allow multiple selection
19655      */
19656     multiSelect : false,
19657     /**
19658      * @cfg {Boolean} singleSelect Allow single selection
19659      */
19660     singleSelect:  false,
19661     
19662     /**
19663      * @cfg {Boolean} toggleSelect - selecting 
19664      */
19665     toggleSelect : false,
19666     
19667     /**
19668      * @cfg {Boolean} tickable - selecting 
19669      */
19670     tickable : false,
19671     
19672     /**
19673      * Returns the element this view is bound to.
19674      * @return {Roo.Element}
19675      */
19676     getEl : function(){
19677         return this.wrapEl;
19678     },
19679     
19680     
19681
19682     /**
19683      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19684      */
19685     refresh : function(){
19686         //Roo.log('refresh');
19687         var t = this.tpl;
19688         
19689         // if we are using something like 'domtemplate', then
19690         // the what gets used is:
19691         // t.applySubtemplate(NAME, data, wrapping data..)
19692         // the outer template then get' applied with
19693         //     the store 'extra data'
19694         // and the body get's added to the
19695         //      roo-name="data" node?
19696         //      <span class='roo-tpl-{name}'></span> ?????
19697         
19698         
19699         
19700         this.clearSelections();
19701         this.el.update("");
19702         var html = [];
19703         var records = this.store.getRange();
19704         if(records.length < 1) {
19705             
19706             // is this valid??  = should it render a template??
19707             
19708             this.el.update(this.emptyText);
19709             return;
19710         }
19711         var el = this.el;
19712         if (this.dataName) {
19713             this.el.update(t.apply(this.store.meta)); //????
19714             el = this.el.child('.roo-tpl-' + this.dataName);
19715         }
19716         
19717         for(var i = 0, len = records.length; i < len; i++){
19718             var data = this.prepareData(records[i].data, i, records[i]);
19719             this.fireEvent("preparedata", this, data, i, records[i]);
19720             
19721             var d = Roo.apply({}, data);
19722             
19723             if(this.tickable){
19724                 Roo.apply(d, {'roo-id' : Roo.id()});
19725                 
19726                 var _this = this;
19727             
19728                 Roo.each(this.parent.item, function(item){
19729                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19730                         return;
19731                     }
19732                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19733                 });
19734             }
19735             
19736             html[html.length] = Roo.util.Format.trim(
19737                 this.dataName ?
19738                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19739                     t.apply(d)
19740             );
19741         }
19742         
19743         
19744         
19745         el.update(html.join(""));
19746         this.nodes = el.dom.childNodes;
19747         this.updateIndexes(0);
19748     },
19749     
19750
19751     /**
19752      * Function to override to reformat the data that is sent to
19753      * the template for each node.
19754      * DEPRICATED - use the preparedata event handler.
19755      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19756      * a JSON object for an UpdateManager bound view).
19757      */
19758     prepareData : function(data, index, record)
19759     {
19760         this.fireEvent("preparedata", this, data, index, record);
19761         return data;
19762     },
19763
19764     onUpdate : function(ds, record){
19765         // Roo.log('on update');   
19766         this.clearSelections();
19767         var index = this.store.indexOf(record);
19768         var n = this.nodes[index];
19769         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19770         n.parentNode.removeChild(n);
19771         this.updateIndexes(index, index);
19772     },
19773
19774     
19775     
19776 // --------- FIXME     
19777     onAdd : function(ds, records, index)
19778     {
19779         //Roo.log(['on Add', ds, records, index] );        
19780         this.clearSelections();
19781         if(this.nodes.length == 0){
19782             this.refresh();
19783             return;
19784         }
19785         var n = this.nodes[index];
19786         for(var i = 0, len = records.length; i < len; i++){
19787             var d = this.prepareData(records[i].data, i, records[i]);
19788             if(n){
19789                 this.tpl.insertBefore(n, d);
19790             }else{
19791                 
19792                 this.tpl.append(this.el, d);
19793             }
19794         }
19795         this.updateIndexes(index);
19796     },
19797
19798     onRemove : function(ds, record, index){
19799        // Roo.log('onRemove');
19800         this.clearSelections();
19801         var el = this.dataName  ?
19802             this.el.child('.roo-tpl-' + this.dataName) :
19803             this.el; 
19804         
19805         el.dom.removeChild(this.nodes[index]);
19806         this.updateIndexes(index);
19807     },
19808
19809     /**
19810      * Refresh an individual node.
19811      * @param {Number} index
19812      */
19813     refreshNode : function(index){
19814         this.onUpdate(this.store, this.store.getAt(index));
19815     },
19816
19817     updateIndexes : function(startIndex, endIndex){
19818         var ns = this.nodes;
19819         startIndex = startIndex || 0;
19820         endIndex = endIndex || ns.length - 1;
19821         for(var i = startIndex; i <= endIndex; i++){
19822             ns[i].nodeIndex = i;
19823         }
19824     },
19825
19826     /**
19827      * Changes the data store this view uses and refresh the view.
19828      * @param {Store} store
19829      */
19830     setStore : function(store, initial){
19831         if(!initial && this.store){
19832             this.store.un("datachanged", this.refresh);
19833             this.store.un("add", this.onAdd);
19834             this.store.un("remove", this.onRemove);
19835             this.store.un("update", this.onUpdate);
19836             this.store.un("clear", this.refresh);
19837             this.store.un("beforeload", this.onBeforeLoad);
19838             this.store.un("load", this.onLoad);
19839             this.store.un("loadexception", this.onLoad);
19840         }
19841         if(store){
19842           
19843             store.on("datachanged", this.refresh, this);
19844             store.on("add", this.onAdd, this);
19845             store.on("remove", this.onRemove, this);
19846             store.on("update", this.onUpdate, this);
19847             store.on("clear", this.refresh, this);
19848             store.on("beforeload", this.onBeforeLoad, this);
19849             store.on("load", this.onLoad, this);
19850             store.on("loadexception", this.onLoad, this);
19851         }
19852         
19853         if(store){
19854             this.refresh();
19855         }
19856     },
19857     /**
19858      * onbeforeLoad - masks the loading area.
19859      *
19860      */
19861     onBeforeLoad : function(store,opts)
19862     {
19863          //Roo.log('onBeforeLoad');   
19864         if (!opts.add) {
19865             this.el.update("");
19866         }
19867         this.el.mask(this.mask ? this.mask : "Loading" ); 
19868     },
19869     onLoad : function ()
19870     {
19871         this.el.unmask();
19872     },
19873     
19874
19875     /**
19876      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19877      * @param {HTMLElement} node
19878      * @return {HTMLElement} The template node
19879      */
19880     findItemFromChild : function(node){
19881         var el = this.dataName  ?
19882             this.el.child('.roo-tpl-' + this.dataName,true) :
19883             this.el.dom; 
19884         
19885         if(!node || node.parentNode == el){
19886                     return node;
19887             }
19888             var p = node.parentNode;
19889             while(p && p != el){
19890             if(p.parentNode == el){
19891                 return p;
19892             }
19893             p = p.parentNode;
19894         }
19895             return null;
19896     },
19897
19898     /** @ignore */
19899     onClick : function(e){
19900         var item = this.findItemFromChild(e.getTarget());
19901         if(item){
19902             var index = this.indexOf(item);
19903             if(this.onItemClick(item, index, e) !== false){
19904                 this.fireEvent("click", this, index, item, e);
19905             }
19906         }else{
19907             this.clearSelections();
19908         }
19909     },
19910
19911     /** @ignore */
19912     onContextMenu : function(e){
19913         var item = this.findItemFromChild(e.getTarget());
19914         if(item){
19915             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19916         }
19917     },
19918
19919     /** @ignore */
19920     onDblClick : function(e){
19921         var item = this.findItemFromChild(e.getTarget());
19922         if(item){
19923             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19924         }
19925     },
19926
19927     onItemClick : function(item, index, e)
19928     {
19929         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19930             return false;
19931         }
19932         if (this.toggleSelect) {
19933             var m = this.isSelected(item) ? 'unselect' : 'select';
19934             //Roo.log(m);
19935             var _t = this;
19936             _t[m](item, true, false);
19937             return true;
19938         }
19939         if(this.multiSelect || this.singleSelect){
19940             if(this.multiSelect && e.shiftKey && this.lastSelection){
19941                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19942             }else{
19943                 this.select(item, this.multiSelect && e.ctrlKey);
19944                 this.lastSelection = item;
19945             }
19946             
19947             if(!this.tickable){
19948                 e.preventDefault();
19949             }
19950             
19951         }
19952         return true;
19953     },
19954
19955     /**
19956      * Get the number of selected nodes.
19957      * @return {Number}
19958      */
19959     getSelectionCount : function(){
19960         return this.selections.length;
19961     },
19962
19963     /**
19964      * Get the currently selected nodes.
19965      * @return {Array} An array of HTMLElements
19966      */
19967     getSelectedNodes : function(){
19968         return this.selections;
19969     },
19970
19971     /**
19972      * Get the indexes of the selected nodes.
19973      * @return {Array}
19974      */
19975     getSelectedIndexes : function(){
19976         var indexes = [], s = this.selections;
19977         for(var i = 0, len = s.length; i < len; i++){
19978             indexes.push(s[i].nodeIndex);
19979         }
19980         return indexes;
19981     },
19982
19983     /**
19984      * Clear all selections
19985      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19986      */
19987     clearSelections : function(suppressEvent){
19988         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19989             this.cmp.elements = this.selections;
19990             this.cmp.removeClass(this.selectedClass);
19991             this.selections = [];
19992             if(!suppressEvent){
19993                 this.fireEvent("selectionchange", this, this.selections);
19994             }
19995         }
19996     },
19997
19998     /**
19999      * Returns true if the passed node is selected
20000      * @param {HTMLElement/Number} node The node or node index
20001      * @return {Boolean}
20002      */
20003     isSelected : function(node){
20004         var s = this.selections;
20005         if(s.length < 1){
20006             return false;
20007         }
20008         node = this.getNode(node);
20009         return s.indexOf(node) !== -1;
20010     },
20011
20012     /**
20013      * Selects nodes.
20014      * @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
20015      * @param {Boolean} keepExisting (optional) true to keep existing selections
20016      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20017      */
20018     select : function(nodeInfo, keepExisting, suppressEvent){
20019         if(nodeInfo instanceof Array){
20020             if(!keepExisting){
20021                 this.clearSelections(true);
20022             }
20023             for(var i = 0, len = nodeInfo.length; i < len; i++){
20024                 this.select(nodeInfo[i], true, true);
20025             }
20026             return;
20027         } 
20028         var node = this.getNode(nodeInfo);
20029         if(!node || this.isSelected(node)){
20030             return; // already selected.
20031         }
20032         if(!keepExisting){
20033             this.clearSelections(true);
20034         }
20035         
20036         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20037             Roo.fly(node).addClass(this.selectedClass);
20038             this.selections.push(node);
20039             if(!suppressEvent){
20040                 this.fireEvent("selectionchange", this, this.selections);
20041             }
20042         }
20043         
20044         
20045     },
20046       /**
20047      * Unselects nodes.
20048      * @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
20049      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20050      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20051      */
20052     unselect : function(nodeInfo, keepExisting, suppressEvent)
20053     {
20054         if(nodeInfo instanceof Array){
20055             Roo.each(this.selections, function(s) {
20056                 this.unselect(s, nodeInfo);
20057             }, this);
20058             return;
20059         }
20060         var node = this.getNode(nodeInfo);
20061         if(!node || !this.isSelected(node)){
20062             //Roo.log("not selected");
20063             return; // not selected.
20064         }
20065         // fireevent???
20066         var ns = [];
20067         Roo.each(this.selections, function(s) {
20068             if (s == node ) {
20069                 Roo.fly(node).removeClass(this.selectedClass);
20070
20071                 return;
20072             }
20073             ns.push(s);
20074         },this);
20075         
20076         this.selections= ns;
20077         this.fireEvent("selectionchange", this, this.selections);
20078     },
20079
20080     /**
20081      * Gets a template node.
20082      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20083      * @return {HTMLElement} The node or null if it wasn't found
20084      */
20085     getNode : function(nodeInfo){
20086         if(typeof nodeInfo == "string"){
20087             return document.getElementById(nodeInfo);
20088         }else if(typeof nodeInfo == "number"){
20089             return this.nodes[nodeInfo];
20090         }
20091         return nodeInfo;
20092     },
20093
20094     /**
20095      * Gets a range template nodes.
20096      * @param {Number} startIndex
20097      * @param {Number} endIndex
20098      * @return {Array} An array of nodes
20099      */
20100     getNodes : function(start, end){
20101         var ns = this.nodes;
20102         start = start || 0;
20103         end = typeof end == "undefined" ? ns.length - 1 : end;
20104         var nodes = [];
20105         if(start <= end){
20106             for(var i = start; i <= end; i++){
20107                 nodes.push(ns[i]);
20108             }
20109         } else{
20110             for(var i = start; i >= end; i--){
20111                 nodes.push(ns[i]);
20112             }
20113         }
20114         return nodes;
20115     },
20116
20117     /**
20118      * Finds the index of the passed node
20119      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20120      * @return {Number} The index of the node or -1
20121      */
20122     indexOf : function(node){
20123         node = this.getNode(node);
20124         if(typeof node.nodeIndex == "number"){
20125             return node.nodeIndex;
20126         }
20127         var ns = this.nodes;
20128         for(var i = 0, len = ns.length; i < len; i++){
20129             if(ns[i] == node){
20130                 return i;
20131             }
20132         }
20133         return -1;
20134     }
20135 });
20136 /*
20137  * - LGPL
20138  *
20139  * based on jquery fullcalendar
20140  * 
20141  */
20142
20143 Roo.bootstrap = Roo.bootstrap || {};
20144 /**
20145  * @class Roo.bootstrap.Calendar
20146  * @extends Roo.bootstrap.Component
20147  * Bootstrap Calendar class
20148  * @cfg {Boolean} loadMask (true|false) default false
20149  * @cfg {Object} header generate the user specific header of the calendar, default false
20150
20151  * @constructor
20152  * Create a new Container
20153  * @param {Object} config The config object
20154  */
20155
20156
20157
20158 Roo.bootstrap.Calendar = function(config){
20159     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20160      this.addEvents({
20161         /**
20162              * @event select
20163              * Fires when a date is selected
20164              * @param {DatePicker} this
20165              * @param {Date} date The selected date
20166              */
20167         'select': true,
20168         /**
20169              * @event monthchange
20170              * Fires when the displayed month changes 
20171              * @param {DatePicker} this
20172              * @param {Date} date The selected month
20173              */
20174         'monthchange': true,
20175         /**
20176              * @event evententer
20177              * Fires when mouse over an event
20178              * @param {Calendar} this
20179              * @param {event} Event
20180              */
20181         'evententer': true,
20182         /**
20183              * @event eventleave
20184              * Fires when the mouse leaves an
20185              * @param {Calendar} this
20186              * @param {event}
20187              */
20188         'eventleave': true,
20189         /**
20190              * @event eventclick
20191              * Fires when the mouse click an
20192              * @param {Calendar} this
20193              * @param {event}
20194              */
20195         'eventclick': true
20196         
20197     });
20198
20199 };
20200
20201 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20202     
20203           /**
20204      * @cfg {Roo.data.Store} store
20205      * The data source for the calendar
20206      */
20207         store : false,
20208      /**
20209      * @cfg {Number} startDay
20210      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20211      */
20212     startDay : 0,
20213     
20214     loadMask : false,
20215     
20216     header : false,
20217       
20218     getAutoCreate : function(){
20219         
20220         
20221         var fc_button = function(name, corner, style, content ) {
20222             return Roo.apply({},{
20223                 tag : 'span',
20224                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20225                          (corner.length ?
20226                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20227                             ''
20228                         ),
20229                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20230                 unselectable: 'on'
20231             });
20232         };
20233         
20234         var header = {};
20235         
20236         if(!this.header){
20237             header = {
20238                 tag : 'table',
20239                 cls : 'fc-header',
20240                 style : 'width:100%',
20241                 cn : [
20242                     {
20243                         tag: 'tr',
20244                         cn : [
20245                             {
20246                                 tag : 'td',
20247                                 cls : 'fc-header-left',
20248                                 cn : [
20249                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20250                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20251                                     { tag: 'span', cls: 'fc-header-space' },
20252                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20253
20254
20255                                 ]
20256                             },
20257
20258                             {
20259                                 tag : 'td',
20260                                 cls : 'fc-header-center',
20261                                 cn : [
20262                                     {
20263                                         tag: 'span',
20264                                         cls: 'fc-header-title',
20265                                         cn : {
20266                                             tag: 'H2',
20267                                             html : 'month / year'
20268                                         }
20269                                     }
20270
20271                                 ]
20272                             },
20273                             {
20274                                 tag : 'td',
20275                                 cls : 'fc-header-right',
20276                                 cn : [
20277                               /*      fc_button('month', 'left', '', 'month' ),
20278                                     fc_button('week', '', '', 'week' ),
20279                                     fc_button('day', 'right', '', 'day' )
20280                                 */    
20281
20282                                 ]
20283                             }
20284
20285                         ]
20286                     }
20287                 ]
20288             };
20289         }
20290         
20291         header = this.header;
20292         
20293        
20294         var cal_heads = function() {
20295             var ret = [];
20296             // fixme - handle this.
20297             
20298             for (var i =0; i < Date.dayNames.length; i++) {
20299                 var d = Date.dayNames[i];
20300                 ret.push({
20301                     tag: 'th',
20302                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20303                     html : d.substring(0,3)
20304                 });
20305                 
20306             }
20307             ret[0].cls += ' fc-first';
20308             ret[6].cls += ' fc-last';
20309             return ret;
20310         };
20311         var cal_cell = function(n) {
20312             return  {
20313                 tag: 'td',
20314                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20315                 cn : [
20316                     {
20317                         cn : [
20318                             {
20319                                 cls: 'fc-day-number',
20320                                 html: 'D'
20321                             },
20322                             {
20323                                 cls: 'fc-day-content',
20324                              
20325                                 cn : [
20326                                      {
20327                                         style: 'position: relative;' // height: 17px;
20328                                     }
20329                                 ]
20330                             }
20331                             
20332                             
20333                         ]
20334                     }
20335                 ]
20336                 
20337             }
20338         };
20339         var cal_rows = function() {
20340             
20341             var ret = [];
20342             for (var r = 0; r < 6; r++) {
20343                 var row= {
20344                     tag : 'tr',
20345                     cls : 'fc-week',
20346                     cn : []
20347                 };
20348                 
20349                 for (var i =0; i < Date.dayNames.length; i++) {
20350                     var d = Date.dayNames[i];
20351                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20352
20353                 }
20354                 row.cn[0].cls+=' fc-first';
20355                 row.cn[0].cn[0].style = 'min-height:90px';
20356                 row.cn[6].cls+=' fc-last';
20357                 ret.push(row);
20358                 
20359             }
20360             ret[0].cls += ' fc-first';
20361             ret[4].cls += ' fc-prev-last';
20362             ret[5].cls += ' fc-last';
20363             return ret;
20364             
20365         };
20366         
20367         var cal_table = {
20368             tag: 'table',
20369             cls: 'fc-border-separate',
20370             style : 'width:100%',
20371             cellspacing  : 0,
20372             cn : [
20373                 { 
20374                     tag: 'thead',
20375                     cn : [
20376                         { 
20377                             tag: 'tr',
20378                             cls : 'fc-first fc-last',
20379                             cn : cal_heads()
20380                         }
20381                     ]
20382                 },
20383                 { 
20384                     tag: 'tbody',
20385                     cn : cal_rows()
20386                 }
20387                   
20388             ]
20389         };
20390          
20391          var cfg = {
20392             cls : 'fc fc-ltr',
20393             cn : [
20394                 header,
20395                 {
20396                     cls : 'fc-content',
20397                     style : "position: relative;",
20398                     cn : [
20399                         {
20400                             cls : 'fc-view fc-view-month fc-grid',
20401                             style : 'position: relative',
20402                             unselectable : 'on',
20403                             cn : [
20404                                 {
20405                                     cls : 'fc-event-container',
20406                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20407                                 },
20408                                 cal_table
20409                             ]
20410                         }
20411                     ]
20412     
20413                 }
20414            ] 
20415             
20416         };
20417         
20418          
20419         
20420         return cfg;
20421     },
20422     
20423     
20424     initEvents : function()
20425     {
20426         if(!this.store){
20427             throw "can not find store for calendar";
20428         }
20429         
20430         var mark = {
20431             tag: "div",
20432             cls:"x-dlg-mask",
20433             style: "text-align:center",
20434             cn: [
20435                 {
20436                     tag: "div",
20437                     style: "background-color:white;width:50%;margin:250 auto",
20438                     cn: [
20439                         {
20440                             tag: "img",
20441                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20442                         },
20443                         {
20444                             tag: "span",
20445                             html: "Loading"
20446                         }
20447                         
20448                     ]
20449                 }
20450             ]
20451         };
20452         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20453         
20454         var size = this.el.select('.fc-content', true).first().getSize();
20455         this.maskEl.setSize(size.width, size.height);
20456         this.maskEl.enableDisplayMode("block");
20457         if(!this.loadMask){
20458             this.maskEl.hide();
20459         }
20460         
20461         this.store = Roo.factory(this.store, Roo.data);
20462         this.store.on('load', this.onLoad, this);
20463         this.store.on('beforeload', this.onBeforeLoad, this);
20464         
20465         this.resize();
20466         
20467         this.cells = this.el.select('.fc-day',true);
20468         //Roo.log(this.cells);
20469         this.textNodes = this.el.query('.fc-day-number');
20470         this.cells.addClassOnOver('fc-state-hover');
20471         
20472         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20473         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20474         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20475         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20476         
20477         this.on('monthchange', this.onMonthChange, this);
20478         
20479         this.update(new Date().clearTime());
20480     },
20481     
20482     resize : function() {
20483         var sz  = this.el.getSize();
20484         
20485         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20486         this.el.select('.fc-day-content div',true).setHeight(34);
20487     },
20488     
20489     
20490     // private
20491     showPrevMonth : function(e){
20492         this.update(this.activeDate.add("mo", -1));
20493     },
20494     showToday : function(e){
20495         this.update(new Date().clearTime());
20496     },
20497     // private
20498     showNextMonth : function(e){
20499         this.update(this.activeDate.add("mo", 1));
20500     },
20501
20502     // private
20503     showPrevYear : function(){
20504         this.update(this.activeDate.add("y", -1));
20505     },
20506
20507     // private
20508     showNextYear : function(){
20509         this.update(this.activeDate.add("y", 1));
20510     },
20511
20512     
20513    // private
20514     update : function(date)
20515     {
20516         var vd = this.activeDate;
20517         this.activeDate = date;
20518 //        if(vd && this.el){
20519 //            var t = date.getTime();
20520 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20521 //                Roo.log('using add remove');
20522 //                
20523 //                this.fireEvent('monthchange', this, date);
20524 //                
20525 //                this.cells.removeClass("fc-state-highlight");
20526 //                this.cells.each(function(c){
20527 //                   if(c.dateValue == t){
20528 //                       c.addClass("fc-state-highlight");
20529 //                       setTimeout(function(){
20530 //                            try{c.dom.firstChild.focus();}catch(e){}
20531 //                       }, 50);
20532 //                       return false;
20533 //                   }
20534 //                   return true;
20535 //                });
20536 //                return;
20537 //            }
20538 //        }
20539         
20540         var days = date.getDaysInMonth();
20541         
20542         var firstOfMonth = date.getFirstDateOfMonth();
20543         var startingPos = firstOfMonth.getDay()-this.startDay;
20544         
20545         if(startingPos < this.startDay){
20546             startingPos += 7;
20547         }
20548         
20549         var pm = date.add(Date.MONTH, -1);
20550         var prevStart = pm.getDaysInMonth()-startingPos;
20551 //        
20552         this.cells = this.el.select('.fc-day',true);
20553         this.textNodes = this.el.query('.fc-day-number');
20554         this.cells.addClassOnOver('fc-state-hover');
20555         
20556         var cells = this.cells.elements;
20557         var textEls = this.textNodes;
20558         
20559         Roo.each(cells, function(cell){
20560             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20561         });
20562         
20563         days += startingPos;
20564
20565         // convert everything to numbers so it's fast
20566         var day = 86400000;
20567         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20568         //Roo.log(d);
20569         //Roo.log(pm);
20570         //Roo.log(prevStart);
20571         
20572         var today = new Date().clearTime().getTime();
20573         var sel = date.clearTime().getTime();
20574         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20575         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20576         var ddMatch = this.disabledDatesRE;
20577         var ddText = this.disabledDatesText;
20578         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20579         var ddaysText = this.disabledDaysText;
20580         var format = this.format;
20581         
20582         var setCellClass = function(cal, cell){
20583             cell.row = 0;
20584             cell.events = [];
20585             cell.more = [];
20586             //Roo.log('set Cell Class');
20587             cell.title = "";
20588             var t = d.getTime();
20589             
20590             //Roo.log(d);
20591             
20592             cell.dateValue = t;
20593             if(t == today){
20594                 cell.className += " fc-today";
20595                 cell.className += " fc-state-highlight";
20596                 cell.title = cal.todayText;
20597             }
20598             if(t == sel){
20599                 // disable highlight in other month..
20600                 //cell.className += " fc-state-highlight";
20601                 
20602             }
20603             // disabling
20604             if(t < min) {
20605                 cell.className = " fc-state-disabled";
20606                 cell.title = cal.minText;
20607                 return;
20608             }
20609             if(t > max) {
20610                 cell.className = " fc-state-disabled";
20611                 cell.title = cal.maxText;
20612                 return;
20613             }
20614             if(ddays){
20615                 if(ddays.indexOf(d.getDay()) != -1){
20616                     cell.title = ddaysText;
20617                     cell.className = " fc-state-disabled";
20618                 }
20619             }
20620             if(ddMatch && format){
20621                 var fvalue = d.dateFormat(format);
20622                 if(ddMatch.test(fvalue)){
20623                     cell.title = ddText.replace("%0", fvalue);
20624                     cell.className = " fc-state-disabled";
20625                 }
20626             }
20627             
20628             if (!cell.initialClassName) {
20629                 cell.initialClassName = cell.dom.className;
20630             }
20631             
20632             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20633         };
20634
20635         var i = 0;
20636         
20637         for(; i < startingPos; i++) {
20638             textEls[i].innerHTML = (++prevStart);
20639             d.setDate(d.getDate()+1);
20640             
20641             cells[i].className = "fc-past fc-other-month";
20642             setCellClass(this, cells[i]);
20643         }
20644         
20645         var intDay = 0;
20646         
20647         for(; i < days; i++){
20648             intDay = i - startingPos + 1;
20649             textEls[i].innerHTML = (intDay);
20650             d.setDate(d.getDate()+1);
20651             
20652             cells[i].className = ''; // "x-date-active";
20653             setCellClass(this, cells[i]);
20654         }
20655         var extraDays = 0;
20656         
20657         for(; i < 42; i++) {
20658             textEls[i].innerHTML = (++extraDays);
20659             d.setDate(d.getDate()+1);
20660             
20661             cells[i].className = "fc-future fc-other-month";
20662             setCellClass(this, cells[i]);
20663         }
20664         
20665         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20666         
20667         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20668         
20669         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20670         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20671         
20672         if(totalRows != 6){
20673             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20674             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20675         }
20676         
20677         this.fireEvent('monthchange', this, date);
20678         
20679         
20680         /*
20681         if(!this.internalRender){
20682             var main = this.el.dom.firstChild;
20683             var w = main.offsetWidth;
20684             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20685             Roo.fly(main).setWidth(w);
20686             this.internalRender = true;
20687             // opera does not respect the auto grow header center column
20688             // then, after it gets a width opera refuses to recalculate
20689             // without a second pass
20690             if(Roo.isOpera && !this.secondPass){
20691                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20692                 this.secondPass = true;
20693                 this.update.defer(10, this, [date]);
20694             }
20695         }
20696         */
20697         
20698     },
20699     
20700     findCell : function(dt) {
20701         dt = dt.clearTime().getTime();
20702         var ret = false;
20703         this.cells.each(function(c){
20704             //Roo.log("check " +c.dateValue + '?=' + dt);
20705             if(c.dateValue == dt){
20706                 ret = c;
20707                 return false;
20708             }
20709             return true;
20710         });
20711         
20712         return ret;
20713     },
20714     
20715     findCells : function(ev) {
20716         var s = ev.start.clone().clearTime().getTime();
20717        // Roo.log(s);
20718         var e= ev.end.clone().clearTime().getTime();
20719        // Roo.log(e);
20720         var ret = [];
20721         this.cells.each(function(c){
20722              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20723             
20724             if(c.dateValue > e){
20725                 return ;
20726             }
20727             if(c.dateValue < s){
20728                 return ;
20729             }
20730             ret.push(c);
20731         });
20732         
20733         return ret;    
20734     },
20735     
20736 //    findBestRow: function(cells)
20737 //    {
20738 //        var ret = 0;
20739 //        
20740 //        for (var i =0 ; i < cells.length;i++) {
20741 //            ret  = Math.max(cells[i].rows || 0,ret);
20742 //        }
20743 //        return ret;
20744 //        
20745 //    },
20746     
20747     
20748     addItem : function(ev)
20749     {
20750         // look for vertical location slot in
20751         var cells = this.findCells(ev);
20752         
20753 //        ev.row = this.findBestRow(cells);
20754         
20755         // work out the location.
20756         
20757         var crow = false;
20758         var rows = [];
20759         for(var i =0; i < cells.length; i++) {
20760             
20761             cells[i].row = cells[0].row;
20762             
20763             if(i == 0){
20764                 cells[i].row = cells[i].row + 1;
20765             }
20766             
20767             if (!crow) {
20768                 crow = {
20769                     start : cells[i],
20770                     end :  cells[i]
20771                 };
20772                 continue;
20773             }
20774             if (crow.start.getY() == cells[i].getY()) {
20775                 // on same row.
20776                 crow.end = cells[i];
20777                 continue;
20778             }
20779             // different row.
20780             rows.push(crow);
20781             crow = {
20782                 start: cells[i],
20783                 end : cells[i]
20784             };
20785             
20786         }
20787         
20788         rows.push(crow);
20789         ev.els = [];
20790         ev.rows = rows;
20791         ev.cells = cells;
20792         
20793         cells[0].events.push(ev);
20794         
20795         this.calevents.push(ev);
20796     },
20797     
20798     clearEvents: function() {
20799         
20800         if(!this.calevents){
20801             return;
20802         }
20803         
20804         Roo.each(this.cells.elements, function(c){
20805             c.row = 0;
20806             c.events = [];
20807             c.more = [];
20808         });
20809         
20810         Roo.each(this.calevents, function(e) {
20811             Roo.each(e.els, function(el) {
20812                 el.un('mouseenter' ,this.onEventEnter, this);
20813                 el.un('mouseleave' ,this.onEventLeave, this);
20814                 el.remove();
20815             },this);
20816         },this);
20817         
20818         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20819             e.remove();
20820         });
20821         
20822     },
20823     
20824     renderEvents: function()
20825     {   
20826         var _this = this;
20827         
20828         this.cells.each(function(c) {
20829             
20830             if(c.row < 5){
20831                 return;
20832             }
20833             
20834             var ev = c.events;
20835             
20836             var r = 4;
20837             if(c.row != c.events.length){
20838                 r = 4 - (4 - (c.row - c.events.length));
20839             }
20840             
20841             c.events = ev.slice(0, r);
20842             c.more = ev.slice(r);
20843             
20844             if(c.more.length && c.more.length == 1){
20845                 c.events.push(c.more.pop());
20846             }
20847             
20848             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20849             
20850         });
20851             
20852         this.cells.each(function(c) {
20853             
20854             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20855             
20856             
20857             for (var e = 0; e < c.events.length; e++){
20858                 var ev = c.events[e];
20859                 var rows = ev.rows;
20860                 
20861                 for(var i = 0; i < rows.length; i++) {
20862                 
20863                     // how many rows should it span..
20864
20865                     var  cfg = {
20866                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20867                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20868
20869                         unselectable : "on",
20870                         cn : [
20871                             {
20872                                 cls: 'fc-event-inner',
20873                                 cn : [
20874     //                                {
20875     //                                  tag:'span',
20876     //                                  cls: 'fc-event-time',
20877     //                                  html : cells.length > 1 ? '' : ev.time
20878     //                                },
20879                                     {
20880                                       tag:'span',
20881                                       cls: 'fc-event-title',
20882                                       html : String.format('{0}', ev.title)
20883                                     }
20884
20885
20886                                 ]
20887                             },
20888                             {
20889                                 cls: 'ui-resizable-handle ui-resizable-e',
20890                                 html : '&nbsp;&nbsp;&nbsp'
20891                             }
20892
20893                         ]
20894                     };
20895
20896                     if (i == 0) {
20897                         cfg.cls += ' fc-event-start';
20898                     }
20899                     if ((i+1) == rows.length) {
20900                         cfg.cls += ' fc-event-end';
20901                     }
20902
20903                     var ctr = _this.el.select('.fc-event-container',true).first();
20904                     var cg = ctr.createChild(cfg);
20905
20906                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20907                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20908
20909                     var r = (c.more.length) ? 1 : 0;
20910                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20911                     cg.setWidth(ebox.right - sbox.x -2);
20912
20913                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20914                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20915                     cg.on('click', _this.onEventClick, _this, ev);
20916
20917                     ev.els.push(cg);
20918                     
20919                 }
20920                 
20921             }
20922             
20923             
20924             if(c.more.length){
20925                 var  cfg = {
20926                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20927                     style : 'position: absolute',
20928                     unselectable : "on",
20929                     cn : [
20930                         {
20931                             cls: 'fc-event-inner',
20932                             cn : [
20933                                 {
20934                                   tag:'span',
20935                                   cls: 'fc-event-title',
20936                                   html : 'More'
20937                                 }
20938
20939
20940                             ]
20941                         },
20942                         {
20943                             cls: 'ui-resizable-handle ui-resizable-e',
20944                             html : '&nbsp;&nbsp;&nbsp'
20945                         }
20946
20947                     ]
20948                 };
20949
20950                 var ctr = _this.el.select('.fc-event-container',true).first();
20951                 var cg = ctr.createChild(cfg);
20952
20953                 var sbox = c.select('.fc-day-content',true).first().getBox();
20954                 var ebox = c.select('.fc-day-content',true).first().getBox();
20955                 //Roo.log(cg);
20956                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20957                 cg.setWidth(ebox.right - sbox.x -2);
20958
20959                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20960                 
20961             }
20962             
20963         });
20964         
20965         
20966         
20967     },
20968     
20969     onEventEnter: function (e, el,event,d) {
20970         this.fireEvent('evententer', this, el, event);
20971     },
20972     
20973     onEventLeave: function (e, el,event,d) {
20974         this.fireEvent('eventleave', this, el, event);
20975     },
20976     
20977     onEventClick: function (e, el,event,d) {
20978         this.fireEvent('eventclick', this, el, event);
20979     },
20980     
20981     onMonthChange: function () {
20982         this.store.load();
20983     },
20984     
20985     onMoreEventClick: function(e, el, more)
20986     {
20987         var _this = this;
20988         
20989         this.calpopover.placement = 'right';
20990         this.calpopover.setTitle('More');
20991         
20992         this.calpopover.setContent('');
20993         
20994         var ctr = this.calpopover.el.select('.popover-content', true).first();
20995         
20996         Roo.each(more, function(m){
20997             var cfg = {
20998                 cls : 'fc-event-hori fc-event-draggable',
20999                 html : m.title
21000             };
21001             var cg = ctr.createChild(cfg);
21002             
21003             cg.on('click', _this.onEventClick, _this, m);
21004         });
21005         
21006         this.calpopover.show(el);
21007         
21008         
21009     },
21010     
21011     onLoad: function () 
21012     {   
21013         this.calevents = [];
21014         var cal = this;
21015         
21016         if(this.store.getCount() > 0){
21017             this.store.data.each(function(d){
21018                cal.addItem({
21019                     id : d.data.id,
21020                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21021                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21022                     time : d.data.start_time,
21023                     title : d.data.title,
21024                     description : d.data.description,
21025                     venue : d.data.venue
21026                 });
21027             });
21028         }
21029         
21030         this.renderEvents();
21031         
21032         if(this.calevents.length && this.loadMask){
21033             this.maskEl.hide();
21034         }
21035     },
21036     
21037     onBeforeLoad: function()
21038     {
21039         this.clearEvents();
21040         if(this.loadMask){
21041             this.maskEl.show();
21042         }
21043     }
21044 });
21045
21046  
21047  /*
21048  * - LGPL
21049  *
21050  * element
21051  * 
21052  */
21053
21054 /**
21055  * @class Roo.bootstrap.Popover
21056  * @extends Roo.bootstrap.Component
21057  * Bootstrap Popover class
21058  * @cfg {String} html contents of the popover   (or false to use children..)
21059  * @cfg {String} title of popover (or false to hide)
21060  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21061  * @cfg {String} trigger click || hover (or false to trigger manually)
21062  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21063  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21064  *      - if false and it has a 'parent' then it will be automatically added to that element
21065  *      - if string - Roo.get  will be called 
21066  * @cfg {Number} delay - delay before showing
21067  
21068  * @constructor
21069  * Create a new Popover
21070  * @param {Object} config The config object
21071  */
21072
21073 Roo.bootstrap.Popover = function(config){
21074     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21075     
21076     this.addEvents({
21077         // raw events
21078          /**
21079          * @event show
21080          * After the popover show
21081          * 
21082          * @param {Roo.bootstrap.Popover} this
21083          */
21084         "show" : true,
21085         /**
21086          * @event hide
21087          * After the popover hide
21088          * 
21089          * @param {Roo.bootstrap.Popover} this
21090          */
21091         "hide" : true
21092     });
21093 };
21094
21095 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21096     
21097     title: false,
21098     html: false,
21099     
21100     placement : 'right',
21101     trigger : 'hover', // hover
21102     modal : false,
21103     delay : 0,
21104     
21105     over: false,
21106     
21107     can_build_overlaid : false,
21108     
21109     maskEl : false, // the mask element
21110     headerEl : false,
21111     contentEl : false,
21112     alignEl : false, // when show is called with an element - this get's stored.
21113     
21114     getChildContainer : function()
21115     {
21116         return this.contentEl;
21117         
21118     },
21119     getPopoverHeader : function()
21120     {
21121         this.title = true; // flag not to hide it..
21122         this.headerEl.addClass('p-0');
21123         return this.headerEl
21124     },
21125     
21126     
21127     getAutoCreate : function(){
21128          
21129         var cfg = {
21130            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21131            style: 'display:block',
21132            cn : [
21133                 {
21134                     cls : 'arrow'
21135                 },
21136                 {
21137                     cls : 'popover-inner ',
21138                     cn : [
21139                         {
21140                             tag: 'h3',
21141                             cls: 'popover-title popover-header',
21142                             html : this.title === false ? '' : this.title
21143                         },
21144                         {
21145                             cls : 'popover-content popover-body '  + (this.cls || ''),
21146                             html : this.html || ''
21147                         }
21148                     ]
21149                     
21150                 }
21151            ]
21152         };
21153         
21154         return cfg;
21155     },
21156     /**
21157      * @param {string} the title
21158      */
21159     setTitle: function(str)
21160     {
21161         this.title = str;
21162         if (this.el) {
21163             this.headerEl.dom.innerHTML = str;
21164         }
21165         
21166     },
21167     /**
21168      * @param {string} the body content
21169      */
21170     setContent: function(str)
21171     {
21172         this.html = str;
21173         if (this.contentEl) {
21174             this.contentEl.dom.innerHTML = str;
21175         }
21176         
21177     },
21178     // as it get's added to the bottom of the page.
21179     onRender : function(ct, position)
21180     {
21181         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21182         
21183         
21184         
21185         if(!this.el){
21186             var cfg = Roo.apply({},  this.getAutoCreate());
21187             cfg.id = Roo.id();
21188             
21189             if (this.cls) {
21190                 cfg.cls += ' ' + this.cls;
21191             }
21192             if (this.style) {
21193                 cfg.style = this.style;
21194             }
21195             //Roo.log("adding to ");
21196             this.el = Roo.get(document.body).createChild(cfg, position);
21197 //            Roo.log(this.el);
21198         }
21199         
21200         this.contentEl = this.el.select('.popover-content',true).first();
21201         this.headerEl =  this.el.select('.popover-title',true).first();
21202         
21203         var nitems = [];
21204         if(typeof(this.items) != 'undefined'){
21205             var items = this.items;
21206             delete this.items;
21207
21208             for(var i =0;i < items.length;i++) {
21209                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21210             }
21211         }
21212
21213         this.items = nitems;
21214         
21215         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21216         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21217         
21218         
21219         
21220         this.initEvents();
21221     },
21222     
21223     resizeMask : function()
21224     {
21225         this.maskEl.setSize(
21226             Roo.lib.Dom.getViewWidth(true),
21227             Roo.lib.Dom.getViewHeight(true)
21228         );
21229     },
21230     
21231     initEvents : function()
21232     {
21233         
21234         if (!this.modal) { 
21235             Roo.bootstrap.Popover.register(this);
21236         }
21237          
21238         this.arrowEl = this.el.select('.arrow',true).first();
21239         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21240         this.el.enableDisplayMode('block');
21241         this.el.hide();
21242  
21243         
21244         if (this.over === false && !this.parent()) {
21245             return; 
21246         }
21247         if (this.triggers === false) {
21248             return;
21249         }
21250          
21251         // support parent
21252         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21253         var triggers = this.trigger ? this.trigger.split(' ') : [];
21254         Roo.each(triggers, function(trigger) {
21255         
21256             if (trigger == 'click') {
21257                 on_el.on('click', this.toggle, this);
21258             } else if (trigger != 'manual') {
21259                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21260                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21261       
21262                 on_el.on(eventIn  ,this.enter, this);
21263                 on_el.on(eventOut, this.leave, this);
21264             }
21265         }, this);
21266     },
21267     
21268     
21269     // private
21270     timeout : null,
21271     hoverState : null,
21272     
21273     toggle : function () {
21274         this.hoverState == 'in' ? this.leave() : this.enter();
21275     },
21276     
21277     enter : function () {
21278         
21279         clearTimeout(this.timeout);
21280     
21281         this.hoverState = 'in';
21282     
21283         if (!this.delay || !this.delay.show) {
21284             this.show();
21285             return;
21286         }
21287         var _t = this;
21288         this.timeout = setTimeout(function () {
21289             if (_t.hoverState == 'in') {
21290                 _t.show();
21291             }
21292         }, this.delay.show)
21293     },
21294     
21295     leave : function() {
21296         clearTimeout(this.timeout);
21297     
21298         this.hoverState = 'out';
21299     
21300         if (!this.delay || !this.delay.hide) {
21301             this.hide();
21302             return;
21303         }
21304         var _t = this;
21305         this.timeout = setTimeout(function () {
21306             if (_t.hoverState == 'out') {
21307                 _t.hide();
21308             }
21309         }, this.delay.hide)
21310     },
21311     /**
21312      * Show the popover
21313      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21314      * @param {string} (left|right|top|bottom) position
21315      */
21316     show : function (on_el, placement)
21317     {
21318         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21319         on_el = on_el || false; // default to false
21320          
21321         if (!on_el) {
21322             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21323                 on_el = this.parent().el;
21324             } else if (this.over) {
21325                 on_el = Roo.get(this.over);
21326             }
21327             
21328         }
21329         
21330         this.alignEl = Roo.get( on_el );
21331
21332         if (!this.el) {
21333             this.render(document.body);
21334         }
21335         
21336         
21337          
21338         
21339         if (this.title === false) {
21340             this.headerEl.hide();
21341         }
21342         
21343        
21344         this.el.show();
21345         this.el.dom.style.display = 'block';
21346          
21347  
21348         if (this.alignEl) {
21349             this.updatePosition(this.placement, true);
21350              
21351         } else {
21352             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21353             var es = this.el.getSize();
21354             var x = Roo.lib.Dom.getViewWidth()/2;
21355             var y = Roo.lib.Dom.getViewHeight()/2;
21356             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21357             
21358         }
21359
21360         
21361         //var arrow = this.el.select('.arrow',true).first();
21362         //arrow.set(align[2], 
21363         
21364         this.el.addClass('in');
21365         
21366          
21367         
21368         this.hoverState = 'in';
21369         
21370         if (this.modal) {
21371             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21372             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21373             this.maskEl.dom.style.display = 'block';
21374             this.maskEl.addClass('show');
21375         }
21376         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21377  
21378         this.fireEvent('show', this);
21379         
21380     },
21381     /**
21382      * fire this manually after loading a grid in the table for example
21383      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21384      * @param {Boolean} try and move it if we cant get right position.
21385      */
21386     updatePosition : function(placement, try_move)
21387     {
21388         // allow for calling with no parameters
21389         placement = placement   ? placement :  this.placement;
21390         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21391         
21392         this.el.removeClass([
21393             'fade','top','bottom', 'left', 'right','in',
21394             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21395         ]);
21396         this.el.addClass(placement + ' bs-popover-' + placement);
21397         
21398         if (!this.alignEl ) {
21399             return false;
21400         }
21401         
21402         switch (placement) {
21403             case 'right':
21404                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21405                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21406                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21407                     //normal display... or moved up/down.
21408                     this.el.setXY(offset);
21409                     var xy = this.alignEl.getAnchorXY('tr', false);
21410                     xy[0]+=2;xy[1]+=5;
21411                     this.arrowEl.setXY(xy);
21412                     return true;
21413                 }
21414                 // continue through...
21415                 return this.updatePosition('left', false);
21416                 
21417             
21418             case 'left':
21419                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21420                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21421                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21422                     //normal display... or moved up/down.
21423                     this.el.setXY(offset);
21424                     var xy = this.alignEl.getAnchorXY('tl', false);
21425                     xy[0]-=10;xy[1]+=5; // << fix me
21426                     this.arrowEl.setXY(xy);
21427                     return true;
21428                 }
21429                 // call self...
21430                 return this.updatePosition('right', false);
21431             
21432             case 'top':
21433                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21434                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21435                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21436                     //normal display... or moved up/down.
21437                     this.el.setXY(offset);
21438                     var xy = this.alignEl.getAnchorXY('t', false);
21439                     xy[1]-=10; // << fix me
21440                     this.arrowEl.setXY(xy);
21441                     return true;
21442                 }
21443                 // fall through
21444                return this.updatePosition('bottom', false);
21445             
21446             case 'bottom':
21447                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21448                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21449                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21450                     //normal display... or moved up/down.
21451                     this.el.setXY(offset);
21452                     var xy = this.alignEl.getAnchorXY('b', false);
21453                      xy[1]+=2; // << fix me
21454                     this.arrowEl.setXY(xy);
21455                     return true;
21456                 }
21457                 // fall through
21458                 return this.updatePosition('top', false);
21459                 
21460             
21461         }
21462         
21463         
21464         return false;
21465     },
21466     
21467     hide : function()
21468     {
21469         this.el.setXY([0,0]);
21470         this.el.removeClass('in');
21471         this.el.hide();
21472         this.hoverState = null;
21473         this.maskEl.hide(); // always..
21474         this.fireEvent('hide', this);
21475     }
21476     
21477 });
21478
21479
21480 Roo.apply(Roo.bootstrap.Popover, {
21481
21482     alignment : {
21483         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21484         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21485         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21486         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21487     },
21488     
21489     zIndex : 20001,
21490
21491     clickHander : false,
21492     
21493     
21494
21495     onMouseDown : function(e)
21496     {
21497         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21498             /// what is nothing is showing..
21499             this.hideAll();
21500         }
21501          
21502     },
21503     
21504     
21505     popups : [],
21506     
21507     register : function(popup)
21508     {
21509         if (!Roo.bootstrap.Popover.clickHandler) {
21510             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21511         }
21512         // hide other popups.
21513         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21514         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21515         this.hideAll(); //<< why?
21516         //this.popups.push(popup);
21517     },
21518     hideAll : function()
21519     {
21520         this.popups.forEach(function(p) {
21521             p.hide();
21522         });
21523     },
21524     onShow : function() {
21525         Roo.bootstrap.Popover.popups.push(this);
21526     },
21527     onHide : function() {
21528         Roo.bootstrap.Popover.popups.remove(this);
21529     } 
21530
21531 });/*
21532  * - LGPL
21533  *
21534  * Card header - holder for the card header elements.
21535  * 
21536  */
21537
21538 /**
21539  * @class Roo.bootstrap.PopoverNav
21540  * @extends Roo.bootstrap.NavGroup
21541  * Bootstrap Popover header navigation class
21542  * @constructor
21543  * Create a new Popover Header Navigation 
21544  * @param {Object} config The config object
21545  */
21546
21547 Roo.bootstrap.PopoverNav = function(config){
21548     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21549 };
21550
21551 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21552     
21553     
21554     container_method : 'getPopoverHeader' 
21555     
21556      
21557     
21558     
21559    
21560 });
21561
21562  
21563
21564  /*
21565  * - LGPL
21566  *
21567  * Progress
21568  * 
21569  */
21570
21571 /**
21572  * @class Roo.bootstrap.Progress
21573  * @extends Roo.bootstrap.Component
21574  * Bootstrap Progress class
21575  * @cfg {Boolean} striped striped of the progress bar
21576  * @cfg {Boolean} active animated of the progress bar
21577  * 
21578  * 
21579  * @constructor
21580  * Create a new Progress
21581  * @param {Object} config The config object
21582  */
21583
21584 Roo.bootstrap.Progress = function(config){
21585     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21586 };
21587
21588 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21589     
21590     striped : false,
21591     active: false,
21592     
21593     getAutoCreate : function(){
21594         var cfg = {
21595             tag: 'div',
21596             cls: 'progress'
21597         };
21598         
21599         
21600         if(this.striped){
21601             cfg.cls += ' progress-striped';
21602         }
21603       
21604         if(this.active){
21605             cfg.cls += ' active';
21606         }
21607         
21608         
21609         return cfg;
21610     }
21611    
21612 });
21613
21614  
21615
21616  /*
21617  * - LGPL
21618  *
21619  * ProgressBar
21620  * 
21621  */
21622
21623 /**
21624  * @class Roo.bootstrap.ProgressBar
21625  * @extends Roo.bootstrap.Component
21626  * Bootstrap ProgressBar class
21627  * @cfg {Number} aria_valuenow aria-value now
21628  * @cfg {Number} aria_valuemin aria-value min
21629  * @cfg {Number} aria_valuemax aria-value max
21630  * @cfg {String} label label for the progress bar
21631  * @cfg {String} panel (success | info | warning | danger )
21632  * @cfg {String} role role of the progress bar
21633  * @cfg {String} sr_only text
21634  * 
21635  * 
21636  * @constructor
21637  * Create a new ProgressBar
21638  * @param {Object} config The config object
21639  */
21640
21641 Roo.bootstrap.ProgressBar = function(config){
21642     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21643 };
21644
21645 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21646     
21647     aria_valuenow : 0,
21648     aria_valuemin : 0,
21649     aria_valuemax : 100,
21650     label : false,
21651     panel : false,
21652     role : false,
21653     sr_only: false,
21654     
21655     getAutoCreate : function()
21656     {
21657         
21658         var cfg = {
21659             tag: 'div',
21660             cls: 'progress-bar',
21661             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21662         };
21663         
21664         if(this.sr_only){
21665             cfg.cn = {
21666                 tag: 'span',
21667                 cls: 'sr-only',
21668                 html: this.sr_only
21669             }
21670         }
21671         
21672         if(this.role){
21673             cfg.role = this.role;
21674         }
21675         
21676         if(this.aria_valuenow){
21677             cfg['aria-valuenow'] = this.aria_valuenow;
21678         }
21679         
21680         if(this.aria_valuemin){
21681             cfg['aria-valuemin'] = this.aria_valuemin;
21682         }
21683         
21684         if(this.aria_valuemax){
21685             cfg['aria-valuemax'] = this.aria_valuemax;
21686         }
21687         
21688         if(this.label && !this.sr_only){
21689             cfg.html = this.label;
21690         }
21691         
21692         if(this.panel){
21693             cfg.cls += ' progress-bar-' + this.panel;
21694         }
21695         
21696         return cfg;
21697     },
21698     
21699     update : function(aria_valuenow)
21700     {
21701         this.aria_valuenow = aria_valuenow;
21702         
21703         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21704     }
21705    
21706 });
21707
21708  
21709
21710  /*
21711  * - LGPL
21712  *
21713  * column
21714  * 
21715  */
21716
21717 /**
21718  * @class Roo.bootstrap.TabGroup
21719  * @extends Roo.bootstrap.Column
21720  * Bootstrap Column class
21721  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21722  * @cfg {Boolean} carousel true to make the group behave like a carousel
21723  * @cfg {Boolean} bullets show bullets for the panels
21724  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21725  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21726  * @cfg {Boolean} showarrow (true|false) show arrow default true
21727  * 
21728  * @constructor
21729  * Create a new TabGroup
21730  * @param {Object} config The config object
21731  */
21732
21733 Roo.bootstrap.TabGroup = function(config){
21734     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21735     if (!this.navId) {
21736         this.navId = Roo.id();
21737     }
21738     this.tabs = [];
21739     Roo.bootstrap.TabGroup.register(this);
21740     
21741 };
21742
21743 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21744     
21745     carousel : false,
21746     transition : false,
21747     bullets : 0,
21748     timer : 0,
21749     autoslide : false,
21750     slideFn : false,
21751     slideOnTouch : false,
21752     showarrow : true,
21753     
21754     getAutoCreate : function()
21755     {
21756         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21757         
21758         cfg.cls += ' tab-content';
21759         
21760         if (this.carousel) {
21761             cfg.cls += ' carousel slide';
21762             
21763             cfg.cn = [{
21764                cls : 'carousel-inner',
21765                cn : []
21766             }];
21767         
21768             if(this.bullets  && !Roo.isTouch){
21769                 
21770                 var bullets = {
21771                     cls : 'carousel-bullets',
21772                     cn : []
21773                 };
21774                
21775                 if(this.bullets_cls){
21776                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21777                 }
21778                 
21779                 bullets.cn.push({
21780                     cls : 'clear'
21781                 });
21782                 
21783                 cfg.cn[0].cn.push(bullets);
21784             }
21785             
21786             if(this.showarrow){
21787                 cfg.cn[0].cn.push({
21788                     tag : 'div',
21789                     class : 'carousel-arrow',
21790                     cn : [
21791                         {
21792                             tag : 'div',
21793                             class : 'carousel-prev',
21794                             cn : [
21795                                 {
21796                                     tag : 'i',
21797                                     class : 'fa fa-chevron-left'
21798                                 }
21799                             ]
21800                         },
21801                         {
21802                             tag : 'div',
21803                             class : 'carousel-next',
21804                             cn : [
21805                                 {
21806                                     tag : 'i',
21807                                     class : 'fa fa-chevron-right'
21808                                 }
21809                             ]
21810                         }
21811                     ]
21812                 });
21813             }
21814             
21815         }
21816         
21817         return cfg;
21818     },
21819     
21820     initEvents:  function()
21821     {
21822 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21823 //            this.el.on("touchstart", this.onTouchStart, this);
21824 //        }
21825         
21826         if(this.autoslide){
21827             var _this = this;
21828             
21829             this.slideFn = window.setInterval(function() {
21830                 _this.showPanelNext();
21831             }, this.timer);
21832         }
21833         
21834         if(this.showarrow){
21835             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21836             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21837         }
21838         
21839         
21840     },
21841     
21842 //    onTouchStart : function(e, el, o)
21843 //    {
21844 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21845 //            return;
21846 //        }
21847 //        
21848 //        this.showPanelNext();
21849 //    },
21850     
21851     
21852     getChildContainer : function()
21853     {
21854         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21855     },
21856     
21857     /**
21858     * register a Navigation item
21859     * @param {Roo.bootstrap.NavItem} the navitem to add
21860     */
21861     register : function(item)
21862     {
21863         this.tabs.push( item);
21864         item.navId = this.navId; // not really needed..
21865         this.addBullet();
21866     
21867     },
21868     
21869     getActivePanel : function()
21870     {
21871         var r = false;
21872         Roo.each(this.tabs, function(t) {
21873             if (t.active) {
21874                 r = t;
21875                 return false;
21876             }
21877             return null;
21878         });
21879         return r;
21880         
21881     },
21882     getPanelByName : function(n)
21883     {
21884         var r = false;
21885         Roo.each(this.tabs, function(t) {
21886             if (t.tabId == n) {
21887                 r = t;
21888                 return false;
21889             }
21890             return null;
21891         });
21892         return r;
21893     },
21894     indexOfPanel : function(p)
21895     {
21896         var r = false;
21897         Roo.each(this.tabs, function(t,i) {
21898             if (t.tabId == p.tabId) {
21899                 r = i;
21900                 return false;
21901             }
21902             return null;
21903         });
21904         return r;
21905     },
21906     /**
21907      * show a specific panel
21908      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21909      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21910      */
21911     showPanel : function (pan)
21912     {
21913         if(this.transition || typeof(pan) == 'undefined'){
21914             Roo.log("waiting for the transitionend");
21915             return false;
21916         }
21917         
21918         if (typeof(pan) == 'number') {
21919             pan = this.tabs[pan];
21920         }
21921         
21922         if (typeof(pan) == 'string') {
21923             pan = this.getPanelByName(pan);
21924         }
21925         
21926         var cur = this.getActivePanel();
21927         
21928         if(!pan || !cur){
21929             Roo.log('pan or acitve pan is undefined');
21930             return false;
21931         }
21932         
21933         if (pan.tabId == this.getActivePanel().tabId) {
21934             return true;
21935         }
21936         
21937         if (false === cur.fireEvent('beforedeactivate')) {
21938             return false;
21939         }
21940         
21941         if(this.bullets > 0 && !Roo.isTouch){
21942             this.setActiveBullet(this.indexOfPanel(pan));
21943         }
21944         
21945         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21946             
21947             //class="carousel-item carousel-item-next carousel-item-left"
21948             
21949             this.transition = true;
21950             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21951             var lr = dir == 'next' ? 'left' : 'right';
21952             pan.el.addClass(dir); // or prev
21953             pan.el.addClass('carousel-item-' + dir); // or prev
21954             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21955             cur.el.addClass(lr); // or right
21956             pan.el.addClass(lr);
21957             cur.el.addClass('carousel-item-' +lr); // or right
21958             pan.el.addClass('carousel-item-' +lr);
21959             
21960             
21961             var _this = this;
21962             cur.el.on('transitionend', function() {
21963                 Roo.log("trans end?");
21964                 
21965                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21966                 pan.setActive(true);
21967                 
21968                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21969                 cur.setActive(false);
21970                 
21971                 _this.transition = false;
21972                 
21973             }, this, { single:  true } );
21974             
21975             return true;
21976         }
21977         
21978         cur.setActive(false);
21979         pan.setActive(true);
21980         
21981         return true;
21982         
21983     },
21984     showPanelNext : function()
21985     {
21986         var i = this.indexOfPanel(this.getActivePanel());
21987         
21988         if (i >= this.tabs.length - 1 && !this.autoslide) {
21989             return;
21990         }
21991         
21992         if (i >= this.tabs.length - 1 && this.autoslide) {
21993             i = -1;
21994         }
21995         
21996         this.showPanel(this.tabs[i+1]);
21997     },
21998     
21999     showPanelPrev : function()
22000     {
22001         var i = this.indexOfPanel(this.getActivePanel());
22002         
22003         if (i  < 1 && !this.autoslide) {
22004             return;
22005         }
22006         
22007         if (i < 1 && this.autoslide) {
22008             i = this.tabs.length;
22009         }
22010         
22011         this.showPanel(this.tabs[i-1]);
22012     },
22013     
22014     
22015     addBullet: function()
22016     {
22017         if(!this.bullets || Roo.isTouch){
22018             return;
22019         }
22020         var ctr = this.el.select('.carousel-bullets',true).first();
22021         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22022         var bullet = ctr.createChild({
22023             cls : 'bullet bullet-' + i
22024         },ctr.dom.lastChild);
22025         
22026         
22027         var _this = this;
22028         
22029         bullet.on('click', (function(e, el, o, ii, t){
22030
22031             e.preventDefault();
22032
22033             this.showPanel(ii);
22034
22035             if(this.autoslide && this.slideFn){
22036                 clearInterval(this.slideFn);
22037                 this.slideFn = window.setInterval(function() {
22038                     _this.showPanelNext();
22039                 }, this.timer);
22040             }
22041
22042         }).createDelegate(this, [i, bullet], true));
22043                 
22044         
22045     },
22046      
22047     setActiveBullet : function(i)
22048     {
22049         if(Roo.isTouch){
22050             return;
22051         }
22052         
22053         Roo.each(this.el.select('.bullet', true).elements, function(el){
22054             el.removeClass('selected');
22055         });
22056
22057         var bullet = this.el.select('.bullet-' + i, true).first();
22058         
22059         if(!bullet){
22060             return;
22061         }
22062         
22063         bullet.addClass('selected');
22064     }
22065     
22066     
22067   
22068 });
22069
22070  
22071
22072  
22073  
22074 Roo.apply(Roo.bootstrap.TabGroup, {
22075     
22076     groups: {},
22077      /**
22078     * register a Navigation Group
22079     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22080     */
22081     register : function(navgrp)
22082     {
22083         this.groups[navgrp.navId] = navgrp;
22084         
22085     },
22086     /**
22087     * fetch a Navigation Group based on the navigation ID
22088     * if one does not exist , it will get created.
22089     * @param {string} the navgroup to add
22090     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22091     */
22092     get: function(navId) {
22093         if (typeof(this.groups[navId]) == 'undefined') {
22094             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22095         }
22096         return this.groups[navId] ;
22097     }
22098     
22099     
22100     
22101 });
22102
22103  /*
22104  * - LGPL
22105  *
22106  * TabPanel
22107  * 
22108  */
22109
22110 /**
22111  * @class Roo.bootstrap.TabPanel
22112  * @extends Roo.bootstrap.Component
22113  * Bootstrap TabPanel class
22114  * @cfg {Boolean} active panel active
22115  * @cfg {String} html panel content
22116  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22117  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22118  * @cfg {String} href click to link..
22119  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22120  * 
22121  * 
22122  * @constructor
22123  * Create a new TabPanel
22124  * @param {Object} config The config object
22125  */
22126
22127 Roo.bootstrap.TabPanel = function(config){
22128     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22129     this.addEvents({
22130         /**
22131              * @event changed
22132              * Fires when the active status changes
22133              * @param {Roo.bootstrap.TabPanel} this
22134              * @param {Boolean} state the new state
22135             
22136          */
22137         'changed': true,
22138         /**
22139              * @event beforedeactivate
22140              * Fires before a tab is de-activated - can be used to do validation on a form.
22141              * @param {Roo.bootstrap.TabPanel} this
22142              * @return {Boolean} false if there is an error
22143             
22144          */
22145         'beforedeactivate': true
22146      });
22147     
22148     this.tabId = this.tabId || Roo.id();
22149   
22150 };
22151
22152 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22153     
22154     active: false,
22155     html: false,
22156     tabId: false,
22157     navId : false,
22158     href : '',
22159     touchSlide : false,
22160     getAutoCreate : function(){
22161         
22162         
22163         var cfg = {
22164             tag: 'div',
22165             // item is needed for carousel - not sure if it has any effect otherwise
22166             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22167             html: this.html || ''
22168         };
22169         
22170         if(this.active){
22171             cfg.cls += ' active';
22172         }
22173         
22174         if(this.tabId){
22175             cfg.tabId = this.tabId;
22176         }
22177         
22178         
22179         
22180         return cfg;
22181     },
22182     
22183     initEvents:  function()
22184     {
22185         var p = this.parent();
22186         
22187         this.navId = this.navId || p.navId;
22188         
22189         if (typeof(this.navId) != 'undefined') {
22190             // not really needed.. but just in case.. parent should be a NavGroup.
22191             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22192             
22193             tg.register(this);
22194             
22195             var i = tg.tabs.length - 1;
22196             
22197             if(this.active && tg.bullets > 0 && i < tg.bullets){
22198                 tg.setActiveBullet(i);
22199             }
22200         }
22201         
22202         this.el.on('click', this.onClick, this);
22203         
22204         if(Roo.isTouch && this.touchSlide){
22205             this.el.on("touchstart", this.onTouchStart, this);
22206             this.el.on("touchmove", this.onTouchMove, this);
22207             this.el.on("touchend", this.onTouchEnd, this);
22208         }
22209         
22210     },
22211     
22212     onRender : function(ct, position)
22213     {
22214         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22215     },
22216     
22217     setActive : function(state)
22218     {
22219         Roo.log("panel - set active " + this.tabId + "=" + state);
22220         
22221         this.active = state;
22222         if (!state) {
22223             this.el.removeClass('active');
22224             
22225         } else  if (!this.el.hasClass('active')) {
22226             this.el.addClass('active');
22227         }
22228         
22229         this.fireEvent('changed', this, state);
22230     },
22231     
22232     onClick : function(e)
22233     {
22234         e.preventDefault();
22235         
22236         if(!this.href.length){
22237             return;
22238         }
22239         
22240         window.location.href = this.href;
22241     },
22242     
22243     startX : 0,
22244     startY : 0,
22245     endX : 0,
22246     endY : 0,
22247     swiping : false,
22248     
22249     onTouchStart : function(e)
22250     {
22251         this.swiping = false;
22252         
22253         this.startX = e.browserEvent.touches[0].clientX;
22254         this.startY = e.browserEvent.touches[0].clientY;
22255     },
22256     
22257     onTouchMove : function(e)
22258     {
22259         this.swiping = true;
22260         
22261         this.endX = e.browserEvent.touches[0].clientX;
22262         this.endY = e.browserEvent.touches[0].clientY;
22263     },
22264     
22265     onTouchEnd : function(e)
22266     {
22267         if(!this.swiping){
22268             this.onClick(e);
22269             return;
22270         }
22271         
22272         var tabGroup = this.parent();
22273         
22274         if(this.endX > this.startX){ // swiping right
22275             tabGroup.showPanelPrev();
22276             return;
22277         }
22278         
22279         if(this.startX > this.endX){ // swiping left
22280             tabGroup.showPanelNext();
22281             return;
22282         }
22283     }
22284     
22285     
22286 });
22287  
22288
22289  
22290
22291  /*
22292  * - LGPL
22293  *
22294  * DateField
22295  * 
22296  */
22297
22298 /**
22299  * @class Roo.bootstrap.DateField
22300  * @extends Roo.bootstrap.Input
22301  * Bootstrap DateField class
22302  * @cfg {Number} weekStart default 0
22303  * @cfg {String} viewMode default empty, (months|years)
22304  * @cfg {String} minViewMode default empty, (months|years)
22305  * @cfg {Number} startDate default -Infinity
22306  * @cfg {Number} endDate default Infinity
22307  * @cfg {Boolean} todayHighlight default false
22308  * @cfg {Boolean} todayBtn default false
22309  * @cfg {Boolean} calendarWeeks default false
22310  * @cfg {Object} daysOfWeekDisabled default empty
22311  * @cfg {Boolean} singleMode default false (true | false)
22312  * 
22313  * @cfg {Boolean} keyboardNavigation default true
22314  * @cfg {String} language default en
22315  * 
22316  * @constructor
22317  * Create a new DateField
22318  * @param {Object} config The config object
22319  */
22320
22321 Roo.bootstrap.DateField = function(config){
22322     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22323      this.addEvents({
22324             /**
22325              * @event show
22326              * Fires when this field show.
22327              * @param {Roo.bootstrap.DateField} this
22328              * @param {Mixed} date The date value
22329              */
22330             show : true,
22331             /**
22332              * @event show
22333              * Fires when this field hide.
22334              * @param {Roo.bootstrap.DateField} this
22335              * @param {Mixed} date The date value
22336              */
22337             hide : true,
22338             /**
22339              * @event select
22340              * Fires when select a date.
22341              * @param {Roo.bootstrap.DateField} this
22342              * @param {Mixed} date The date value
22343              */
22344             select : true,
22345             /**
22346              * @event beforeselect
22347              * Fires when before select a date.
22348              * @param {Roo.bootstrap.DateField} this
22349              * @param {Mixed} date The date value
22350              */
22351             beforeselect : true
22352         });
22353 };
22354
22355 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22356     
22357     /**
22358      * @cfg {String} format
22359      * The default date format string which can be overriden for localization support.  The format must be
22360      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22361      */
22362     format : "m/d/y",
22363     /**
22364      * @cfg {String} altFormats
22365      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22366      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22367      */
22368     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22369     
22370     weekStart : 0,
22371     
22372     viewMode : '',
22373     
22374     minViewMode : '',
22375     
22376     todayHighlight : false,
22377     
22378     todayBtn: false,
22379     
22380     language: 'en',
22381     
22382     keyboardNavigation: true,
22383     
22384     calendarWeeks: false,
22385     
22386     startDate: -Infinity,
22387     
22388     endDate: Infinity,
22389     
22390     daysOfWeekDisabled: [],
22391     
22392     _events: [],
22393     
22394     singleMode : false,
22395     
22396     UTCDate: function()
22397     {
22398         return new Date(Date.UTC.apply(Date, arguments));
22399     },
22400     
22401     UTCToday: function()
22402     {
22403         var today = new Date();
22404         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22405     },
22406     
22407     getDate: function() {
22408             var d = this.getUTCDate();
22409             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22410     },
22411     
22412     getUTCDate: function() {
22413             return this.date;
22414     },
22415     
22416     setDate: function(d) {
22417             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22418     },
22419     
22420     setUTCDate: function(d) {
22421             this.date = d;
22422             this.setValue(this.formatDate(this.date));
22423     },
22424         
22425     onRender: function(ct, position)
22426     {
22427         
22428         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22429         
22430         this.language = this.language || 'en';
22431         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22432         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22433         
22434         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22435         this.format = this.format || 'm/d/y';
22436         this.isInline = false;
22437         this.isInput = true;
22438         this.component = this.el.select('.add-on', true).first() || false;
22439         this.component = (this.component && this.component.length === 0) ? false : this.component;
22440         this.hasInput = this.component && this.inputEl().length;
22441         
22442         if (typeof(this.minViewMode === 'string')) {
22443             switch (this.minViewMode) {
22444                 case 'months':
22445                     this.minViewMode = 1;
22446                     break;
22447                 case 'years':
22448                     this.minViewMode = 2;
22449                     break;
22450                 default:
22451                     this.minViewMode = 0;
22452                     break;
22453             }
22454         }
22455         
22456         if (typeof(this.viewMode === 'string')) {
22457             switch (this.viewMode) {
22458                 case 'months':
22459                     this.viewMode = 1;
22460                     break;
22461                 case 'years':
22462                     this.viewMode = 2;
22463                     break;
22464                 default:
22465                     this.viewMode = 0;
22466                     break;
22467             }
22468         }
22469                 
22470         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22471         
22472 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22473         
22474         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22475         
22476         this.picker().on('mousedown', this.onMousedown, this);
22477         this.picker().on('click', this.onClick, this);
22478         
22479         this.picker().addClass('datepicker-dropdown');
22480         
22481         this.startViewMode = this.viewMode;
22482         
22483         if(this.singleMode){
22484             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22485                 v.setVisibilityMode(Roo.Element.DISPLAY);
22486                 v.hide();
22487             });
22488             
22489             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22490                 v.setStyle('width', '189px');
22491             });
22492         }
22493         
22494         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22495             if(!this.calendarWeeks){
22496                 v.remove();
22497                 return;
22498             }
22499             
22500             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22501             v.attr('colspan', function(i, val){
22502                 return parseInt(val) + 1;
22503             });
22504         });
22505                         
22506         
22507         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22508         
22509         this.setStartDate(this.startDate);
22510         this.setEndDate(this.endDate);
22511         
22512         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22513         
22514         this.fillDow();
22515         this.fillMonths();
22516         this.update();
22517         this.showMode();
22518         
22519         if(this.isInline) {
22520             this.showPopup();
22521         }
22522     },
22523     
22524     picker : function()
22525     {
22526         return this.pickerEl;
22527 //        return this.el.select('.datepicker', true).first();
22528     },
22529     
22530     fillDow: function()
22531     {
22532         var dowCnt = this.weekStart;
22533         
22534         var dow = {
22535             tag: 'tr',
22536             cn: [
22537                 
22538             ]
22539         };
22540         
22541         if(this.calendarWeeks){
22542             dow.cn.push({
22543                 tag: 'th',
22544                 cls: 'cw',
22545                 html: '&nbsp;'
22546             })
22547         }
22548         
22549         while (dowCnt < this.weekStart + 7) {
22550             dow.cn.push({
22551                 tag: 'th',
22552                 cls: 'dow',
22553                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22554             });
22555         }
22556         
22557         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22558     },
22559     
22560     fillMonths: function()
22561     {    
22562         var i = 0;
22563         var months = this.picker().select('>.datepicker-months td', true).first();
22564         
22565         months.dom.innerHTML = '';
22566         
22567         while (i < 12) {
22568             var month = {
22569                 tag: 'span',
22570                 cls: 'month',
22571                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22572             };
22573             
22574             months.createChild(month);
22575         }
22576         
22577     },
22578     
22579     update: function()
22580     {
22581         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;
22582         
22583         if (this.date < this.startDate) {
22584             this.viewDate = new Date(this.startDate);
22585         } else if (this.date > this.endDate) {
22586             this.viewDate = new Date(this.endDate);
22587         } else {
22588             this.viewDate = new Date(this.date);
22589         }
22590         
22591         this.fill();
22592     },
22593     
22594     fill: function() 
22595     {
22596         var d = new Date(this.viewDate),
22597                 year = d.getUTCFullYear(),
22598                 month = d.getUTCMonth(),
22599                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22600                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22601                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22602                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22603                 currentDate = this.date && this.date.valueOf(),
22604                 today = this.UTCToday();
22605         
22606         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22607         
22608 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22609         
22610 //        this.picker.select('>tfoot th.today').
22611 //                                              .text(dates[this.language].today)
22612 //                                              .toggle(this.todayBtn !== false);
22613     
22614         this.updateNavArrows();
22615         this.fillMonths();
22616                                                 
22617         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22618         
22619         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22620          
22621         prevMonth.setUTCDate(day);
22622         
22623         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22624         
22625         var nextMonth = new Date(prevMonth);
22626         
22627         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22628         
22629         nextMonth = nextMonth.valueOf();
22630         
22631         var fillMonths = false;
22632         
22633         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22634         
22635         while(prevMonth.valueOf() <= nextMonth) {
22636             var clsName = '';
22637             
22638             if (prevMonth.getUTCDay() === this.weekStart) {
22639                 if(fillMonths){
22640                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22641                 }
22642                     
22643                 fillMonths = {
22644                     tag: 'tr',
22645                     cn: []
22646                 };
22647                 
22648                 if(this.calendarWeeks){
22649                     // ISO 8601: First week contains first thursday.
22650                     // ISO also states week starts on Monday, but we can be more abstract here.
22651                     var
22652                     // Start of current week: based on weekstart/current date
22653                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22654                     // Thursday of this week
22655                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22656                     // First Thursday of year, year from thursday
22657                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22658                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22659                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22660                     
22661                     fillMonths.cn.push({
22662                         tag: 'td',
22663                         cls: 'cw',
22664                         html: calWeek
22665                     });
22666                 }
22667             }
22668             
22669             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22670                 clsName += ' old';
22671             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22672                 clsName += ' new';
22673             }
22674             if (this.todayHighlight &&
22675                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22676                 prevMonth.getUTCMonth() == today.getMonth() &&
22677                 prevMonth.getUTCDate() == today.getDate()) {
22678                 clsName += ' today';
22679             }
22680             
22681             if (currentDate && prevMonth.valueOf() === currentDate) {
22682                 clsName += ' active';
22683             }
22684             
22685             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22686                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22687                     clsName += ' disabled';
22688             }
22689             
22690             fillMonths.cn.push({
22691                 tag: 'td',
22692                 cls: 'day ' + clsName,
22693                 html: prevMonth.getDate()
22694             });
22695             
22696             prevMonth.setDate(prevMonth.getDate()+1);
22697         }
22698           
22699         var currentYear = this.date && this.date.getUTCFullYear();
22700         var currentMonth = this.date && this.date.getUTCMonth();
22701         
22702         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22703         
22704         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22705             v.removeClass('active');
22706             
22707             if(currentYear === year && k === currentMonth){
22708                 v.addClass('active');
22709             }
22710             
22711             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22712                 v.addClass('disabled');
22713             }
22714             
22715         });
22716         
22717         
22718         year = parseInt(year/10, 10) * 10;
22719         
22720         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22721         
22722         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22723         
22724         year -= 1;
22725         for (var i = -1; i < 11; i++) {
22726             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22727                 tag: 'span',
22728                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22729                 html: year
22730             });
22731             
22732             year += 1;
22733         }
22734     },
22735     
22736     showMode: function(dir) 
22737     {
22738         if (dir) {
22739             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22740         }
22741         
22742         Roo.each(this.picker().select('>div',true).elements, function(v){
22743             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22744             v.hide();
22745         });
22746         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22747     },
22748     
22749     place: function()
22750     {
22751         if(this.isInline) {
22752             return;
22753         }
22754         
22755         this.picker().removeClass(['bottom', 'top']);
22756         
22757         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22758             /*
22759              * place to the top of element!
22760              *
22761              */
22762             
22763             this.picker().addClass('top');
22764             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22765             
22766             return;
22767         }
22768         
22769         this.picker().addClass('bottom');
22770         
22771         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22772     },
22773     
22774     parseDate : function(value)
22775     {
22776         if(!value || value instanceof Date){
22777             return value;
22778         }
22779         var v = Date.parseDate(value, this.format);
22780         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22781             v = Date.parseDate(value, 'Y-m-d');
22782         }
22783         if(!v && this.altFormats){
22784             if(!this.altFormatsArray){
22785                 this.altFormatsArray = this.altFormats.split("|");
22786             }
22787             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22788                 v = Date.parseDate(value, this.altFormatsArray[i]);
22789             }
22790         }
22791         return v;
22792     },
22793     
22794     formatDate : function(date, fmt)
22795     {   
22796         return (!date || !(date instanceof Date)) ?
22797         date : date.dateFormat(fmt || this.format);
22798     },
22799     
22800     onFocus : function()
22801     {
22802         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22803         this.showPopup();
22804     },
22805     
22806     onBlur : function()
22807     {
22808         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22809         
22810         var d = this.inputEl().getValue();
22811         
22812         this.setValue(d);
22813                 
22814         this.hidePopup();
22815     },
22816     
22817     showPopup : function()
22818     {
22819         this.picker().show();
22820         this.update();
22821         this.place();
22822         
22823         this.fireEvent('showpopup', this, this.date);
22824     },
22825     
22826     hidePopup : function()
22827     {
22828         if(this.isInline) {
22829             return;
22830         }
22831         this.picker().hide();
22832         this.viewMode = this.startViewMode;
22833         this.showMode();
22834         
22835         this.fireEvent('hidepopup', this, this.date);
22836         
22837     },
22838     
22839     onMousedown: function(e)
22840     {
22841         e.stopPropagation();
22842         e.preventDefault();
22843     },
22844     
22845     keyup: function(e)
22846     {
22847         Roo.bootstrap.DateField.superclass.keyup.call(this);
22848         this.update();
22849     },
22850
22851     setValue: function(v)
22852     {
22853         if(this.fireEvent('beforeselect', this, v) !== false){
22854             var d = new Date(this.parseDate(v) ).clearTime();
22855         
22856             if(isNaN(d.getTime())){
22857                 this.date = this.viewDate = '';
22858                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22859                 return;
22860             }
22861
22862             v = this.formatDate(d);
22863
22864             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22865
22866             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22867
22868             this.update();
22869
22870             this.fireEvent('select', this, this.date);
22871         }
22872     },
22873     
22874     getValue: function()
22875     {
22876         return this.formatDate(this.date);
22877     },
22878     
22879     fireKey: function(e)
22880     {
22881         if (!this.picker().isVisible()){
22882             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22883                 this.showPopup();
22884             }
22885             return;
22886         }
22887         
22888         var dateChanged = false,
22889         dir, day, month,
22890         newDate, newViewDate;
22891         
22892         switch(e.keyCode){
22893             case 27: // escape
22894                 this.hidePopup();
22895                 e.preventDefault();
22896                 break;
22897             case 37: // left
22898             case 39: // right
22899                 if (!this.keyboardNavigation) {
22900                     break;
22901                 }
22902                 dir = e.keyCode == 37 ? -1 : 1;
22903                 
22904                 if (e.ctrlKey){
22905                     newDate = this.moveYear(this.date, dir);
22906                     newViewDate = this.moveYear(this.viewDate, dir);
22907                 } else if (e.shiftKey){
22908                     newDate = this.moveMonth(this.date, dir);
22909                     newViewDate = this.moveMonth(this.viewDate, dir);
22910                 } else {
22911                     newDate = new Date(this.date);
22912                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22913                     newViewDate = new Date(this.viewDate);
22914                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22915                 }
22916                 if (this.dateWithinRange(newDate)){
22917                     this.date = newDate;
22918                     this.viewDate = newViewDate;
22919                     this.setValue(this.formatDate(this.date));
22920 //                    this.update();
22921                     e.preventDefault();
22922                     dateChanged = true;
22923                 }
22924                 break;
22925             case 38: // up
22926             case 40: // down
22927                 if (!this.keyboardNavigation) {
22928                     break;
22929                 }
22930                 dir = e.keyCode == 38 ? -1 : 1;
22931                 if (e.ctrlKey){
22932                     newDate = this.moveYear(this.date, dir);
22933                     newViewDate = this.moveYear(this.viewDate, dir);
22934                 } else if (e.shiftKey){
22935                     newDate = this.moveMonth(this.date, dir);
22936                     newViewDate = this.moveMonth(this.viewDate, dir);
22937                 } else {
22938                     newDate = new Date(this.date);
22939                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22940                     newViewDate = new Date(this.viewDate);
22941                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22942                 }
22943                 if (this.dateWithinRange(newDate)){
22944                     this.date = newDate;
22945                     this.viewDate = newViewDate;
22946                     this.setValue(this.formatDate(this.date));
22947 //                    this.update();
22948                     e.preventDefault();
22949                     dateChanged = true;
22950                 }
22951                 break;
22952             case 13: // enter
22953                 this.setValue(this.formatDate(this.date));
22954                 this.hidePopup();
22955                 e.preventDefault();
22956                 break;
22957             case 9: // tab
22958                 this.setValue(this.formatDate(this.date));
22959                 this.hidePopup();
22960                 break;
22961             case 16: // shift
22962             case 17: // ctrl
22963             case 18: // alt
22964                 break;
22965             default :
22966                 this.hidePopup();
22967                 
22968         }
22969     },
22970     
22971     
22972     onClick: function(e) 
22973     {
22974         e.stopPropagation();
22975         e.preventDefault();
22976         
22977         var target = e.getTarget();
22978         
22979         if(target.nodeName.toLowerCase() === 'i'){
22980             target = Roo.get(target).dom.parentNode;
22981         }
22982         
22983         var nodeName = target.nodeName;
22984         var className = target.className;
22985         var html = target.innerHTML;
22986         //Roo.log(nodeName);
22987         
22988         switch(nodeName.toLowerCase()) {
22989             case 'th':
22990                 switch(className) {
22991                     case 'switch':
22992                         this.showMode(1);
22993                         break;
22994                     case 'prev':
22995                     case 'next':
22996                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22997                         switch(this.viewMode){
22998                                 case 0:
22999                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23000                                         break;
23001                                 case 1:
23002                                 case 2:
23003                                         this.viewDate = this.moveYear(this.viewDate, dir);
23004                                         break;
23005                         }
23006                         this.fill();
23007                         break;
23008                     case 'today':
23009                         var date = new Date();
23010                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23011 //                        this.fill()
23012                         this.setValue(this.formatDate(this.date));
23013                         
23014                         this.hidePopup();
23015                         break;
23016                 }
23017                 break;
23018             case 'span':
23019                 if (className.indexOf('disabled') < 0) {
23020                 if (!this.viewDate) {
23021                     this.viewDate = new Date();
23022                 }
23023                 this.viewDate.setUTCDate(1);
23024                     if (className.indexOf('month') > -1) {
23025                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23026                     } else {
23027                         var year = parseInt(html, 10) || 0;
23028                         this.viewDate.setUTCFullYear(year);
23029                         
23030                     }
23031                     
23032                     if(this.singleMode){
23033                         this.setValue(this.formatDate(this.viewDate));
23034                         this.hidePopup();
23035                         return;
23036                     }
23037                     
23038                     this.showMode(-1);
23039                     this.fill();
23040                 }
23041                 break;
23042                 
23043             case 'td':
23044                 //Roo.log(className);
23045                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23046                     var day = parseInt(html, 10) || 1;
23047                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23048                         month = (this.viewDate || new Date()).getUTCMonth();
23049
23050                     if (className.indexOf('old') > -1) {
23051                         if(month === 0 ){
23052                             month = 11;
23053                             year -= 1;
23054                         }else{
23055                             month -= 1;
23056                         }
23057                     } else if (className.indexOf('new') > -1) {
23058                         if (month == 11) {
23059                             month = 0;
23060                             year += 1;
23061                         } else {
23062                             month += 1;
23063                         }
23064                     }
23065                     //Roo.log([year,month,day]);
23066                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23067                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23068 //                    this.fill();
23069                     //Roo.log(this.formatDate(this.date));
23070                     this.setValue(this.formatDate(this.date));
23071                     this.hidePopup();
23072                 }
23073                 break;
23074         }
23075     },
23076     
23077     setStartDate: function(startDate)
23078     {
23079         this.startDate = startDate || -Infinity;
23080         if (this.startDate !== -Infinity) {
23081             this.startDate = this.parseDate(this.startDate);
23082         }
23083         this.update();
23084         this.updateNavArrows();
23085     },
23086
23087     setEndDate: function(endDate)
23088     {
23089         this.endDate = endDate || Infinity;
23090         if (this.endDate !== Infinity) {
23091             this.endDate = this.parseDate(this.endDate);
23092         }
23093         this.update();
23094         this.updateNavArrows();
23095     },
23096     
23097     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23098     {
23099         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23100         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23101             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23102         }
23103         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23104             return parseInt(d, 10);
23105         });
23106         this.update();
23107         this.updateNavArrows();
23108     },
23109     
23110     updateNavArrows: function() 
23111     {
23112         if(this.singleMode){
23113             return;
23114         }
23115         
23116         var d = new Date(this.viewDate),
23117         year = d.getUTCFullYear(),
23118         month = d.getUTCMonth();
23119         
23120         Roo.each(this.picker().select('.prev', true).elements, function(v){
23121             v.show();
23122             switch (this.viewMode) {
23123                 case 0:
23124
23125                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23126                         v.hide();
23127                     }
23128                     break;
23129                 case 1:
23130                 case 2:
23131                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23132                         v.hide();
23133                     }
23134                     break;
23135             }
23136         });
23137         
23138         Roo.each(this.picker().select('.next', true).elements, function(v){
23139             v.show();
23140             switch (this.viewMode) {
23141                 case 0:
23142
23143                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23144                         v.hide();
23145                     }
23146                     break;
23147                 case 1:
23148                 case 2:
23149                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23150                         v.hide();
23151                     }
23152                     break;
23153             }
23154         })
23155     },
23156     
23157     moveMonth: function(date, dir)
23158     {
23159         if (!dir) {
23160             return date;
23161         }
23162         var new_date = new Date(date.valueOf()),
23163         day = new_date.getUTCDate(),
23164         month = new_date.getUTCMonth(),
23165         mag = Math.abs(dir),
23166         new_month, test;
23167         dir = dir > 0 ? 1 : -1;
23168         if (mag == 1){
23169             test = dir == -1
23170             // If going back one month, make sure month is not current month
23171             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23172             ? function(){
23173                 return new_date.getUTCMonth() == month;
23174             }
23175             // If going forward one month, make sure month is as expected
23176             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23177             : function(){
23178                 return new_date.getUTCMonth() != new_month;
23179             };
23180             new_month = month + dir;
23181             new_date.setUTCMonth(new_month);
23182             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23183             if (new_month < 0 || new_month > 11) {
23184                 new_month = (new_month + 12) % 12;
23185             }
23186         } else {
23187             // For magnitudes >1, move one month at a time...
23188             for (var i=0; i<mag; i++) {
23189                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23190                 new_date = this.moveMonth(new_date, dir);
23191             }
23192             // ...then reset the day, keeping it in the new month
23193             new_month = new_date.getUTCMonth();
23194             new_date.setUTCDate(day);
23195             test = function(){
23196                 return new_month != new_date.getUTCMonth();
23197             };
23198         }
23199         // Common date-resetting loop -- if date is beyond end of month, make it
23200         // end of month
23201         while (test()){
23202             new_date.setUTCDate(--day);
23203             new_date.setUTCMonth(new_month);
23204         }
23205         return new_date;
23206     },
23207
23208     moveYear: function(date, dir)
23209     {
23210         return this.moveMonth(date, dir*12);
23211     },
23212
23213     dateWithinRange: function(date)
23214     {
23215         return date >= this.startDate && date <= this.endDate;
23216     },
23217
23218     
23219     remove: function() 
23220     {
23221         this.picker().remove();
23222     },
23223     
23224     validateValue : function(value)
23225     {
23226         if(this.getVisibilityEl().hasClass('hidden')){
23227             return true;
23228         }
23229         
23230         if(value.length < 1)  {
23231             if(this.allowBlank){
23232                 return true;
23233             }
23234             return false;
23235         }
23236         
23237         if(value.length < this.minLength){
23238             return false;
23239         }
23240         if(value.length > this.maxLength){
23241             return false;
23242         }
23243         if(this.vtype){
23244             var vt = Roo.form.VTypes;
23245             if(!vt[this.vtype](value, this)){
23246                 return false;
23247             }
23248         }
23249         if(typeof this.validator == "function"){
23250             var msg = this.validator(value);
23251             if(msg !== true){
23252                 return false;
23253             }
23254         }
23255         
23256         if(this.regex && !this.regex.test(value)){
23257             return false;
23258         }
23259         
23260         if(typeof(this.parseDate(value)) == 'undefined'){
23261             return false;
23262         }
23263         
23264         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23265             return false;
23266         }      
23267         
23268         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23269             return false;
23270         } 
23271         
23272         
23273         return true;
23274     },
23275     
23276     reset : function()
23277     {
23278         this.date = this.viewDate = '';
23279         
23280         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23281     }
23282    
23283 });
23284
23285 Roo.apply(Roo.bootstrap.DateField,  {
23286     
23287     head : {
23288         tag: 'thead',
23289         cn: [
23290         {
23291             tag: 'tr',
23292             cn: [
23293             {
23294                 tag: 'th',
23295                 cls: 'prev',
23296                 html: '<i class="fa fa-arrow-left"/>'
23297             },
23298             {
23299                 tag: 'th',
23300                 cls: 'switch',
23301                 colspan: '5'
23302             },
23303             {
23304                 tag: 'th',
23305                 cls: 'next',
23306                 html: '<i class="fa fa-arrow-right"/>'
23307             }
23308
23309             ]
23310         }
23311         ]
23312     },
23313     
23314     content : {
23315         tag: 'tbody',
23316         cn: [
23317         {
23318             tag: 'tr',
23319             cn: [
23320             {
23321                 tag: 'td',
23322                 colspan: '7'
23323             }
23324             ]
23325         }
23326         ]
23327     },
23328     
23329     footer : {
23330         tag: 'tfoot',
23331         cn: [
23332         {
23333             tag: 'tr',
23334             cn: [
23335             {
23336                 tag: 'th',
23337                 colspan: '7',
23338                 cls: 'today'
23339             }
23340                     
23341             ]
23342         }
23343         ]
23344     },
23345     
23346     dates:{
23347         en: {
23348             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23349             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23350             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23351             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23352             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23353             today: "Today"
23354         }
23355     },
23356     
23357     modes: [
23358     {
23359         clsName: 'days',
23360         navFnc: 'Month',
23361         navStep: 1
23362     },
23363     {
23364         clsName: 'months',
23365         navFnc: 'FullYear',
23366         navStep: 1
23367     },
23368     {
23369         clsName: 'years',
23370         navFnc: 'FullYear',
23371         navStep: 10
23372     }]
23373 });
23374
23375 Roo.apply(Roo.bootstrap.DateField,  {
23376   
23377     template : {
23378         tag: 'div',
23379         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23380         cn: [
23381         {
23382             tag: 'div',
23383             cls: 'datepicker-days',
23384             cn: [
23385             {
23386                 tag: 'table',
23387                 cls: 'table-condensed',
23388                 cn:[
23389                 Roo.bootstrap.DateField.head,
23390                 {
23391                     tag: 'tbody'
23392                 },
23393                 Roo.bootstrap.DateField.footer
23394                 ]
23395             }
23396             ]
23397         },
23398         {
23399             tag: 'div',
23400             cls: 'datepicker-months',
23401             cn: [
23402             {
23403                 tag: 'table',
23404                 cls: 'table-condensed',
23405                 cn:[
23406                 Roo.bootstrap.DateField.head,
23407                 Roo.bootstrap.DateField.content,
23408                 Roo.bootstrap.DateField.footer
23409                 ]
23410             }
23411             ]
23412         },
23413         {
23414             tag: 'div',
23415             cls: 'datepicker-years',
23416             cn: [
23417             {
23418                 tag: 'table',
23419                 cls: 'table-condensed',
23420                 cn:[
23421                 Roo.bootstrap.DateField.head,
23422                 Roo.bootstrap.DateField.content,
23423                 Roo.bootstrap.DateField.footer
23424                 ]
23425             }
23426             ]
23427         }
23428         ]
23429     }
23430 });
23431
23432  
23433
23434  /*
23435  * - LGPL
23436  *
23437  * TimeField
23438  * 
23439  */
23440
23441 /**
23442  * @class Roo.bootstrap.TimeField
23443  * @extends Roo.bootstrap.Input
23444  * Bootstrap DateField class
23445  * 
23446  * 
23447  * @constructor
23448  * Create a new TimeField
23449  * @param {Object} config The config object
23450  */
23451
23452 Roo.bootstrap.TimeField = function(config){
23453     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23454     this.addEvents({
23455             /**
23456              * @event show
23457              * Fires when this field show.
23458              * @param {Roo.bootstrap.DateField} thisthis
23459              * @param {Mixed} date The date value
23460              */
23461             show : true,
23462             /**
23463              * @event show
23464              * Fires when this field hide.
23465              * @param {Roo.bootstrap.DateField} this
23466              * @param {Mixed} date The date value
23467              */
23468             hide : true,
23469             /**
23470              * @event select
23471              * Fires when select a date.
23472              * @param {Roo.bootstrap.DateField} this
23473              * @param {Mixed} date The date value
23474              */
23475             select : true
23476         });
23477 };
23478
23479 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23480     
23481     /**
23482      * @cfg {String} format
23483      * The default time format string which can be overriden for localization support.  The format must be
23484      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23485      */
23486     format : "H:i",
23487
23488     getAutoCreate : function()
23489     {
23490         this.after = '<i class="fa far fa-clock"></i>';
23491         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23492         
23493          
23494     },
23495     onRender: function(ct, position)
23496     {
23497         
23498         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23499                 
23500         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23501         
23502         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23503         
23504         this.pop = this.picker().select('>.datepicker-time',true).first();
23505         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23506         
23507         this.picker().on('mousedown', this.onMousedown, this);
23508         this.picker().on('click', this.onClick, this);
23509         
23510         this.picker().addClass('datepicker-dropdown');
23511     
23512         this.fillTime();
23513         this.update();
23514             
23515         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23516         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23517         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23518         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23519         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23520         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23521
23522     },
23523     
23524     fireKey: function(e){
23525         if (!this.picker().isVisible()){
23526             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23527                 this.show();
23528             }
23529             return;
23530         }
23531
23532         e.preventDefault();
23533         
23534         switch(e.keyCode){
23535             case 27: // escape
23536                 this.hide();
23537                 break;
23538             case 37: // left
23539             case 39: // right
23540                 this.onTogglePeriod();
23541                 break;
23542             case 38: // up
23543                 this.onIncrementMinutes();
23544                 break;
23545             case 40: // down
23546                 this.onDecrementMinutes();
23547                 break;
23548             case 13: // enter
23549             case 9: // tab
23550                 this.setTime();
23551                 break;
23552         }
23553     },
23554     
23555     onClick: function(e) {
23556         e.stopPropagation();
23557         e.preventDefault();
23558     },
23559     
23560     picker : function()
23561     {
23562         return this.pickerEl;
23563     },
23564     
23565     fillTime: function()
23566     {    
23567         var time = this.pop.select('tbody', true).first();
23568         
23569         time.dom.innerHTML = '';
23570         
23571         time.createChild({
23572             tag: 'tr',
23573             cn: [
23574                 {
23575                     tag: 'td',
23576                     cn: [
23577                         {
23578                             tag: 'a',
23579                             href: '#',
23580                             cls: 'btn',
23581                             cn: [
23582                                 {
23583                                     tag: 'i',
23584                                     cls: 'hours-up fa fas fa-chevron-up'
23585                                 }
23586                             ]
23587                         } 
23588                     ]
23589                 },
23590                 {
23591                     tag: 'td',
23592                     cls: 'separator'
23593                 },
23594                 {
23595                     tag: 'td',
23596                     cn: [
23597                         {
23598                             tag: 'a',
23599                             href: '#',
23600                             cls: 'btn',
23601                             cn: [
23602                                 {
23603                                     tag: 'i',
23604                                     cls: 'minutes-up fa fas fa-chevron-up'
23605                                 }
23606                             ]
23607                         }
23608                     ]
23609                 },
23610                 {
23611                     tag: 'td',
23612                     cls: 'separator'
23613                 }
23614             ]
23615         });
23616         
23617         time.createChild({
23618             tag: 'tr',
23619             cn: [
23620                 {
23621                     tag: 'td',
23622                     cn: [
23623                         {
23624                             tag: 'span',
23625                             cls: 'timepicker-hour',
23626                             html: '00'
23627                         }  
23628                     ]
23629                 },
23630                 {
23631                     tag: 'td',
23632                     cls: 'separator',
23633                     html: ':'
23634                 },
23635                 {
23636                     tag: 'td',
23637                     cn: [
23638                         {
23639                             tag: 'span',
23640                             cls: 'timepicker-minute',
23641                             html: '00'
23642                         }  
23643                     ]
23644                 },
23645                 {
23646                     tag: 'td',
23647                     cls: 'separator'
23648                 },
23649                 {
23650                     tag: 'td',
23651                     cn: [
23652                         {
23653                             tag: 'button',
23654                             type: 'button',
23655                             cls: 'btn btn-primary period',
23656                             html: 'AM'
23657                             
23658                         }
23659                     ]
23660                 }
23661             ]
23662         });
23663         
23664         time.createChild({
23665             tag: 'tr',
23666             cn: [
23667                 {
23668                     tag: 'td',
23669                     cn: [
23670                         {
23671                             tag: 'a',
23672                             href: '#',
23673                             cls: 'btn',
23674                             cn: [
23675                                 {
23676                                     tag: 'span',
23677                                     cls: 'hours-down fa fas fa-chevron-down'
23678                                 }
23679                             ]
23680                         }
23681                     ]
23682                 },
23683                 {
23684                     tag: 'td',
23685                     cls: 'separator'
23686                 },
23687                 {
23688                     tag: 'td',
23689                     cn: [
23690                         {
23691                             tag: 'a',
23692                             href: '#',
23693                             cls: 'btn',
23694                             cn: [
23695                                 {
23696                                     tag: 'span',
23697                                     cls: 'minutes-down fa fas fa-chevron-down'
23698                                 }
23699                             ]
23700                         }
23701                     ]
23702                 },
23703                 {
23704                     tag: 'td',
23705                     cls: 'separator'
23706                 }
23707             ]
23708         });
23709         
23710     },
23711     
23712     update: function()
23713     {
23714         
23715         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23716         
23717         this.fill();
23718     },
23719     
23720     fill: function() 
23721     {
23722         var hours = this.time.getHours();
23723         var minutes = this.time.getMinutes();
23724         var period = 'AM';
23725         
23726         if(hours > 11){
23727             period = 'PM';
23728         }
23729         
23730         if(hours == 0){
23731             hours = 12;
23732         }
23733         
23734         
23735         if(hours > 12){
23736             hours = hours - 12;
23737         }
23738         
23739         if(hours < 10){
23740             hours = '0' + hours;
23741         }
23742         
23743         if(minutes < 10){
23744             minutes = '0' + minutes;
23745         }
23746         
23747         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23748         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23749         this.pop.select('button', true).first().dom.innerHTML = period;
23750         
23751     },
23752     
23753     place: function()
23754     {   
23755         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23756         
23757         var cls = ['bottom'];
23758         
23759         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23760             cls.pop();
23761             cls.push('top');
23762         }
23763         
23764         cls.push('right');
23765         
23766         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23767             cls.pop();
23768             cls.push('left');
23769         }
23770         //this.picker().setXY(20000,20000);
23771         this.picker().addClass(cls.join('-'));
23772         
23773         var _this = this;
23774         
23775         Roo.each(cls, function(c){
23776             if(c == 'bottom'){
23777                 (function() {
23778                  //  
23779                 }).defer(200);
23780                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23781                 //_this.picker().setTop(_this.inputEl().getHeight());
23782                 return;
23783             }
23784             if(c == 'top'){
23785                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23786                 
23787                 //_this.picker().setTop(0 - _this.picker().getHeight());
23788                 return;
23789             }
23790             /*
23791             if(c == 'left'){
23792                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23793                 return;
23794             }
23795             if(c == 'right'){
23796                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23797                 return;
23798             }
23799             */
23800         });
23801         
23802     },
23803   
23804     onFocus : function()
23805     {
23806         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23807         this.show();
23808     },
23809     
23810     onBlur : function()
23811     {
23812         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23813         this.hide();
23814     },
23815     
23816     show : function()
23817     {
23818         this.picker().show();
23819         this.pop.show();
23820         this.update();
23821         this.place();
23822         
23823         this.fireEvent('show', this, this.date);
23824     },
23825     
23826     hide : function()
23827     {
23828         this.picker().hide();
23829         this.pop.hide();
23830         
23831         this.fireEvent('hide', this, this.date);
23832     },
23833     
23834     setTime : function()
23835     {
23836         this.hide();
23837         this.setValue(this.time.format(this.format));
23838         
23839         this.fireEvent('select', this, this.date);
23840         
23841         
23842     },
23843     
23844     onMousedown: function(e){
23845         e.stopPropagation();
23846         e.preventDefault();
23847     },
23848     
23849     onIncrementHours: function()
23850     {
23851         Roo.log('onIncrementHours');
23852         this.time = this.time.add(Date.HOUR, 1);
23853         this.update();
23854         
23855     },
23856     
23857     onDecrementHours: function()
23858     {
23859         Roo.log('onDecrementHours');
23860         this.time = this.time.add(Date.HOUR, -1);
23861         this.update();
23862     },
23863     
23864     onIncrementMinutes: function()
23865     {
23866         Roo.log('onIncrementMinutes');
23867         this.time = this.time.add(Date.MINUTE, 1);
23868         this.update();
23869     },
23870     
23871     onDecrementMinutes: function()
23872     {
23873         Roo.log('onDecrementMinutes');
23874         this.time = this.time.add(Date.MINUTE, -1);
23875         this.update();
23876     },
23877     
23878     onTogglePeriod: function()
23879     {
23880         Roo.log('onTogglePeriod');
23881         this.time = this.time.add(Date.HOUR, 12);
23882         this.update();
23883     }
23884     
23885    
23886 });
23887  
23888
23889 Roo.apply(Roo.bootstrap.TimeField,  {
23890   
23891     template : {
23892         tag: 'div',
23893         cls: 'datepicker dropdown-menu',
23894         cn: [
23895             {
23896                 tag: 'div',
23897                 cls: 'datepicker-time',
23898                 cn: [
23899                 {
23900                     tag: 'table',
23901                     cls: 'table-condensed',
23902                     cn:[
23903                         {
23904                             tag: 'tbody',
23905                             cn: [
23906                                 {
23907                                     tag: 'tr',
23908                                     cn: [
23909                                     {
23910                                         tag: 'td',
23911                                         colspan: '7'
23912                                     }
23913                                     ]
23914                                 }
23915                             ]
23916                         },
23917                         {
23918                             tag: 'tfoot',
23919                             cn: [
23920                                 {
23921                                     tag: 'tr',
23922                                     cn: [
23923                                     {
23924                                         tag: 'th',
23925                                         colspan: '7',
23926                                         cls: '',
23927                                         cn: [
23928                                             {
23929                                                 tag: 'button',
23930                                                 cls: 'btn btn-info ok',
23931                                                 html: 'OK'
23932                                             }
23933                                         ]
23934                                     }
23935                     
23936                                     ]
23937                                 }
23938                             ]
23939                         }
23940                     ]
23941                 }
23942                 ]
23943             }
23944         ]
23945     }
23946 });
23947
23948  
23949
23950  /*
23951  * - LGPL
23952  *
23953  * MonthField
23954  * 
23955  */
23956
23957 /**
23958  * @class Roo.bootstrap.MonthField
23959  * @extends Roo.bootstrap.Input
23960  * Bootstrap MonthField class
23961  * 
23962  * @cfg {String} language default en
23963  * 
23964  * @constructor
23965  * Create a new MonthField
23966  * @param {Object} config The config object
23967  */
23968
23969 Roo.bootstrap.MonthField = function(config){
23970     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23971     
23972     this.addEvents({
23973         /**
23974          * @event show
23975          * Fires when this field show.
23976          * @param {Roo.bootstrap.MonthField} this
23977          * @param {Mixed} date The date value
23978          */
23979         show : true,
23980         /**
23981          * @event show
23982          * Fires when this field hide.
23983          * @param {Roo.bootstrap.MonthField} this
23984          * @param {Mixed} date The date value
23985          */
23986         hide : true,
23987         /**
23988          * @event select
23989          * Fires when select a date.
23990          * @param {Roo.bootstrap.MonthField} this
23991          * @param {String} oldvalue The old value
23992          * @param {String} newvalue The new value
23993          */
23994         select : true
23995     });
23996 };
23997
23998 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23999     
24000     onRender: function(ct, position)
24001     {
24002         
24003         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24004         
24005         this.language = this.language || 'en';
24006         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24007         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24008         
24009         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24010         this.isInline = false;
24011         this.isInput = true;
24012         this.component = this.el.select('.add-on', true).first() || false;
24013         this.component = (this.component && this.component.length === 0) ? false : this.component;
24014         this.hasInput = this.component && this.inputEL().length;
24015         
24016         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24017         
24018         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24019         
24020         this.picker().on('mousedown', this.onMousedown, this);
24021         this.picker().on('click', this.onClick, this);
24022         
24023         this.picker().addClass('datepicker-dropdown');
24024         
24025         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24026             v.setStyle('width', '189px');
24027         });
24028         
24029         this.fillMonths();
24030         
24031         this.update();
24032         
24033         if(this.isInline) {
24034             this.show();
24035         }
24036         
24037     },
24038     
24039     setValue: function(v, suppressEvent)
24040     {   
24041         var o = this.getValue();
24042         
24043         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24044         
24045         this.update();
24046
24047         if(suppressEvent !== true){
24048             this.fireEvent('select', this, o, v);
24049         }
24050         
24051     },
24052     
24053     getValue: function()
24054     {
24055         return this.value;
24056     },
24057     
24058     onClick: function(e) 
24059     {
24060         e.stopPropagation();
24061         e.preventDefault();
24062         
24063         var target = e.getTarget();
24064         
24065         if(target.nodeName.toLowerCase() === 'i'){
24066             target = Roo.get(target).dom.parentNode;
24067         }
24068         
24069         var nodeName = target.nodeName;
24070         var className = target.className;
24071         var html = target.innerHTML;
24072         
24073         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24074             return;
24075         }
24076         
24077         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24078         
24079         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24080         
24081         this.hide();
24082                         
24083     },
24084     
24085     picker : function()
24086     {
24087         return this.pickerEl;
24088     },
24089     
24090     fillMonths: function()
24091     {    
24092         var i = 0;
24093         var months = this.picker().select('>.datepicker-months td', true).first();
24094         
24095         months.dom.innerHTML = '';
24096         
24097         while (i < 12) {
24098             var month = {
24099                 tag: 'span',
24100                 cls: 'month',
24101                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24102             };
24103             
24104             months.createChild(month);
24105         }
24106         
24107     },
24108     
24109     update: function()
24110     {
24111         var _this = this;
24112         
24113         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24114             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24115         }
24116         
24117         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24118             e.removeClass('active');
24119             
24120             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24121                 e.addClass('active');
24122             }
24123         })
24124     },
24125     
24126     place: function()
24127     {
24128         if(this.isInline) {
24129             return;
24130         }
24131         
24132         this.picker().removeClass(['bottom', 'top']);
24133         
24134         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24135             /*
24136              * place to the top of element!
24137              *
24138              */
24139             
24140             this.picker().addClass('top');
24141             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24142             
24143             return;
24144         }
24145         
24146         this.picker().addClass('bottom');
24147         
24148         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24149     },
24150     
24151     onFocus : function()
24152     {
24153         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24154         this.show();
24155     },
24156     
24157     onBlur : function()
24158     {
24159         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24160         
24161         var d = this.inputEl().getValue();
24162         
24163         this.setValue(d);
24164                 
24165         this.hide();
24166     },
24167     
24168     show : function()
24169     {
24170         this.picker().show();
24171         this.picker().select('>.datepicker-months', true).first().show();
24172         this.update();
24173         this.place();
24174         
24175         this.fireEvent('show', this, this.date);
24176     },
24177     
24178     hide : function()
24179     {
24180         if(this.isInline) {
24181             return;
24182         }
24183         this.picker().hide();
24184         this.fireEvent('hide', this, this.date);
24185         
24186     },
24187     
24188     onMousedown: function(e)
24189     {
24190         e.stopPropagation();
24191         e.preventDefault();
24192     },
24193     
24194     keyup: function(e)
24195     {
24196         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24197         this.update();
24198     },
24199
24200     fireKey: function(e)
24201     {
24202         if (!this.picker().isVisible()){
24203             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24204                 this.show();
24205             }
24206             return;
24207         }
24208         
24209         var dir;
24210         
24211         switch(e.keyCode){
24212             case 27: // escape
24213                 this.hide();
24214                 e.preventDefault();
24215                 break;
24216             case 37: // left
24217             case 39: // right
24218                 dir = e.keyCode == 37 ? -1 : 1;
24219                 
24220                 this.vIndex = this.vIndex + dir;
24221                 
24222                 if(this.vIndex < 0){
24223                     this.vIndex = 0;
24224                 }
24225                 
24226                 if(this.vIndex > 11){
24227                     this.vIndex = 11;
24228                 }
24229                 
24230                 if(isNaN(this.vIndex)){
24231                     this.vIndex = 0;
24232                 }
24233                 
24234                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24235                 
24236                 break;
24237             case 38: // up
24238             case 40: // down
24239                 
24240                 dir = e.keyCode == 38 ? -1 : 1;
24241                 
24242                 this.vIndex = this.vIndex + dir * 4;
24243                 
24244                 if(this.vIndex < 0){
24245                     this.vIndex = 0;
24246                 }
24247                 
24248                 if(this.vIndex > 11){
24249                     this.vIndex = 11;
24250                 }
24251                 
24252                 if(isNaN(this.vIndex)){
24253                     this.vIndex = 0;
24254                 }
24255                 
24256                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24257                 break;
24258                 
24259             case 13: // enter
24260                 
24261                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24262                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24263                 }
24264                 
24265                 this.hide();
24266                 e.preventDefault();
24267                 break;
24268             case 9: // tab
24269                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24270                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24271                 }
24272                 this.hide();
24273                 break;
24274             case 16: // shift
24275             case 17: // ctrl
24276             case 18: // alt
24277                 break;
24278             default :
24279                 this.hide();
24280                 
24281         }
24282     },
24283     
24284     remove: function() 
24285     {
24286         this.picker().remove();
24287     }
24288    
24289 });
24290
24291 Roo.apply(Roo.bootstrap.MonthField,  {
24292     
24293     content : {
24294         tag: 'tbody',
24295         cn: [
24296         {
24297             tag: 'tr',
24298             cn: [
24299             {
24300                 tag: 'td',
24301                 colspan: '7'
24302             }
24303             ]
24304         }
24305         ]
24306     },
24307     
24308     dates:{
24309         en: {
24310             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24311             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24312         }
24313     }
24314 });
24315
24316 Roo.apply(Roo.bootstrap.MonthField,  {
24317   
24318     template : {
24319         tag: 'div',
24320         cls: 'datepicker dropdown-menu roo-dynamic',
24321         cn: [
24322             {
24323                 tag: 'div',
24324                 cls: 'datepicker-months',
24325                 cn: [
24326                 {
24327                     tag: 'table',
24328                     cls: 'table-condensed',
24329                     cn:[
24330                         Roo.bootstrap.DateField.content
24331                     ]
24332                 }
24333                 ]
24334             }
24335         ]
24336     }
24337 });
24338
24339  
24340
24341  
24342  /*
24343  * - LGPL
24344  *
24345  * CheckBox
24346  * 
24347  */
24348
24349 /**
24350  * @class Roo.bootstrap.CheckBox
24351  * @extends Roo.bootstrap.Input
24352  * Bootstrap CheckBox class
24353  * 
24354  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24355  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24356  * @cfg {String} boxLabel The text that appears beside the checkbox
24357  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24358  * @cfg {Boolean} checked initnal the element
24359  * @cfg {Boolean} inline inline the element (default false)
24360  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24361  * @cfg {String} tooltip label tooltip
24362  * 
24363  * @constructor
24364  * Create a new CheckBox
24365  * @param {Object} config The config object
24366  */
24367
24368 Roo.bootstrap.CheckBox = function(config){
24369     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24370    
24371     this.addEvents({
24372         /**
24373         * @event check
24374         * Fires when the element is checked or unchecked.
24375         * @param {Roo.bootstrap.CheckBox} this This input
24376         * @param {Boolean} checked The new checked value
24377         */
24378        check : true,
24379        /**
24380         * @event click
24381         * Fires when the element is click.
24382         * @param {Roo.bootstrap.CheckBox} this This input
24383         */
24384        click : true
24385     });
24386     
24387 };
24388
24389 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24390   
24391     inputType: 'checkbox',
24392     inputValue: 1,
24393     valueOff: 0,
24394     boxLabel: false,
24395     checked: false,
24396     weight : false,
24397     inline: false,
24398     tooltip : '',
24399     
24400     // checkbox success does not make any sense really.. 
24401     invalidClass : "",
24402     validClass : "",
24403     
24404     
24405     getAutoCreate : function()
24406     {
24407         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24408         
24409         var id = Roo.id();
24410         
24411         var cfg = {};
24412         
24413         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24414         
24415         if(this.inline){
24416             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24417         }
24418         
24419         var input =  {
24420             tag: 'input',
24421             id : id,
24422             type : this.inputType,
24423             value : this.inputValue,
24424             cls : 'roo-' + this.inputType, //'form-box',
24425             placeholder : this.placeholder || ''
24426             
24427         };
24428         
24429         if(this.inputType != 'radio'){
24430             var hidden =  {
24431                 tag: 'input',
24432                 type : 'hidden',
24433                 cls : 'roo-hidden-value',
24434                 value : this.checked ? this.inputValue : this.valueOff
24435             };
24436         }
24437         
24438             
24439         if (this.weight) { // Validity check?
24440             cfg.cls += " " + this.inputType + "-" + this.weight;
24441         }
24442         
24443         if (this.disabled) {
24444             input.disabled=true;
24445         }
24446         
24447         if(this.checked){
24448             input.checked = this.checked;
24449         }
24450         
24451         if (this.name) {
24452             
24453             input.name = this.name;
24454             
24455             if(this.inputType != 'radio'){
24456                 hidden.name = this.name;
24457                 input.name = '_hidden_' + this.name;
24458             }
24459         }
24460         
24461         if (this.size) {
24462             input.cls += ' input-' + this.size;
24463         }
24464         
24465         var settings=this;
24466         
24467         ['xs','sm','md','lg'].map(function(size){
24468             if (settings[size]) {
24469                 cfg.cls += ' col-' + size + '-' + settings[size];
24470             }
24471         });
24472         
24473         var inputblock = input;
24474          
24475         if (this.before || this.after) {
24476             
24477             inputblock = {
24478                 cls : 'input-group',
24479                 cn :  [] 
24480             };
24481             
24482             if (this.before) {
24483                 inputblock.cn.push({
24484                     tag :'span',
24485                     cls : 'input-group-addon',
24486                     html : this.before
24487                 });
24488             }
24489             
24490             inputblock.cn.push(input);
24491             
24492             if(this.inputType != 'radio'){
24493                 inputblock.cn.push(hidden);
24494             }
24495             
24496             if (this.after) {
24497                 inputblock.cn.push({
24498                     tag :'span',
24499                     cls : 'input-group-addon',
24500                     html : this.after
24501                 });
24502             }
24503             
24504         }
24505         var boxLabelCfg = false;
24506         
24507         if(this.boxLabel){
24508            
24509             boxLabelCfg = {
24510                 tag: 'label',
24511                 //'for': id, // box label is handled by onclick - so no for...
24512                 cls: 'box-label',
24513                 html: this.boxLabel
24514             };
24515             if(this.tooltip){
24516                 boxLabelCfg.tooltip = this.tooltip;
24517             }
24518              
24519         }
24520         
24521         
24522         if (align ==='left' && this.fieldLabel.length) {
24523 //                Roo.log("left and has label");
24524             cfg.cn = [
24525                 {
24526                     tag: 'label',
24527                     'for' :  id,
24528                     cls : 'control-label',
24529                     html : this.fieldLabel
24530                 },
24531                 {
24532                     cls : "", 
24533                     cn: [
24534                         inputblock
24535                     ]
24536                 }
24537             ];
24538             
24539             if (boxLabelCfg) {
24540                 cfg.cn[1].cn.push(boxLabelCfg);
24541             }
24542             
24543             if(this.labelWidth > 12){
24544                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24545             }
24546             
24547             if(this.labelWidth < 13 && this.labelmd == 0){
24548                 this.labelmd = this.labelWidth;
24549             }
24550             
24551             if(this.labellg > 0){
24552                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24553                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24554             }
24555             
24556             if(this.labelmd > 0){
24557                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24558                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24559             }
24560             
24561             if(this.labelsm > 0){
24562                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24563                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24564             }
24565             
24566             if(this.labelxs > 0){
24567                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24568                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24569             }
24570             
24571         } else if ( this.fieldLabel.length) {
24572 //                Roo.log(" label");
24573                 cfg.cn = [
24574                    
24575                     {
24576                         tag: this.boxLabel ? 'span' : 'label',
24577                         'for': id,
24578                         cls: 'control-label box-input-label',
24579                         //cls : 'input-group-addon',
24580                         html : this.fieldLabel
24581                     },
24582                     
24583                     inputblock
24584                     
24585                 ];
24586                 if (boxLabelCfg) {
24587                     cfg.cn.push(boxLabelCfg);
24588                 }
24589
24590         } else {
24591             
24592 //                Roo.log(" no label && no align");
24593                 cfg.cn = [  inputblock ] ;
24594                 if (boxLabelCfg) {
24595                     cfg.cn.push(boxLabelCfg);
24596                 }
24597
24598                 
24599         }
24600         
24601        
24602         
24603         if(this.inputType != 'radio'){
24604             cfg.cn.push(hidden);
24605         }
24606         
24607         return cfg;
24608         
24609     },
24610     
24611     /**
24612      * return the real input element.
24613      */
24614     inputEl: function ()
24615     {
24616         return this.el.select('input.roo-' + this.inputType,true).first();
24617     },
24618     hiddenEl: function ()
24619     {
24620         return this.el.select('input.roo-hidden-value',true).first();
24621     },
24622     
24623     labelEl: function()
24624     {
24625         return this.el.select('label.control-label',true).first();
24626     },
24627     /* depricated... */
24628     
24629     label: function()
24630     {
24631         return this.labelEl();
24632     },
24633     
24634     boxLabelEl: function()
24635     {
24636         return this.el.select('label.box-label',true).first();
24637     },
24638     
24639     initEvents : function()
24640     {
24641 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24642         
24643         this.inputEl().on('click', this.onClick,  this);
24644         
24645         if (this.boxLabel) { 
24646             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24647         }
24648         
24649         this.startValue = this.getValue();
24650         
24651         if(this.groupId){
24652             Roo.bootstrap.CheckBox.register(this);
24653         }
24654     },
24655     
24656     onClick : function(e)
24657     {   
24658         if(this.fireEvent('click', this, e) !== false){
24659             this.setChecked(!this.checked);
24660         }
24661         
24662     },
24663     
24664     setChecked : function(state,suppressEvent)
24665     {
24666         this.startValue = this.getValue();
24667
24668         if(this.inputType == 'radio'){
24669             
24670             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24671                 e.dom.checked = false;
24672             });
24673             
24674             this.inputEl().dom.checked = true;
24675             
24676             this.inputEl().dom.value = this.inputValue;
24677             
24678             if(suppressEvent !== true){
24679                 this.fireEvent('check', this, true);
24680             }
24681             
24682             this.validate();
24683             
24684             return;
24685         }
24686         
24687         this.checked = state;
24688         
24689         this.inputEl().dom.checked = state;
24690         
24691         
24692         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24693         
24694         if(suppressEvent !== true){
24695             this.fireEvent('check', this, state);
24696         }
24697         
24698         this.validate();
24699     },
24700     
24701     getValue : function()
24702     {
24703         if(this.inputType == 'radio'){
24704             return this.getGroupValue();
24705         }
24706         
24707         return this.hiddenEl().dom.value;
24708         
24709     },
24710     
24711     getGroupValue : function()
24712     {
24713         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24714             return '';
24715         }
24716         
24717         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24718     },
24719     
24720     setValue : function(v,suppressEvent)
24721     {
24722         if(this.inputType == 'radio'){
24723             this.setGroupValue(v, suppressEvent);
24724             return;
24725         }
24726         
24727         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24728         
24729         this.validate();
24730     },
24731     
24732     setGroupValue : function(v, suppressEvent)
24733     {
24734         this.startValue = this.getValue();
24735         
24736         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24737             e.dom.checked = false;
24738             
24739             if(e.dom.value == v){
24740                 e.dom.checked = true;
24741             }
24742         });
24743         
24744         if(suppressEvent !== true){
24745             this.fireEvent('check', this, true);
24746         }
24747
24748         this.validate();
24749         
24750         return;
24751     },
24752     
24753     validate : function()
24754     {
24755         if(this.getVisibilityEl().hasClass('hidden')){
24756             return true;
24757         }
24758         
24759         if(
24760                 this.disabled || 
24761                 (this.inputType == 'radio' && this.validateRadio()) ||
24762                 (this.inputType == 'checkbox' && this.validateCheckbox())
24763         ){
24764             this.markValid();
24765             return true;
24766         }
24767         
24768         this.markInvalid();
24769         return false;
24770     },
24771     
24772     validateRadio : function()
24773     {
24774         if(this.getVisibilityEl().hasClass('hidden')){
24775             return true;
24776         }
24777         
24778         if(this.allowBlank){
24779             return true;
24780         }
24781         
24782         var valid = false;
24783         
24784         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24785             if(!e.dom.checked){
24786                 return;
24787             }
24788             
24789             valid = true;
24790             
24791             return false;
24792         });
24793         
24794         return valid;
24795     },
24796     
24797     validateCheckbox : function()
24798     {
24799         if(!this.groupId){
24800             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24801             //return (this.getValue() == this.inputValue) ? true : false;
24802         }
24803         
24804         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24805         
24806         if(!group){
24807             return false;
24808         }
24809         
24810         var r = false;
24811         
24812         for(var i in group){
24813             if(group[i].el.isVisible(true)){
24814                 r = false;
24815                 break;
24816             }
24817             
24818             r = true;
24819         }
24820         
24821         for(var i in group){
24822             if(r){
24823                 break;
24824             }
24825             
24826             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24827         }
24828         
24829         return r;
24830     },
24831     
24832     /**
24833      * Mark this field as valid
24834      */
24835     markValid : function()
24836     {
24837         var _this = this;
24838         
24839         this.fireEvent('valid', this);
24840         
24841         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24842         
24843         if(this.groupId){
24844             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24845         }
24846         
24847         if(label){
24848             label.markValid();
24849         }
24850
24851         if(this.inputType == 'radio'){
24852             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24853                 var fg = e.findParent('.form-group', false, true);
24854                 if (Roo.bootstrap.version == 3) {
24855                     fg.removeClass([_this.invalidClass, _this.validClass]);
24856                     fg.addClass(_this.validClass);
24857                 } else {
24858                     fg.removeClass(['is-valid', 'is-invalid']);
24859                     fg.addClass('is-valid');
24860                 }
24861             });
24862             
24863             return;
24864         }
24865
24866         if(!this.groupId){
24867             var fg = this.el.findParent('.form-group', false, true);
24868             if (Roo.bootstrap.version == 3) {
24869                 fg.removeClass([this.invalidClass, this.validClass]);
24870                 fg.addClass(this.validClass);
24871             } else {
24872                 fg.removeClass(['is-valid', 'is-invalid']);
24873                 fg.addClass('is-valid');
24874             }
24875             return;
24876         }
24877         
24878         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24879         
24880         if(!group){
24881             return;
24882         }
24883         
24884         for(var i in group){
24885             var fg = group[i].el.findParent('.form-group', false, true);
24886             if (Roo.bootstrap.version == 3) {
24887                 fg.removeClass([this.invalidClass, this.validClass]);
24888                 fg.addClass(this.validClass);
24889             } else {
24890                 fg.removeClass(['is-valid', 'is-invalid']);
24891                 fg.addClass('is-valid');
24892             }
24893         }
24894     },
24895     
24896      /**
24897      * Mark this field as invalid
24898      * @param {String} msg The validation message
24899      */
24900     markInvalid : function(msg)
24901     {
24902         if(this.allowBlank){
24903             return;
24904         }
24905         
24906         var _this = this;
24907         
24908         this.fireEvent('invalid', this, msg);
24909         
24910         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24911         
24912         if(this.groupId){
24913             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24914         }
24915         
24916         if(label){
24917             label.markInvalid();
24918         }
24919             
24920         if(this.inputType == 'radio'){
24921             
24922             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24923                 var fg = e.findParent('.form-group', false, true);
24924                 if (Roo.bootstrap.version == 3) {
24925                     fg.removeClass([_this.invalidClass, _this.validClass]);
24926                     fg.addClass(_this.invalidClass);
24927                 } else {
24928                     fg.removeClass(['is-invalid', 'is-valid']);
24929                     fg.addClass('is-invalid');
24930                 }
24931             });
24932             
24933             return;
24934         }
24935         
24936         if(!this.groupId){
24937             var fg = this.el.findParent('.form-group', false, true);
24938             if (Roo.bootstrap.version == 3) {
24939                 fg.removeClass([_this.invalidClass, _this.validClass]);
24940                 fg.addClass(_this.invalidClass);
24941             } else {
24942                 fg.removeClass(['is-invalid', 'is-valid']);
24943                 fg.addClass('is-invalid');
24944             }
24945             return;
24946         }
24947         
24948         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24949         
24950         if(!group){
24951             return;
24952         }
24953         
24954         for(var i in group){
24955             var fg = group[i].el.findParent('.form-group', false, true);
24956             if (Roo.bootstrap.version == 3) {
24957                 fg.removeClass([_this.invalidClass, _this.validClass]);
24958                 fg.addClass(_this.invalidClass);
24959             } else {
24960                 fg.removeClass(['is-invalid', 'is-valid']);
24961                 fg.addClass('is-invalid');
24962             }
24963         }
24964         
24965     },
24966     
24967     clearInvalid : function()
24968     {
24969         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24970         
24971         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24972         
24973         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24974         
24975         if (label && label.iconEl) {
24976             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24977             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24978         }
24979     },
24980     
24981     disable : function()
24982     {
24983         if(this.inputType != 'radio'){
24984             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24985             return;
24986         }
24987         
24988         var _this = this;
24989         
24990         if(this.rendered){
24991             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24992                 _this.getActionEl().addClass(this.disabledClass);
24993                 e.dom.disabled = true;
24994             });
24995         }
24996         
24997         this.disabled = true;
24998         this.fireEvent("disable", this);
24999         return this;
25000     },
25001
25002     enable : function()
25003     {
25004         if(this.inputType != 'radio'){
25005             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25006             return;
25007         }
25008         
25009         var _this = this;
25010         
25011         if(this.rendered){
25012             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25013                 _this.getActionEl().removeClass(this.disabledClass);
25014                 e.dom.disabled = false;
25015             });
25016         }
25017         
25018         this.disabled = false;
25019         this.fireEvent("enable", this);
25020         return this;
25021     },
25022     
25023     setBoxLabel : function(v)
25024     {
25025         this.boxLabel = v;
25026         
25027         if(this.rendered){
25028             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25029         }
25030     }
25031
25032 });
25033
25034 Roo.apply(Roo.bootstrap.CheckBox, {
25035     
25036     groups: {},
25037     
25038      /**
25039     * register a CheckBox Group
25040     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25041     */
25042     register : function(checkbox)
25043     {
25044         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25045             this.groups[checkbox.groupId] = {};
25046         }
25047         
25048         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25049             return;
25050         }
25051         
25052         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25053         
25054     },
25055     /**
25056     * fetch a CheckBox Group based on the group ID
25057     * @param {string} the group ID
25058     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25059     */
25060     get: function(groupId) {
25061         if (typeof(this.groups[groupId]) == 'undefined') {
25062             return false;
25063         }
25064         
25065         return this.groups[groupId] ;
25066     }
25067     
25068     
25069 });
25070 /*
25071  * - LGPL
25072  *
25073  * RadioItem
25074  * 
25075  */
25076
25077 /**
25078  * @class Roo.bootstrap.Radio
25079  * @extends Roo.bootstrap.Component
25080  * Bootstrap Radio class
25081  * @cfg {String} boxLabel - the label associated
25082  * @cfg {String} value - the value of radio
25083  * 
25084  * @constructor
25085  * Create a new Radio
25086  * @param {Object} config The config object
25087  */
25088 Roo.bootstrap.Radio = function(config){
25089     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25090     
25091 };
25092
25093 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25094     
25095     boxLabel : '',
25096     
25097     value : '',
25098     
25099     getAutoCreate : function()
25100     {
25101         var cfg = {
25102             tag : 'div',
25103             cls : 'form-group radio',
25104             cn : [
25105                 {
25106                     tag : 'label',
25107                     cls : 'box-label',
25108                     html : this.boxLabel
25109                 }
25110             ]
25111         };
25112         
25113         return cfg;
25114     },
25115     
25116     initEvents : function() 
25117     {
25118         this.parent().register(this);
25119         
25120         this.el.on('click', this.onClick, this);
25121         
25122     },
25123     
25124     onClick : function(e)
25125     {
25126         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25127             this.setChecked(true);
25128         }
25129     },
25130     
25131     setChecked : function(state, suppressEvent)
25132     {
25133         this.parent().setValue(this.value, suppressEvent);
25134         
25135     },
25136     
25137     setBoxLabel : function(v)
25138     {
25139         this.boxLabel = v;
25140         
25141         if(this.rendered){
25142             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25143         }
25144     }
25145     
25146 });
25147  
25148
25149  /*
25150  * - LGPL
25151  *
25152  * Input
25153  * 
25154  */
25155
25156 /**
25157  * @class Roo.bootstrap.SecurePass
25158  * @extends Roo.bootstrap.Input
25159  * Bootstrap SecurePass class
25160  *
25161  * 
25162  * @constructor
25163  * Create a new SecurePass
25164  * @param {Object} config The config object
25165  */
25166  
25167 Roo.bootstrap.SecurePass = function (config) {
25168     // these go here, so the translation tool can replace them..
25169     this.errors = {
25170         PwdEmpty: "Please type a password, and then retype it to confirm.",
25171         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25172         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25173         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25174         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25175         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25176         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25177         TooWeak: "Your password is Too Weak."
25178     },
25179     this.meterLabel = "Password strength:";
25180     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25181     this.meterClass = [
25182         "roo-password-meter-tooweak", 
25183         "roo-password-meter-weak", 
25184         "roo-password-meter-medium", 
25185         "roo-password-meter-strong", 
25186         "roo-password-meter-grey"
25187     ];
25188     
25189     this.errors = {};
25190     
25191     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25192 }
25193
25194 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25195     /**
25196      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25197      * {
25198      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25199      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25200      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25201      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25202      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25203      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25204      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25205      * })
25206      */
25207     // private
25208     
25209     meterWidth: 300,
25210     errorMsg :'',    
25211     errors: false,
25212     imageRoot: '/',
25213     /**
25214      * @cfg {String/Object} Label for the strength meter (defaults to
25215      * 'Password strength:')
25216      */
25217     // private
25218     meterLabel: '',
25219     /**
25220      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25221      * ['Weak', 'Medium', 'Strong'])
25222      */
25223     // private    
25224     pwdStrengths: false,    
25225     // private
25226     strength: 0,
25227     // private
25228     _lastPwd: null,
25229     // private
25230     kCapitalLetter: 0,
25231     kSmallLetter: 1,
25232     kDigit: 2,
25233     kPunctuation: 3,
25234     
25235     insecure: false,
25236     // private
25237     initEvents: function ()
25238     {
25239         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25240
25241         if (this.el.is('input[type=password]') && Roo.isSafari) {
25242             this.el.on('keydown', this.SafariOnKeyDown, this);
25243         }
25244
25245         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25246     },
25247     // private
25248     onRender: function (ct, position)
25249     {
25250         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25251         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25252         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25253
25254         this.trigger.createChild({
25255                    cn: [
25256                     {
25257                     //id: 'PwdMeter',
25258                     tag: 'div',
25259                     cls: 'roo-password-meter-grey col-xs-12',
25260                     style: {
25261                         //width: 0,
25262                         //width: this.meterWidth + 'px'                                                
25263                         }
25264                     },
25265                     {                            
25266                          cls: 'roo-password-meter-text'                          
25267                     }
25268                 ]            
25269         });
25270
25271          
25272         if (this.hideTrigger) {
25273             this.trigger.setDisplayed(false);
25274         }
25275         this.setSize(this.width || '', this.height || '');
25276     },
25277     // private
25278     onDestroy: function ()
25279     {
25280         if (this.trigger) {
25281             this.trigger.removeAllListeners();
25282             this.trigger.remove();
25283         }
25284         if (this.wrap) {
25285             this.wrap.remove();
25286         }
25287         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25288     },
25289     // private
25290     checkStrength: function ()
25291     {
25292         var pwd = this.inputEl().getValue();
25293         if (pwd == this._lastPwd) {
25294             return;
25295         }
25296
25297         var strength;
25298         if (this.ClientSideStrongPassword(pwd)) {
25299             strength = 3;
25300         } else if (this.ClientSideMediumPassword(pwd)) {
25301             strength = 2;
25302         } else if (this.ClientSideWeakPassword(pwd)) {
25303             strength = 1;
25304         } else {
25305             strength = 0;
25306         }
25307         
25308         Roo.log('strength1: ' + strength);
25309         
25310         //var pm = this.trigger.child('div/div/div').dom;
25311         var pm = this.trigger.child('div/div');
25312         pm.removeClass(this.meterClass);
25313         pm.addClass(this.meterClass[strength]);
25314                 
25315         
25316         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25317                 
25318         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25319         
25320         this._lastPwd = pwd;
25321     },
25322     reset: function ()
25323     {
25324         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25325         
25326         this._lastPwd = '';
25327         
25328         var pm = this.trigger.child('div/div');
25329         pm.removeClass(this.meterClass);
25330         pm.addClass('roo-password-meter-grey');        
25331         
25332         
25333         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25334         
25335         pt.innerHTML = '';
25336         this.inputEl().dom.type='password';
25337     },
25338     // private
25339     validateValue: function (value)
25340     {
25341         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25342             return false;
25343         }
25344         if (value.length == 0) {
25345             if (this.allowBlank) {
25346                 this.clearInvalid();
25347                 return true;
25348             }
25349
25350             this.markInvalid(this.errors.PwdEmpty);
25351             this.errorMsg = this.errors.PwdEmpty;
25352             return false;
25353         }
25354         
25355         if(this.insecure){
25356             return true;
25357         }
25358         
25359         if (!value.match(/[\x21-\x7e]+/)) {
25360             this.markInvalid(this.errors.PwdBadChar);
25361             this.errorMsg = this.errors.PwdBadChar;
25362             return false;
25363         }
25364         if (value.length < 6) {
25365             this.markInvalid(this.errors.PwdShort);
25366             this.errorMsg = this.errors.PwdShort;
25367             return false;
25368         }
25369         if (value.length > 16) {
25370             this.markInvalid(this.errors.PwdLong);
25371             this.errorMsg = this.errors.PwdLong;
25372             return false;
25373         }
25374         var strength;
25375         if (this.ClientSideStrongPassword(value)) {
25376             strength = 3;
25377         } else if (this.ClientSideMediumPassword(value)) {
25378             strength = 2;
25379         } else if (this.ClientSideWeakPassword(value)) {
25380             strength = 1;
25381         } else {
25382             strength = 0;
25383         }
25384
25385         
25386         if (strength < 2) {
25387             //this.markInvalid(this.errors.TooWeak);
25388             this.errorMsg = this.errors.TooWeak;
25389             //return false;
25390         }
25391         
25392         
25393         console.log('strength2: ' + strength);
25394         
25395         //var pm = this.trigger.child('div/div/div').dom;
25396         
25397         var pm = this.trigger.child('div/div');
25398         pm.removeClass(this.meterClass);
25399         pm.addClass(this.meterClass[strength]);
25400                 
25401         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25402                 
25403         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25404         
25405         this.errorMsg = ''; 
25406         return true;
25407     },
25408     // private
25409     CharacterSetChecks: function (type)
25410     {
25411         this.type = type;
25412         this.fResult = false;
25413     },
25414     // private
25415     isctype: function (character, type)
25416     {
25417         switch (type) {  
25418             case this.kCapitalLetter:
25419                 if (character >= 'A' && character <= 'Z') {
25420                     return true;
25421                 }
25422                 break;
25423             
25424             case this.kSmallLetter:
25425                 if (character >= 'a' && character <= 'z') {
25426                     return true;
25427                 }
25428                 break;
25429             
25430             case this.kDigit:
25431                 if (character >= '0' && character <= '9') {
25432                     return true;
25433                 }
25434                 break;
25435             
25436             case this.kPunctuation:
25437                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25438                     return true;
25439                 }
25440                 break;
25441             
25442             default:
25443                 return false;
25444         }
25445
25446     },
25447     // private
25448     IsLongEnough: function (pwd, size)
25449     {
25450         return !(pwd == null || isNaN(size) || pwd.length < size);
25451     },
25452     // private
25453     SpansEnoughCharacterSets: function (word, nb)
25454     {
25455         if (!this.IsLongEnough(word, nb))
25456         {
25457             return false;
25458         }
25459
25460         var characterSetChecks = new Array(
25461             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25462             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25463         );
25464         
25465         for (var index = 0; index < word.length; ++index) {
25466             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25467                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25468                     characterSetChecks[nCharSet].fResult = true;
25469                     break;
25470                 }
25471             }
25472         }
25473
25474         var nCharSets = 0;
25475         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25476             if (characterSetChecks[nCharSet].fResult) {
25477                 ++nCharSets;
25478             }
25479         }
25480
25481         if (nCharSets < nb) {
25482             return false;
25483         }
25484         return true;
25485     },
25486     // private
25487     ClientSideStrongPassword: function (pwd)
25488     {
25489         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25490     },
25491     // private
25492     ClientSideMediumPassword: function (pwd)
25493     {
25494         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25495     },
25496     // private
25497     ClientSideWeakPassword: function (pwd)
25498     {
25499         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25500     }
25501           
25502 })//<script type="text/javascript">
25503
25504 /*
25505  * Based  Ext JS Library 1.1.1
25506  * Copyright(c) 2006-2007, Ext JS, LLC.
25507  * LGPL
25508  *
25509  */
25510  
25511 /**
25512  * @class Roo.HtmlEditorCore
25513  * @extends Roo.Component
25514  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25515  *
25516  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25517  */
25518
25519 Roo.HtmlEditorCore = function(config){
25520     
25521     
25522     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25523     
25524     
25525     this.addEvents({
25526         /**
25527          * @event initialize
25528          * Fires when the editor is fully initialized (including the iframe)
25529          * @param {Roo.HtmlEditorCore} this
25530          */
25531         initialize: true,
25532         /**
25533          * @event activate
25534          * Fires when the editor is first receives the focus. Any insertion must wait
25535          * until after this event.
25536          * @param {Roo.HtmlEditorCore} this
25537          */
25538         activate: true,
25539          /**
25540          * @event beforesync
25541          * Fires before the textarea is updated with content from the editor iframe. Return false
25542          * to cancel the sync.
25543          * @param {Roo.HtmlEditorCore} this
25544          * @param {String} html
25545          */
25546         beforesync: true,
25547          /**
25548          * @event beforepush
25549          * Fires before the iframe editor is updated with content from the textarea. Return false
25550          * to cancel the push.
25551          * @param {Roo.HtmlEditorCore} this
25552          * @param {String} html
25553          */
25554         beforepush: true,
25555          /**
25556          * @event sync
25557          * Fires when the textarea is updated with content from the editor iframe.
25558          * @param {Roo.HtmlEditorCore} this
25559          * @param {String} html
25560          */
25561         sync: true,
25562          /**
25563          * @event push
25564          * Fires when the iframe editor is updated with content from the textarea.
25565          * @param {Roo.HtmlEditorCore} this
25566          * @param {String} html
25567          */
25568         push: true,
25569         
25570         /**
25571          * @event editorevent
25572          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25573          * @param {Roo.HtmlEditorCore} this
25574          */
25575         editorevent: true
25576         
25577     });
25578     
25579     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25580     
25581     // defaults : white / black...
25582     this.applyBlacklists();
25583     
25584     
25585     
25586 };
25587
25588
25589 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25590
25591
25592      /**
25593      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25594      */
25595     
25596     owner : false,
25597     
25598      /**
25599      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25600      *                        Roo.resizable.
25601      */
25602     resizable : false,
25603      /**
25604      * @cfg {Number} height (in pixels)
25605      */   
25606     height: 300,
25607    /**
25608      * @cfg {Number} width (in pixels)
25609      */   
25610     width: 500,
25611     
25612     /**
25613      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25614      * 
25615      */
25616     stylesheets: false,
25617     
25618     /**
25619      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25620      */
25621     allowComments: false,
25622     // id of frame..
25623     frameId: false,
25624     
25625     // private properties
25626     validationEvent : false,
25627     deferHeight: true,
25628     initialized : false,
25629     activated : false,
25630     sourceEditMode : false,
25631     onFocus : Roo.emptyFn,
25632     iframePad:3,
25633     hideMode:'offsets',
25634     
25635     clearUp: true,
25636     
25637     // blacklist + whitelisted elements..
25638     black: false,
25639     white: false,
25640      
25641     bodyCls : '',
25642
25643     /**
25644      * Protected method that will not generally be called directly. It
25645      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25646      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25647      */
25648     getDocMarkup : function(){
25649         // body styles..
25650         var st = '';
25651         
25652         // inherit styels from page...?? 
25653         if (this.stylesheets === false) {
25654             
25655             Roo.get(document.head).select('style').each(function(node) {
25656                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25657             });
25658             
25659             Roo.get(document.head).select('link').each(function(node) { 
25660                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25661             });
25662             
25663         } else if (!this.stylesheets.length) {
25664                 // simple..
25665                 st = '<style type="text/css">' +
25666                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25667                    '</style>';
25668         } else {
25669             for (var i in this.stylesheets) { 
25670                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25671             }
25672             
25673         }
25674         
25675         st +=  '<style type="text/css">' +
25676             'IMG { cursor: pointer } ' +
25677         '</style>';
25678
25679         var cls = 'roo-htmleditor-body';
25680         
25681         if(this.bodyCls.length){
25682             cls += ' ' + this.bodyCls;
25683         }
25684         
25685         return '<html><head>' + st  +
25686             //<style type="text/css">' +
25687             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25688             //'</style>' +
25689             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25690     },
25691
25692     // private
25693     onRender : function(ct, position)
25694     {
25695         var _t = this;
25696         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25697         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25698         
25699         
25700         this.el.dom.style.border = '0 none';
25701         this.el.dom.setAttribute('tabIndex', -1);
25702         this.el.addClass('x-hidden hide');
25703         
25704         
25705         
25706         if(Roo.isIE){ // fix IE 1px bogus margin
25707             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25708         }
25709        
25710         
25711         this.frameId = Roo.id();
25712         
25713          
25714         
25715         var iframe = this.owner.wrap.createChild({
25716             tag: 'iframe',
25717             cls: 'form-control', // bootstrap..
25718             id: this.frameId,
25719             name: this.frameId,
25720             frameBorder : 'no',
25721             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25722         }, this.el
25723         );
25724         
25725         
25726         this.iframe = iframe.dom;
25727
25728          this.assignDocWin();
25729         
25730         this.doc.designMode = 'on';
25731        
25732         this.doc.open();
25733         this.doc.write(this.getDocMarkup());
25734         this.doc.close();
25735
25736         
25737         var task = { // must defer to wait for browser to be ready
25738             run : function(){
25739                 //console.log("run task?" + this.doc.readyState);
25740                 this.assignDocWin();
25741                 if(this.doc.body || this.doc.readyState == 'complete'){
25742                     try {
25743                         this.doc.designMode="on";
25744                     } catch (e) {
25745                         return;
25746                     }
25747                     Roo.TaskMgr.stop(task);
25748                     this.initEditor.defer(10, this);
25749                 }
25750             },
25751             interval : 10,
25752             duration: 10000,
25753             scope: this
25754         };
25755         Roo.TaskMgr.start(task);
25756
25757     },
25758
25759     // private
25760     onResize : function(w, h)
25761     {
25762          Roo.log('resize: ' +w + ',' + h );
25763         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25764         if(!this.iframe){
25765             return;
25766         }
25767         if(typeof w == 'number'){
25768             
25769             this.iframe.style.width = w + 'px';
25770         }
25771         if(typeof h == 'number'){
25772             
25773             this.iframe.style.height = h + 'px';
25774             if(this.doc){
25775                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25776             }
25777         }
25778         
25779     },
25780
25781     /**
25782      * Toggles the editor between standard and source edit mode.
25783      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25784      */
25785     toggleSourceEdit : function(sourceEditMode){
25786         
25787         this.sourceEditMode = sourceEditMode === true;
25788         
25789         if(this.sourceEditMode){
25790  
25791             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25792             
25793         }else{
25794             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25795             //this.iframe.className = '';
25796             this.deferFocus();
25797         }
25798         //this.setSize(this.owner.wrap.getSize());
25799         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25800     },
25801
25802     
25803   
25804
25805     /**
25806      * Protected method that will not generally be called directly. If you need/want
25807      * custom HTML cleanup, this is the method you should override.
25808      * @param {String} html The HTML to be cleaned
25809      * return {String} The cleaned HTML
25810      */
25811     cleanHtml : function(html){
25812         html = String(html);
25813         if(html.length > 5){
25814             if(Roo.isSafari){ // strip safari nonsense
25815                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25816             }
25817         }
25818         if(html == '&nbsp;'){
25819             html = '';
25820         }
25821         return html;
25822     },
25823
25824     /**
25825      * HTML Editor -> Textarea
25826      * Protected method that will not generally be called directly. Syncs the contents
25827      * of the editor iframe with the textarea.
25828      */
25829     syncValue : function(){
25830         if(this.initialized){
25831             var bd = (this.doc.body || this.doc.documentElement);
25832             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25833             var html = bd.innerHTML;
25834             if(Roo.isSafari){
25835                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25836                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25837                 if(m && m[1]){
25838                     html = '<div style="'+m[0]+'">' + html + '</div>';
25839                 }
25840             }
25841             html = this.cleanHtml(html);
25842             // fix up the special chars.. normaly like back quotes in word...
25843             // however we do not want to do this with chinese..
25844             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25845                 
25846                 var cc = match.charCodeAt();
25847
25848                 // Get the character value, handling surrogate pairs
25849                 if (match.length == 2) {
25850                     // It's a surrogate pair, calculate the Unicode code point
25851                     var high = match.charCodeAt(0) - 0xD800;
25852                     var low  = match.charCodeAt(1) - 0xDC00;
25853                     cc = (high * 0x400) + low + 0x10000;
25854                 }  else if (
25855                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25856                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25857                     (cc >= 0xf900 && cc < 0xfb00 )
25858                 ) {
25859                         return match;
25860                 }  
25861          
25862                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25863                 return "&#" + cc + ";";
25864                 
25865                 
25866             });
25867             
25868             
25869              
25870             if(this.owner.fireEvent('beforesync', this, html) !== false){
25871                 this.el.dom.value = html;
25872                 this.owner.fireEvent('sync', this, html);
25873             }
25874         }
25875     },
25876
25877     /**
25878      * Protected method that will not generally be called directly. Pushes the value of the textarea
25879      * into the iframe editor.
25880      */
25881     pushValue : function(){
25882         if(this.initialized){
25883             var v = this.el.dom.value.trim();
25884             
25885 //            if(v.length < 1){
25886 //                v = '&#160;';
25887 //            }
25888             
25889             if(this.owner.fireEvent('beforepush', this, v) !== false){
25890                 var d = (this.doc.body || this.doc.documentElement);
25891                 d.innerHTML = v;
25892                 this.cleanUpPaste();
25893                 this.el.dom.value = d.innerHTML;
25894                 this.owner.fireEvent('push', this, v);
25895             }
25896         }
25897     },
25898
25899     // private
25900     deferFocus : function(){
25901         this.focus.defer(10, this);
25902     },
25903
25904     // doc'ed in Field
25905     focus : function(){
25906         if(this.win && !this.sourceEditMode){
25907             this.win.focus();
25908         }else{
25909             this.el.focus();
25910         }
25911     },
25912     
25913     assignDocWin: function()
25914     {
25915         var iframe = this.iframe;
25916         
25917          if(Roo.isIE){
25918             this.doc = iframe.contentWindow.document;
25919             this.win = iframe.contentWindow;
25920         } else {
25921 //            if (!Roo.get(this.frameId)) {
25922 //                return;
25923 //            }
25924 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25925 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25926             
25927             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25928                 return;
25929             }
25930             
25931             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25932             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25933         }
25934     },
25935     
25936     // private
25937     initEditor : function(){
25938         //console.log("INIT EDITOR");
25939         this.assignDocWin();
25940         
25941         
25942         
25943         this.doc.designMode="on";
25944         this.doc.open();
25945         this.doc.write(this.getDocMarkup());
25946         this.doc.close();
25947         
25948         var dbody = (this.doc.body || this.doc.documentElement);
25949         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25950         // this copies styles from the containing element into thsi one..
25951         // not sure why we need all of this..
25952         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25953         
25954         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25955         //ss['background-attachment'] = 'fixed'; // w3c
25956         dbody.bgProperties = 'fixed'; // ie
25957         //Roo.DomHelper.applyStyles(dbody, ss);
25958         Roo.EventManager.on(this.doc, {
25959             //'mousedown': this.onEditorEvent,
25960             'mouseup': this.onEditorEvent,
25961             'dblclick': this.onEditorEvent,
25962             'click': this.onEditorEvent,
25963             'keyup': this.onEditorEvent,
25964             buffer:100,
25965             scope: this
25966         });
25967         if(Roo.isGecko){
25968             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25969         }
25970         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25971             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25972         }
25973         this.initialized = true;
25974
25975         this.owner.fireEvent('initialize', this);
25976         this.pushValue();
25977     },
25978
25979     // private
25980     onDestroy : function(){
25981         
25982         
25983         
25984         if(this.rendered){
25985             
25986             //for (var i =0; i < this.toolbars.length;i++) {
25987             //    // fixme - ask toolbars for heights?
25988             //    this.toolbars[i].onDestroy();
25989            // }
25990             
25991             //this.wrap.dom.innerHTML = '';
25992             //this.wrap.remove();
25993         }
25994     },
25995
25996     // private
25997     onFirstFocus : function(){
25998         
25999         this.assignDocWin();
26000         
26001         
26002         this.activated = true;
26003          
26004     
26005         if(Roo.isGecko){ // prevent silly gecko errors
26006             this.win.focus();
26007             var s = this.win.getSelection();
26008             if(!s.focusNode || s.focusNode.nodeType != 3){
26009                 var r = s.getRangeAt(0);
26010                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26011                 r.collapse(true);
26012                 this.deferFocus();
26013             }
26014             try{
26015                 this.execCmd('useCSS', true);
26016                 this.execCmd('styleWithCSS', false);
26017             }catch(e){}
26018         }
26019         this.owner.fireEvent('activate', this);
26020     },
26021
26022     // private
26023     adjustFont: function(btn){
26024         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26025         //if(Roo.isSafari){ // safari
26026         //    adjust *= 2;
26027        // }
26028         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26029         if(Roo.isSafari){ // safari
26030             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26031             v =  (v < 10) ? 10 : v;
26032             v =  (v > 48) ? 48 : v;
26033             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26034             
26035         }
26036         
26037         
26038         v = Math.max(1, v+adjust);
26039         
26040         this.execCmd('FontSize', v  );
26041     },
26042
26043     onEditorEvent : function(e)
26044     {
26045         this.owner.fireEvent('editorevent', this, e);
26046       //  this.updateToolbar();
26047         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26048     },
26049
26050     insertTag : function(tg)
26051     {
26052         // could be a bit smarter... -> wrap the current selected tRoo..
26053         if (tg.toLowerCase() == 'span' ||
26054             tg.toLowerCase() == 'code' ||
26055             tg.toLowerCase() == 'sup' ||
26056             tg.toLowerCase() == 'sub' 
26057             ) {
26058             
26059             range = this.createRange(this.getSelection());
26060             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26061             wrappingNode.appendChild(range.extractContents());
26062             range.insertNode(wrappingNode);
26063
26064             return;
26065             
26066             
26067             
26068         }
26069         this.execCmd("formatblock",   tg);
26070         
26071     },
26072     
26073     insertText : function(txt)
26074     {
26075         
26076         
26077         var range = this.createRange();
26078         range.deleteContents();
26079                //alert(Sender.getAttribute('label'));
26080                
26081         range.insertNode(this.doc.createTextNode(txt));
26082     } ,
26083     
26084      
26085
26086     /**
26087      * Executes a Midas editor command on the editor document and performs necessary focus and
26088      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26089      * @param {String} cmd The Midas command
26090      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26091      */
26092     relayCmd : function(cmd, value){
26093         this.win.focus();
26094         this.execCmd(cmd, value);
26095         this.owner.fireEvent('editorevent', this);
26096         //this.updateToolbar();
26097         this.owner.deferFocus();
26098     },
26099
26100     /**
26101      * Executes a Midas editor command directly on the editor document.
26102      * For visual commands, you should use {@link #relayCmd} instead.
26103      * <b>This should only be called after the editor is initialized.</b>
26104      * @param {String} cmd The Midas command
26105      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26106      */
26107     execCmd : function(cmd, value){
26108         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26109         this.syncValue();
26110     },
26111  
26112  
26113    
26114     /**
26115      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26116      * to insert tRoo.
26117      * @param {String} text | dom node.. 
26118      */
26119     insertAtCursor : function(text)
26120     {
26121         
26122         if(!this.activated){
26123             return;
26124         }
26125         /*
26126         if(Roo.isIE){
26127             this.win.focus();
26128             var r = this.doc.selection.createRange();
26129             if(r){
26130                 r.collapse(true);
26131                 r.pasteHTML(text);
26132                 this.syncValue();
26133                 this.deferFocus();
26134             
26135             }
26136             return;
26137         }
26138         */
26139         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26140             this.win.focus();
26141             
26142             
26143             // from jquery ui (MIT licenced)
26144             var range, node;
26145             var win = this.win;
26146             
26147             if (win.getSelection && win.getSelection().getRangeAt) {
26148                 range = win.getSelection().getRangeAt(0);
26149                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26150                 range.insertNode(node);
26151             } else if (win.document.selection && win.document.selection.createRange) {
26152                 // no firefox support
26153                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26154                 win.document.selection.createRange().pasteHTML(txt);
26155             } else {
26156                 // no firefox support
26157                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26158                 this.execCmd('InsertHTML', txt);
26159             } 
26160             
26161             this.syncValue();
26162             
26163             this.deferFocus();
26164         }
26165     },
26166  // private
26167     mozKeyPress : function(e){
26168         if(e.ctrlKey){
26169             var c = e.getCharCode(), cmd;
26170           
26171             if(c > 0){
26172                 c = String.fromCharCode(c).toLowerCase();
26173                 switch(c){
26174                     case 'b':
26175                         cmd = 'bold';
26176                         break;
26177                     case 'i':
26178                         cmd = 'italic';
26179                         break;
26180                     
26181                     case 'u':
26182                         cmd = 'underline';
26183                         break;
26184                     
26185                     case 'v':
26186                         this.cleanUpPaste.defer(100, this);
26187                         return;
26188                         
26189                 }
26190                 if(cmd){
26191                     this.win.focus();
26192                     this.execCmd(cmd);
26193                     this.deferFocus();
26194                     e.preventDefault();
26195                 }
26196                 
26197             }
26198         }
26199     },
26200
26201     // private
26202     fixKeys : function(){ // load time branching for fastest keydown performance
26203         if(Roo.isIE){
26204             return function(e){
26205                 var k = e.getKey(), r;
26206                 if(k == e.TAB){
26207                     e.stopEvent();
26208                     r = this.doc.selection.createRange();
26209                     if(r){
26210                         r.collapse(true);
26211                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26212                         this.deferFocus();
26213                     }
26214                     return;
26215                 }
26216                 
26217                 if(k == e.ENTER){
26218                     r = this.doc.selection.createRange();
26219                     if(r){
26220                         var target = r.parentElement();
26221                         if(!target || target.tagName.toLowerCase() != 'li'){
26222                             e.stopEvent();
26223                             r.pasteHTML('<br />');
26224                             r.collapse(false);
26225                             r.select();
26226                         }
26227                     }
26228                 }
26229                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26230                     this.cleanUpPaste.defer(100, this);
26231                     return;
26232                 }
26233                 
26234                 
26235             };
26236         }else if(Roo.isOpera){
26237             return function(e){
26238                 var k = e.getKey();
26239                 if(k == e.TAB){
26240                     e.stopEvent();
26241                     this.win.focus();
26242                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26243                     this.deferFocus();
26244                 }
26245                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26246                     this.cleanUpPaste.defer(100, this);
26247                     return;
26248                 }
26249                 
26250             };
26251         }else if(Roo.isSafari){
26252             return function(e){
26253                 var k = e.getKey();
26254                 
26255                 if(k == e.TAB){
26256                     e.stopEvent();
26257                     this.execCmd('InsertText','\t');
26258                     this.deferFocus();
26259                     return;
26260                 }
26261                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26262                     this.cleanUpPaste.defer(100, this);
26263                     return;
26264                 }
26265                 
26266              };
26267         }
26268     }(),
26269     
26270     getAllAncestors: function()
26271     {
26272         var p = this.getSelectedNode();
26273         var a = [];
26274         if (!p) {
26275             a.push(p); // push blank onto stack..
26276             p = this.getParentElement();
26277         }
26278         
26279         
26280         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26281             a.push(p);
26282             p = p.parentNode;
26283         }
26284         a.push(this.doc.body);
26285         return a;
26286     },
26287     lastSel : false,
26288     lastSelNode : false,
26289     
26290     
26291     getSelection : function() 
26292     {
26293         this.assignDocWin();
26294         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26295     },
26296     
26297     getSelectedNode: function() 
26298     {
26299         // this may only work on Gecko!!!
26300         
26301         // should we cache this!!!!
26302         
26303         
26304         
26305          
26306         var range = this.createRange(this.getSelection()).cloneRange();
26307         
26308         if (Roo.isIE) {
26309             var parent = range.parentElement();
26310             while (true) {
26311                 var testRange = range.duplicate();
26312                 testRange.moveToElementText(parent);
26313                 if (testRange.inRange(range)) {
26314                     break;
26315                 }
26316                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26317                     break;
26318                 }
26319                 parent = parent.parentElement;
26320             }
26321             return parent;
26322         }
26323         
26324         // is ancestor a text element.
26325         var ac =  range.commonAncestorContainer;
26326         if (ac.nodeType == 3) {
26327             ac = ac.parentNode;
26328         }
26329         
26330         var ar = ac.childNodes;
26331          
26332         var nodes = [];
26333         var other_nodes = [];
26334         var has_other_nodes = false;
26335         for (var i=0;i<ar.length;i++) {
26336             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26337                 continue;
26338             }
26339             // fullly contained node.
26340             
26341             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26342                 nodes.push(ar[i]);
26343                 continue;
26344             }
26345             
26346             // probably selected..
26347             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26348                 other_nodes.push(ar[i]);
26349                 continue;
26350             }
26351             // outer..
26352             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26353                 continue;
26354             }
26355             
26356             
26357             has_other_nodes = true;
26358         }
26359         if (!nodes.length && other_nodes.length) {
26360             nodes= other_nodes;
26361         }
26362         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26363             return false;
26364         }
26365         
26366         return nodes[0];
26367     },
26368     createRange: function(sel)
26369     {
26370         // this has strange effects when using with 
26371         // top toolbar - not sure if it's a great idea.
26372         //this.editor.contentWindow.focus();
26373         if (typeof sel != "undefined") {
26374             try {
26375                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26376             } catch(e) {
26377                 return this.doc.createRange();
26378             }
26379         } else {
26380             return this.doc.createRange();
26381         }
26382     },
26383     getParentElement: function()
26384     {
26385         
26386         this.assignDocWin();
26387         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26388         
26389         var range = this.createRange(sel);
26390          
26391         try {
26392             var p = range.commonAncestorContainer;
26393             while (p.nodeType == 3) { // text node
26394                 p = p.parentNode;
26395             }
26396             return p;
26397         } catch (e) {
26398             return null;
26399         }
26400     
26401     },
26402     /***
26403      *
26404      * Range intersection.. the hard stuff...
26405      *  '-1' = before
26406      *  '0' = hits..
26407      *  '1' = after.
26408      *         [ -- selected range --- ]
26409      *   [fail]                        [fail]
26410      *
26411      *    basically..
26412      *      if end is before start or  hits it. fail.
26413      *      if start is after end or hits it fail.
26414      *
26415      *   if either hits (but other is outside. - then it's not 
26416      *   
26417      *    
26418      **/
26419     
26420     
26421     // @see http://www.thismuchiknow.co.uk/?p=64.
26422     rangeIntersectsNode : function(range, node)
26423     {
26424         var nodeRange = node.ownerDocument.createRange();
26425         try {
26426             nodeRange.selectNode(node);
26427         } catch (e) {
26428             nodeRange.selectNodeContents(node);
26429         }
26430     
26431         var rangeStartRange = range.cloneRange();
26432         rangeStartRange.collapse(true);
26433     
26434         var rangeEndRange = range.cloneRange();
26435         rangeEndRange.collapse(false);
26436     
26437         var nodeStartRange = nodeRange.cloneRange();
26438         nodeStartRange.collapse(true);
26439     
26440         var nodeEndRange = nodeRange.cloneRange();
26441         nodeEndRange.collapse(false);
26442     
26443         return rangeStartRange.compareBoundaryPoints(
26444                  Range.START_TO_START, nodeEndRange) == -1 &&
26445                rangeEndRange.compareBoundaryPoints(
26446                  Range.START_TO_START, nodeStartRange) == 1;
26447         
26448          
26449     },
26450     rangeCompareNode : function(range, node)
26451     {
26452         var nodeRange = node.ownerDocument.createRange();
26453         try {
26454             nodeRange.selectNode(node);
26455         } catch (e) {
26456             nodeRange.selectNodeContents(node);
26457         }
26458         
26459         
26460         range.collapse(true);
26461     
26462         nodeRange.collapse(true);
26463      
26464         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26465         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26466          
26467         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26468         
26469         var nodeIsBefore   =  ss == 1;
26470         var nodeIsAfter    = ee == -1;
26471         
26472         if (nodeIsBefore && nodeIsAfter) {
26473             return 0; // outer
26474         }
26475         if (!nodeIsBefore && nodeIsAfter) {
26476             return 1; //right trailed.
26477         }
26478         
26479         if (nodeIsBefore && !nodeIsAfter) {
26480             return 2;  // left trailed.
26481         }
26482         // fully contined.
26483         return 3;
26484     },
26485
26486     // private? - in a new class?
26487     cleanUpPaste :  function()
26488     {
26489         // cleans up the whole document..
26490         Roo.log('cleanuppaste');
26491         
26492         this.cleanUpChildren(this.doc.body);
26493         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26494         if (clean != this.doc.body.innerHTML) {
26495             this.doc.body.innerHTML = clean;
26496         }
26497         
26498     },
26499     
26500     cleanWordChars : function(input) {// change the chars to hex code
26501         var he = Roo.HtmlEditorCore;
26502         
26503         var output = input;
26504         Roo.each(he.swapCodes, function(sw) { 
26505             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26506             
26507             output = output.replace(swapper, sw[1]);
26508         });
26509         
26510         return output;
26511     },
26512     
26513     
26514     cleanUpChildren : function (n)
26515     {
26516         if (!n.childNodes.length) {
26517             return;
26518         }
26519         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26520            this.cleanUpChild(n.childNodes[i]);
26521         }
26522     },
26523     
26524     
26525         
26526     
26527     cleanUpChild : function (node)
26528     {
26529         var ed = this;
26530         //console.log(node);
26531         if (node.nodeName == "#text") {
26532             // clean up silly Windows -- stuff?
26533             return; 
26534         }
26535         if (node.nodeName == "#comment") {
26536             if (!this.allowComments) {
26537                 node.parentNode.removeChild(node);
26538             }
26539             // clean up silly Windows -- stuff?
26540             return; 
26541         }
26542         var lcname = node.tagName.toLowerCase();
26543         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26544         // whitelist of tags..
26545         
26546         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26547             // remove node.
26548             node.parentNode.removeChild(node);
26549             return;
26550             
26551         }
26552         
26553         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26554         
26555         // spans with no attributes - just remove them..
26556         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26557             remove_keep_children = true;
26558         }
26559         
26560         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26561         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26562         
26563         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26564         //    remove_keep_children = true;
26565         //}
26566         
26567         if (remove_keep_children) {
26568             this.cleanUpChildren(node);
26569             // inserts everything just before this node...
26570             while (node.childNodes.length) {
26571                 var cn = node.childNodes[0];
26572                 node.removeChild(cn);
26573                 node.parentNode.insertBefore(cn, node);
26574             }
26575             node.parentNode.removeChild(node);
26576             return;
26577         }
26578         
26579         if (!node.attributes || !node.attributes.length) {
26580             
26581           
26582             
26583             
26584             this.cleanUpChildren(node);
26585             return;
26586         }
26587         
26588         function cleanAttr(n,v)
26589         {
26590             
26591             if (v.match(/^\./) || v.match(/^\//)) {
26592                 return;
26593             }
26594             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26595                 return;
26596             }
26597             if (v.match(/^#/)) {
26598                 return;
26599             }
26600             if (v.match(/^\{/)) { // allow template editing.
26601                 return;
26602             }
26603 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26604             node.removeAttribute(n);
26605             
26606         }
26607         
26608         var cwhite = this.cwhite;
26609         var cblack = this.cblack;
26610             
26611         function cleanStyle(n,v)
26612         {
26613             if (v.match(/expression/)) { //XSS?? should we even bother..
26614                 node.removeAttribute(n);
26615                 return;
26616             }
26617             
26618             var parts = v.split(/;/);
26619             var clean = [];
26620             
26621             Roo.each(parts, function(p) {
26622                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26623                 if (!p.length) {
26624                     return true;
26625                 }
26626                 var l = p.split(':').shift().replace(/\s+/g,'');
26627                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26628                 
26629                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26630 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26631                     //node.removeAttribute(n);
26632                     return true;
26633                 }
26634                 //Roo.log()
26635                 // only allow 'c whitelisted system attributes'
26636                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26637 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26638                     //node.removeAttribute(n);
26639                     return true;
26640                 }
26641                 
26642                 
26643                  
26644                 
26645                 clean.push(p);
26646                 return true;
26647             });
26648             if (clean.length) { 
26649                 node.setAttribute(n, clean.join(';'));
26650             } else {
26651                 node.removeAttribute(n);
26652             }
26653             
26654         }
26655         
26656         
26657         for (var i = node.attributes.length-1; i > -1 ; i--) {
26658             var a = node.attributes[i];
26659             //console.log(a);
26660             
26661             if (a.name.toLowerCase().substr(0,2)=='on')  {
26662                 node.removeAttribute(a.name);
26663                 continue;
26664             }
26665             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26666                 node.removeAttribute(a.name);
26667                 continue;
26668             }
26669             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26670                 cleanAttr(a.name,a.value); // fixme..
26671                 continue;
26672             }
26673             if (a.name == 'style') {
26674                 cleanStyle(a.name,a.value);
26675                 continue;
26676             }
26677             /// clean up MS crap..
26678             // tecnically this should be a list of valid class'es..
26679             
26680             
26681             if (a.name == 'class') {
26682                 if (a.value.match(/^Mso/)) {
26683                     node.removeAttribute('class');
26684                 }
26685                 
26686                 if (a.value.match(/^body$/)) {
26687                     node.removeAttribute('class');
26688                 }
26689                 continue;
26690             }
26691             
26692             // style cleanup!?
26693             // class cleanup?
26694             
26695         }
26696         
26697         
26698         this.cleanUpChildren(node);
26699         
26700         
26701     },
26702     
26703     /**
26704      * Clean up MS wordisms...
26705      */
26706     cleanWord : function(node)
26707     {
26708         if (!node) {
26709             this.cleanWord(this.doc.body);
26710             return;
26711         }
26712         
26713         if(
26714                 node.nodeName == 'SPAN' &&
26715                 !node.hasAttributes() &&
26716                 node.childNodes.length == 1 &&
26717                 node.firstChild.nodeName == "#text"  
26718         ) {
26719             var textNode = node.firstChild;
26720             node.removeChild(textNode);
26721             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26722                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26723             }
26724             node.parentNode.insertBefore(textNode, node);
26725             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26726                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26727             }
26728             node.parentNode.removeChild(node);
26729         }
26730         
26731         if (node.nodeName == "#text") {
26732             // clean up silly Windows -- stuff?
26733             return; 
26734         }
26735         if (node.nodeName == "#comment") {
26736             node.parentNode.removeChild(node);
26737             // clean up silly Windows -- stuff?
26738             return; 
26739         }
26740         
26741         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26742             node.parentNode.removeChild(node);
26743             return;
26744         }
26745         //Roo.log(node.tagName);
26746         // remove - but keep children..
26747         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26748             //Roo.log('-- removed');
26749             while (node.childNodes.length) {
26750                 var cn = node.childNodes[0];
26751                 node.removeChild(cn);
26752                 node.parentNode.insertBefore(cn, node);
26753                 // move node to parent - and clean it..
26754                 this.cleanWord(cn);
26755             }
26756             node.parentNode.removeChild(node);
26757             /// no need to iterate chidlren = it's got none..
26758             //this.iterateChildren(node, this.cleanWord);
26759             return;
26760         }
26761         // clean styles
26762         if (node.className.length) {
26763             
26764             var cn = node.className.split(/\W+/);
26765             var cna = [];
26766             Roo.each(cn, function(cls) {
26767                 if (cls.match(/Mso[a-zA-Z]+/)) {
26768                     return;
26769                 }
26770                 cna.push(cls);
26771             });
26772             node.className = cna.length ? cna.join(' ') : '';
26773             if (!cna.length) {
26774                 node.removeAttribute("class");
26775             }
26776         }
26777         
26778         if (node.hasAttribute("lang")) {
26779             node.removeAttribute("lang");
26780         }
26781         
26782         if (node.hasAttribute("style")) {
26783             
26784             var styles = node.getAttribute("style").split(";");
26785             var nstyle = [];
26786             Roo.each(styles, function(s) {
26787                 if (!s.match(/:/)) {
26788                     return;
26789                 }
26790                 var kv = s.split(":");
26791                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26792                     return;
26793                 }
26794                 // what ever is left... we allow.
26795                 nstyle.push(s);
26796             });
26797             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26798             if (!nstyle.length) {
26799                 node.removeAttribute('style');
26800             }
26801         }
26802         this.iterateChildren(node, this.cleanWord);
26803         
26804         
26805         
26806     },
26807     /**
26808      * iterateChildren of a Node, calling fn each time, using this as the scole..
26809      * @param {DomNode} node node to iterate children of.
26810      * @param {Function} fn method of this class to call on each item.
26811      */
26812     iterateChildren : function(node, fn)
26813     {
26814         if (!node.childNodes.length) {
26815                 return;
26816         }
26817         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26818            fn.call(this, node.childNodes[i])
26819         }
26820     },
26821     
26822     
26823     /**
26824      * cleanTableWidths.
26825      *
26826      * Quite often pasting from word etc.. results in tables with column and widths.
26827      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26828      *
26829      */
26830     cleanTableWidths : function(node)
26831     {
26832          
26833          
26834         if (!node) {
26835             this.cleanTableWidths(this.doc.body);
26836             return;
26837         }
26838         
26839         // ignore list...
26840         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26841             return; 
26842         }
26843         Roo.log(node.tagName);
26844         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26845             this.iterateChildren(node, this.cleanTableWidths);
26846             return;
26847         }
26848         if (node.hasAttribute('width')) {
26849             node.removeAttribute('width');
26850         }
26851         
26852          
26853         if (node.hasAttribute("style")) {
26854             // pretty basic...
26855             
26856             var styles = node.getAttribute("style").split(";");
26857             var nstyle = [];
26858             Roo.each(styles, function(s) {
26859                 if (!s.match(/:/)) {
26860                     return;
26861                 }
26862                 var kv = s.split(":");
26863                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26864                     return;
26865                 }
26866                 // what ever is left... we allow.
26867                 nstyle.push(s);
26868             });
26869             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26870             if (!nstyle.length) {
26871                 node.removeAttribute('style');
26872             }
26873         }
26874         
26875         this.iterateChildren(node, this.cleanTableWidths);
26876         
26877         
26878     },
26879     
26880     
26881     
26882     
26883     domToHTML : function(currentElement, depth, nopadtext) {
26884         
26885         depth = depth || 0;
26886         nopadtext = nopadtext || false;
26887     
26888         if (!currentElement) {
26889             return this.domToHTML(this.doc.body);
26890         }
26891         
26892         //Roo.log(currentElement);
26893         var j;
26894         var allText = false;
26895         var nodeName = currentElement.nodeName;
26896         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26897         
26898         if  (nodeName == '#text') {
26899             
26900             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26901         }
26902         
26903         
26904         var ret = '';
26905         if (nodeName != 'BODY') {
26906              
26907             var i = 0;
26908             // Prints the node tagName, such as <A>, <IMG>, etc
26909             if (tagName) {
26910                 var attr = [];
26911                 for(i = 0; i < currentElement.attributes.length;i++) {
26912                     // quoting?
26913                     var aname = currentElement.attributes.item(i).name;
26914                     if (!currentElement.attributes.item(i).value.length) {
26915                         continue;
26916                     }
26917                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26918                 }
26919                 
26920                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26921             } 
26922             else {
26923                 
26924                 // eack
26925             }
26926         } else {
26927             tagName = false;
26928         }
26929         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26930             return ret;
26931         }
26932         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26933             nopadtext = true;
26934         }
26935         
26936         
26937         // Traverse the tree
26938         i = 0;
26939         var currentElementChild = currentElement.childNodes.item(i);
26940         var allText = true;
26941         var innerHTML  = '';
26942         lastnode = '';
26943         while (currentElementChild) {
26944             // Formatting code (indent the tree so it looks nice on the screen)
26945             var nopad = nopadtext;
26946             if (lastnode == 'SPAN') {
26947                 nopad  = true;
26948             }
26949             // text
26950             if  (currentElementChild.nodeName == '#text') {
26951                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26952                 toadd = nopadtext ? toadd : toadd.trim();
26953                 if (!nopad && toadd.length > 80) {
26954                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26955                 }
26956                 innerHTML  += toadd;
26957                 
26958                 i++;
26959                 currentElementChild = currentElement.childNodes.item(i);
26960                 lastNode = '';
26961                 continue;
26962             }
26963             allText = false;
26964             
26965             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26966                 
26967             // Recursively traverse the tree structure of the child node
26968             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26969             lastnode = currentElementChild.nodeName;
26970             i++;
26971             currentElementChild=currentElement.childNodes.item(i);
26972         }
26973         
26974         ret += innerHTML;
26975         
26976         if (!allText) {
26977                 // The remaining code is mostly for formatting the tree
26978             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26979         }
26980         
26981         
26982         if (tagName) {
26983             ret+= "</"+tagName+">";
26984         }
26985         return ret;
26986         
26987     },
26988         
26989     applyBlacklists : function()
26990     {
26991         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26992         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26993         
26994         this.white = [];
26995         this.black = [];
26996         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26997             if (b.indexOf(tag) > -1) {
26998                 return;
26999             }
27000             this.white.push(tag);
27001             
27002         }, this);
27003         
27004         Roo.each(w, function(tag) {
27005             if (b.indexOf(tag) > -1) {
27006                 return;
27007             }
27008             if (this.white.indexOf(tag) > -1) {
27009                 return;
27010             }
27011             this.white.push(tag);
27012             
27013         }, this);
27014         
27015         
27016         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27017             if (w.indexOf(tag) > -1) {
27018                 return;
27019             }
27020             this.black.push(tag);
27021             
27022         }, this);
27023         
27024         Roo.each(b, function(tag) {
27025             if (w.indexOf(tag) > -1) {
27026                 return;
27027             }
27028             if (this.black.indexOf(tag) > -1) {
27029                 return;
27030             }
27031             this.black.push(tag);
27032             
27033         }, this);
27034         
27035         
27036         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27037         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27038         
27039         this.cwhite = [];
27040         this.cblack = [];
27041         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27042             if (b.indexOf(tag) > -1) {
27043                 return;
27044             }
27045             this.cwhite.push(tag);
27046             
27047         }, this);
27048         
27049         Roo.each(w, function(tag) {
27050             if (b.indexOf(tag) > -1) {
27051                 return;
27052             }
27053             if (this.cwhite.indexOf(tag) > -1) {
27054                 return;
27055             }
27056             this.cwhite.push(tag);
27057             
27058         }, this);
27059         
27060         
27061         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27062             if (w.indexOf(tag) > -1) {
27063                 return;
27064             }
27065             this.cblack.push(tag);
27066             
27067         }, this);
27068         
27069         Roo.each(b, function(tag) {
27070             if (w.indexOf(tag) > -1) {
27071                 return;
27072             }
27073             if (this.cblack.indexOf(tag) > -1) {
27074                 return;
27075             }
27076             this.cblack.push(tag);
27077             
27078         }, this);
27079     },
27080     
27081     setStylesheets : function(stylesheets)
27082     {
27083         if(typeof(stylesheets) == 'string'){
27084             Roo.get(this.iframe.contentDocument.head).createChild({
27085                 tag : 'link',
27086                 rel : 'stylesheet',
27087                 type : 'text/css',
27088                 href : stylesheets
27089             });
27090             
27091             return;
27092         }
27093         var _this = this;
27094      
27095         Roo.each(stylesheets, function(s) {
27096             if(!s.length){
27097                 return;
27098             }
27099             
27100             Roo.get(_this.iframe.contentDocument.head).createChild({
27101                 tag : 'link',
27102                 rel : 'stylesheet',
27103                 type : 'text/css',
27104                 href : s
27105             });
27106         });
27107
27108         
27109     },
27110     
27111     removeStylesheets : function()
27112     {
27113         var _this = this;
27114         
27115         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27116             s.remove();
27117         });
27118     },
27119     
27120     setStyle : function(style)
27121     {
27122         Roo.get(this.iframe.contentDocument.head).createChild({
27123             tag : 'style',
27124             type : 'text/css',
27125             html : style
27126         });
27127
27128         return;
27129     }
27130     
27131     // hide stuff that is not compatible
27132     /**
27133      * @event blur
27134      * @hide
27135      */
27136     /**
27137      * @event change
27138      * @hide
27139      */
27140     /**
27141      * @event focus
27142      * @hide
27143      */
27144     /**
27145      * @event specialkey
27146      * @hide
27147      */
27148     /**
27149      * @cfg {String} fieldClass @hide
27150      */
27151     /**
27152      * @cfg {String} focusClass @hide
27153      */
27154     /**
27155      * @cfg {String} autoCreate @hide
27156      */
27157     /**
27158      * @cfg {String} inputType @hide
27159      */
27160     /**
27161      * @cfg {String} invalidClass @hide
27162      */
27163     /**
27164      * @cfg {String} invalidText @hide
27165      */
27166     /**
27167      * @cfg {String} msgFx @hide
27168      */
27169     /**
27170      * @cfg {String} validateOnBlur @hide
27171      */
27172 });
27173
27174 Roo.HtmlEditorCore.white = [
27175         'area', 'br', 'img', 'input', 'hr', 'wbr',
27176         
27177        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27178        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27179        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27180        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27181        'table',   'ul',         'xmp', 
27182        
27183        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27184       'thead',   'tr', 
27185      
27186       'dir', 'menu', 'ol', 'ul', 'dl',
27187        
27188       'embed',  'object'
27189 ];
27190
27191
27192 Roo.HtmlEditorCore.black = [
27193     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27194         'applet', // 
27195         'base',   'basefont', 'bgsound', 'blink',  'body', 
27196         'frame',  'frameset', 'head',    'html',   'ilayer', 
27197         'iframe', 'layer',  'link',     'meta',    'object',   
27198         'script', 'style' ,'title',  'xml' // clean later..
27199 ];
27200 Roo.HtmlEditorCore.clean = [
27201     'script', 'style', 'title', 'xml'
27202 ];
27203 Roo.HtmlEditorCore.remove = [
27204     'font'
27205 ];
27206 // attributes..
27207
27208 Roo.HtmlEditorCore.ablack = [
27209     'on'
27210 ];
27211     
27212 Roo.HtmlEditorCore.aclean = [ 
27213     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27214 ];
27215
27216 // protocols..
27217 Roo.HtmlEditorCore.pwhite= [
27218         'http',  'https',  'mailto'
27219 ];
27220
27221 // white listed style attributes.
27222 Roo.HtmlEditorCore.cwhite= [
27223       //  'text-align', /// default is to allow most things..
27224       
27225          
27226 //        'font-size'//??
27227 ];
27228
27229 // black listed style attributes.
27230 Roo.HtmlEditorCore.cblack= [
27231       //  'font-size' -- this can be set by the project 
27232 ];
27233
27234
27235 Roo.HtmlEditorCore.swapCodes   =[ 
27236     [    8211, "&#8211;" ], 
27237     [    8212, "&#8212;" ], 
27238     [    8216,  "'" ],  
27239     [    8217, "'" ],  
27240     [    8220, '"' ],  
27241     [    8221, '"' ],  
27242     [    8226, "*" ],  
27243     [    8230, "..." ]
27244 ]; 
27245
27246     /*
27247  * - LGPL
27248  *
27249  * HtmlEditor
27250  * 
27251  */
27252
27253 /**
27254  * @class Roo.bootstrap.HtmlEditor
27255  * @extends Roo.bootstrap.TextArea
27256  * Bootstrap HtmlEditor class
27257
27258  * @constructor
27259  * Create a new HtmlEditor
27260  * @param {Object} config The config object
27261  */
27262
27263 Roo.bootstrap.HtmlEditor = function(config){
27264     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27265     if (!this.toolbars) {
27266         this.toolbars = [];
27267     }
27268     
27269     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27270     this.addEvents({
27271             /**
27272              * @event initialize
27273              * Fires when the editor is fully initialized (including the iframe)
27274              * @param {HtmlEditor} this
27275              */
27276             initialize: true,
27277             /**
27278              * @event activate
27279              * Fires when the editor is first receives the focus. Any insertion must wait
27280              * until after this event.
27281              * @param {HtmlEditor} this
27282              */
27283             activate: true,
27284              /**
27285              * @event beforesync
27286              * Fires before the textarea is updated with content from the editor iframe. Return false
27287              * to cancel the sync.
27288              * @param {HtmlEditor} this
27289              * @param {String} html
27290              */
27291             beforesync: true,
27292              /**
27293              * @event beforepush
27294              * Fires before the iframe editor is updated with content from the textarea. Return false
27295              * to cancel the push.
27296              * @param {HtmlEditor} this
27297              * @param {String} html
27298              */
27299             beforepush: true,
27300              /**
27301              * @event sync
27302              * Fires when the textarea is updated with content from the editor iframe.
27303              * @param {HtmlEditor} this
27304              * @param {String} html
27305              */
27306             sync: true,
27307              /**
27308              * @event push
27309              * Fires when the iframe editor is updated with content from the textarea.
27310              * @param {HtmlEditor} this
27311              * @param {String} html
27312              */
27313             push: true,
27314              /**
27315              * @event editmodechange
27316              * Fires when the editor switches edit modes
27317              * @param {HtmlEditor} this
27318              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27319              */
27320             editmodechange: true,
27321             /**
27322              * @event editorevent
27323              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27324              * @param {HtmlEditor} this
27325              */
27326             editorevent: true,
27327             /**
27328              * @event firstfocus
27329              * Fires when on first focus - needed by toolbars..
27330              * @param {HtmlEditor} this
27331              */
27332             firstfocus: true,
27333             /**
27334              * @event autosave
27335              * Auto save the htmlEditor value as a file into Events
27336              * @param {HtmlEditor} this
27337              */
27338             autosave: true,
27339             /**
27340              * @event savedpreview
27341              * preview the saved version of htmlEditor
27342              * @param {HtmlEditor} this
27343              */
27344             savedpreview: true
27345         });
27346 };
27347
27348
27349 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27350     
27351     
27352       /**
27353      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27354      */
27355     toolbars : false,
27356     
27357      /**
27358     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27359     */
27360     btns : [],
27361    
27362      /**
27363      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27364      *                        Roo.resizable.
27365      */
27366     resizable : false,
27367      /**
27368      * @cfg {Number} height (in pixels)
27369      */   
27370     height: 300,
27371    /**
27372      * @cfg {Number} width (in pixels)
27373      */   
27374     width: false,
27375     
27376     /**
27377      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27378      * 
27379      */
27380     stylesheets: false,
27381     
27382     // id of frame..
27383     frameId: false,
27384     
27385     // private properties
27386     validationEvent : false,
27387     deferHeight: true,
27388     initialized : false,
27389     activated : false,
27390     
27391     onFocus : Roo.emptyFn,
27392     iframePad:3,
27393     hideMode:'offsets',
27394     
27395     tbContainer : false,
27396     
27397     bodyCls : '',
27398     
27399     toolbarContainer :function() {
27400         return this.wrap.select('.x-html-editor-tb',true).first();
27401     },
27402
27403     /**
27404      * Protected method that will not generally be called directly. It
27405      * is called when the editor creates its toolbar. Override this method if you need to
27406      * add custom toolbar buttons.
27407      * @param {HtmlEditor} editor
27408      */
27409     createToolbar : function(){
27410         Roo.log('renewing');
27411         Roo.log("create toolbars");
27412         
27413         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27414         this.toolbars[0].render(this.toolbarContainer());
27415         
27416         return;
27417         
27418 //        if (!editor.toolbars || !editor.toolbars.length) {
27419 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27420 //        }
27421 //        
27422 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27423 //            editor.toolbars[i] = Roo.factory(
27424 //                    typeof(editor.toolbars[i]) == 'string' ?
27425 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27426 //                Roo.bootstrap.HtmlEditor);
27427 //            editor.toolbars[i].init(editor);
27428 //        }
27429     },
27430
27431      
27432     // private
27433     onRender : function(ct, position)
27434     {
27435        // Roo.log("Call onRender: " + this.xtype);
27436         var _t = this;
27437         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27438       
27439         this.wrap = this.inputEl().wrap({
27440             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27441         });
27442         
27443         this.editorcore.onRender(ct, position);
27444          
27445         if (this.resizable) {
27446             this.resizeEl = new Roo.Resizable(this.wrap, {
27447                 pinned : true,
27448                 wrap: true,
27449                 dynamic : true,
27450                 minHeight : this.height,
27451                 height: this.height,
27452                 handles : this.resizable,
27453                 width: this.width,
27454                 listeners : {
27455                     resize : function(r, w, h) {
27456                         _t.onResize(w,h); // -something
27457                     }
27458                 }
27459             });
27460             
27461         }
27462         this.createToolbar(this);
27463        
27464         
27465         if(!this.width && this.resizable){
27466             this.setSize(this.wrap.getSize());
27467         }
27468         if (this.resizeEl) {
27469             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27470             // should trigger onReize..
27471         }
27472         
27473     },
27474
27475     // private
27476     onResize : function(w, h)
27477     {
27478         Roo.log('resize: ' +w + ',' + h );
27479         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27480         var ew = false;
27481         var eh = false;
27482         
27483         if(this.inputEl() ){
27484             if(typeof w == 'number'){
27485                 var aw = w - this.wrap.getFrameWidth('lr');
27486                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27487                 ew = aw;
27488             }
27489             if(typeof h == 'number'){
27490                  var tbh = -11;  // fixme it needs to tool bar size!
27491                 for (var i =0; i < this.toolbars.length;i++) {
27492                     // fixme - ask toolbars for heights?
27493                     tbh += this.toolbars[i].el.getHeight();
27494                     //if (this.toolbars[i].footer) {
27495                     //    tbh += this.toolbars[i].footer.el.getHeight();
27496                     //}
27497                 }
27498               
27499                 
27500                 
27501                 
27502                 
27503                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27504                 ah -= 5; // knock a few pixes off for look..
27505                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27506                 var eh = ah;
27507             }
27508         }
27509         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27510         this.editorcore.onResize(ew,eh);
27511         
27512     },
27513
27514     /**
27515      * Toggles the editor between standard and source edit mode.
27516      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27517      */
27518     toggleSourceEdit : function(sourceEditMode)
27519     {
27520         this.editorcore.toggleSourceEdit(sourceEditMode);
27521         
27522         if(this.editorcore.sourceEditMode){
27523             Roo.log('editor - showing textarea');
27524             
27525 //            Roo.log('in');
27526 //            Roo.log(this.syncValue());
27527             this.syncValue();
27528             this.inputEl().removeClass(['hide', 'x-hidden']);
27529             this.inputEl().dom.removeAttribute('tabIndex');
27530             this.inputEl().focus();
27531         }else{
27532             Roo.log('editor - hiding textarea');
27533 //            Roo.log('out')
27534 //            Roo.log(this.pushValue()); 
27535             this.pushValue();
27536             
27537             this.inputEl().addClass(['hide', 'x-hidden']);
27538             this.inputEl().dom.setAttribute('tabIndex', -1);
27539             //this.deferFocus();
27540         }
27541          
27542         if(this.resizable){
27543             this.setSize(this.wrap.getSize());
27544         }
27545         
27546         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27547     },
27548  
27549     // private (for BoxComponent)
27550     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27551
27552     // private (for BoxComponent)
27553     getResizeEl : function(){
27554         return this.wrap;
27555     },
27556
27557     // private (for BoxComponent)
27558     getPositionEl : function(){
27559         return this.wrap;
27560     },
27561
27562     // private
27563     initEvents : function(){
27564         this.originalValue = this.getValue();
27565     },
27566
27567 //    /**
27568 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27569 //     * @method
27570 //     */
27571 //    markInvalid : Roo.emptyFn,
27572 //    /**
27573 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27574 //     * @method
27575 //     */
27576 //    clearInvalid : Roo.emptyFn,
27577
27578     setValue : function(v){
27579         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27580         this.editorcore.pushValue();
27581     },
27582
27583      
27584     // private
27585     deferFocus : function(){
27586         this.focus.defer(10, this);
27587     },
27588
27589     // doc'ed in Field
27590     focus : function(){
27591         this.editorcore.focus();
27592         
27593     },
27594       
27595
27596     // private
27597     onDestroy : function(){
27598         
27599         
27600         
27601         if(this.rendered){
27602             
27603             for (var i =0; i < this.toolbars.length;i++) {
27604                 // fixme - ask toolbars for heights?
27605                 this.toolbars[i].onDestroy();
27606             }
27607             
27608             this.wrap.dom.innerHTML = '';
27609             this.wrap.remove();
27610         }
27611     },
27612
27613     // private
27614     onFirstFocus : function(){
27615         //Roo.log("onFirstFocus");
27616         this.editorcore.onFirstFocus();
27617          for (var i =0; i < this.toolbars.length;i++) {
27618             this.toolbars[i].onFirstFocus();
27619         }
27620         
27621     },
27622     
27623     // private
27624     syncValue : function()
27625     {   
27626         this.editorcore.syncValue();
27627     },
27628     
27629     pushValue : function()
27630     {   
27631         this.editorcore.pushValue();
27632     }
27633      
27634     
27635     // hide stuff that is not compatible
27636     /**
27637      * @event blur
27638      * @hide
27639      */
27640     /**
27641      * @event change
27642      * @hide
27643      */
27644     /**
27645      * @event focus
27646      * @hide
27647      */
27648     /**
27649      * @event specialkey
27650      * @hide
27651      */
27652     /**
27653      * @cfg {String} fieldClass @hide
27654      */
27655     /**
27656      * @cfg {String} focusClass @hide
27657      */
27658     /**
27659      * @cfg {String} autoCreate @hide
27660      */
27661     /**
27662      * @cfg {String} inputType @hide
27663      */
27664      
27665     /**
27666      * @cfg {String} invalidText @hide
27667      */
27668     /**
27669      * @cfg {String} msgFx @hide
27670      */
27671     /**
27672      * @cfg {String} validateOnBlur @hide
27673      */
27674 });
27675  
27676     
27677    
27678    
27679    
27680       
27681 Roo.namespace('Roo.bootstrap.htmleditor');
27682 /**
27683  * @class Roo.bootstrap.HtmlEditorToolbar1
27684  * Basic Toolbar
27685  * 
27686  * @example
27687  * Usage:
27688  *
27689  new Roo.bootstrap.HtmlEditor({
27690     ....
27691     toolbars : [
27692         new Roo.bootstrap.HtmlEditorToolbar1({
27693             disable : { fonts: 1 , format: 1, ..., ... , ...],
27694             btns : [ .... ]
27695         })
27696     }
27697      
27698  * 
27699  * @cfg {Object} disable List of elements to disable..
27700  * @cfg {Array} btns List of additional buttons.
27701  * 
27702  * 
27703  * NEEDS Extra CSS? 
27704  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27705  */
27706  
27707 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27708 {
27709     
27710     Roo.apply(this, config);
27711     
27712     // default disabled, based on 'good practice'..
27713     this.disable = this.disable || {};
27714     Roo.applyIf(this.disable, {
27715         fontSize : true,
27716         colors : true,
27717         specialElements : true
27718     });
27719     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27720     
27721     this.editor = config.editor;
27722     this.editorcore = config.editor.editorcore;
27723     
27724     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27725     
27726     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27727     // dont call parent... till later.
27728 }
27729 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27730      
27731     bar : true,
27732     
27733     editor : false,
27734     editorcore : false,
27735     
27736     
27737     formats : [
27738         "p" ,  
27739         "h1","h2","h3","h4","h5","h6", 
27740         "pre", "code", 
27741         "abbr", "acronym", "address", "cite", "samp", "var",
27742         'div','span'
27743     ],
27744     
27745     onRender : function(ct, position)
27746     {
27747        // Roo.log("Call onRender: " + this.xtype);
27748         
27749        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27750        Roo.log(this.el);
27751        this.el.dom.style.marginBottom = '0';
27752        var _this = this;
27753        var editorcore = this.editorcore;
27754        var editor= this.editor;
27755        
27756        var children = [];
27757        var btn = function(id,cmd , toggle, handler, html){
27758        
27759             var  event = toggle ? 'toggle' : 'click';
27760        
27761             var a = {
27762                 size : 'sm',
27763                 xtype: 'Button',
27764                 xns: Roo.bootstrap,
27765                 //glyphicon : id,
27766                 fa: id,
27767                 cmd : id || cmd,
27768                 enableToggle:toggle !== false,
27769                 html : html || '',
27770                 pressed : toggle ? false : null,
27771                 listeners : {}
27772             };
27773             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27774                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27775             };
27776             children.push(a);
27777             return a;
27778        }
27779        
27780     //    var cb_box = function...
27781         
27782         var style = {
27783                 xtype: 'Button',
27784                 size : 'sm',
27785                 xns: Roo.bootstrap,
27786                 fa : 'font',
27787                 //html : 'submit'
27788                 menu : {
27789                     xtype: 'Menu',
27790                     xns: Roo.bootstrap,
27791                     items:  []
27792                 }
27793         };
27794         Roo.each(this.formats, function(f) {
27795             style.menu.items.push({
27796                 xtype :'MenuItem',
27797                 xns: Roo.bootstrap,
27798                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27799                 tagname : f,
27800                 listeners : {
27801                     click : function()
27802                     {
27803                         editorcore.insertTag(this.tagname);
27804                         editor.focus();
27805                     }
27806                 }
27807                 
27808             });
27809         });
27810         children.push(style);   
27811         
27812         btn('bold',false,true);
27813         btn('italic',false,true);
27814         btn('align-left', 'justifyleft',true);
27815         btn('align-center', 'justifycenter',true);
27816         btn('align-right' , 'justifyright',true);
27817         btn('link', false, false, function(btn) {
27818             //Roo.log("create link?");
27819             var url = prompt(this.createLinkText, this.defaultLinkValue);
27820             if(url && url != 'http:/'+'/'){
27821                 this.editorcore.relayCmd('createlink', url);
27822             }
27823         }),
27824         btn('list','insertunorderedlist',true);
27825         btn('pencil', false,true, function(btn){
27826                 Roo.log(this);
27827                 this.toggleSourceEdit(btn.pressed);
27828         });
27829         
27830         if (this.editor.btns.length > 0) {
27831             for (var i = 0; i<this.editor.btns.length; i++) {
27832                 children.push(this.editor.btns[i]);
27833             }
27834         }
27835         
27836         /*
27837         var cog = {
27838                 xtype: 'Button',
27839                 size : 'sm',
27840                 xns: Roo.bootstrap,
27841                 glyphicon : 'cog',
27842                 //html : 'submit'
27843                 menu : {
27844                     xtype: 'Menu',
27845                     xns: Roo.bootstrap,
27846                     items:  []
27847                 }
27848         };
27849         
27850         cog.menu.items.push({
27851             xtype :'MenuItem',
27852             xns: Roo.bootstrap,
27853             html : Clean styles,
27854             tagname : f,
27855             listeners : {
27856                 click : function()
27857                 {
27858                     editorcore.insertTag(this.tagname);
27859                     editor.focus();
27860                 }
27861             }
27862             
27863         });
27864        */
27865         
27866          
27867        this.xtype = 'NavSimplebar';
27868         
27869         for(var i=0;i< children.length;i++) {
27870             
27871             this.buttons.add(this.addxtypeChild(children[i]));
27872             
27873         }
27874         
27875         editor.on('editorevent', this.updateToolbar, this);
27876     },
27877     onBtnClick : function(id)
27878     {
27879        this.editorcore.relayCmd(id);
27880        this.editorcore.focus();
27881     },
27882     
27883     /**
27884      * Protected method that will not generally be called directly. It triggers
27885      * a toolbar update by reading the markup state of the current selection in the editor.
27886      */
27887     updateToolbar: function(){
27888
27889         if(!this.editorcore.activated){
27890             this.editor.onFirstFocus(); // is this neeed?
27891             return;
27892         }
27893
27894         var btns = this.buttons; 
27895         var doc = this.editorcore.doc;
27896         btns.get('bold').setActive(doc.queryCommandState('bold'));
27897         btns.get('italic').setActive(doc.queryCommandState('italic'));
27898         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27899         
27900         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27901         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27902         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27903         
27904         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27905         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27906          /*
27907         
27908         var ans = this.editorcore.getAllAncestors();
27909         if (this.formatCombo) {
27910             
27911             
27912             var store = this.formatCombo.store;
27913             this.formatCombo.setValue("");
27914             for (var i =0; i < ans.length;i++) {
27915                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27916                     // select it..
27917                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27918                     break;
27919                 }
27920             }
27921         }
27922         
27923         
27924         
27925         // hides menus... - so this cant be on a menu...
27926         Roo.bootstrap.MenuMgr.hideAll();
27927         */
27928         Roo.bootstrap.MenuMgr.hideAll();
27929         //this.editorsyncValue();
27930     },
27931     onFirstFocus: function() {
27932         this.buttons.each(function(item){
27933            item.enable();
27934         });
27935     },
27936     toggleSourceEdit : function(sourceEditMode){
27937         
27938           
27939         if(sourceEditMode){
27940             Roo.log("disabling buttons");
27941            this.buttons.each( function(item){
27942                 if(item.cmd != 'pencil'){
27943                     item.disable();
27944                 }
27945             });
27946           
27947         }else{
27948             Roo.log("enabling buttons");
27949             if(this.editorcore.initialized){
27950                 this.buttons.each( function(item){
27951                     item.enable();
27952                 });
27953             }
27954             
27955         }
27956         Roo.log("calling toggole on editor");
27957         // tell the editor that it's been pressed..
27958         this.editor.toggleSourceEdit(sourceEditMode);
27959        
27960     }
27961 });
27962
27963
27964
27965
27966  
27967 /*
27968  * - LGPL
27969  */
27970
27971 /**
27972  * @class Roo.bootstrap.Markdown
27973  * @extends Roo.bootstrap.TextArea
27974  * Bootstrap Showdown editable area
27975  * @cfg {string} content
27976  * 
27977  * @constructor
27978  * Create a new Showdown
27979  */
27980
27981 Roo.bootstrap.Markdown = function(config){
27982     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27983    
27984 };
27985
27986 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27987     
27988     editing :false,
27989     
27990     initEvents : function()
27991     {
27992         
27993         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27994         this.markdownEl = this.el.createChild({
27995             cls : 'roo-markdown-area'
27996         });
27997         this.inputEl().addClass('d-none');
27998         if (this.getValue() == '') {
27999             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28000             
28001         } else {
28002             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28003         }
28004         this.markdownEl.on('click', this.toggleTextEdit, this);
28005         this.on('blur', this.toggleTextEdit, this);
28006         this.on('specialkey', this.resizeTextArea, this);
28007     },
28008     
28009     toggleTextEdit : function()
28010     {
28011         var sh = this.markdownEl.getHeight();
28012         this.inputEl().addClass('d-none');
28013         this.markdownEl.addClass('d-none');
28014         if (!this.editing) {
28015             // show editor?
28016             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28017             this.inputEl().removeClass('d-none');
28018             this.inputEl().focus();
28019             this.editing = true;
28020             return;
28021         }
28022         // show showdown...
28023         this.updateMarkdown();
28024         this.markdownEl.removeClass('d-none');
28025         this.editing = false;
28026         return;
28027     },
28028     updateMarkdown : function()
28029     {
28030         if (this.getValue() == '') {
28031             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28032             return;
28033         }
28034  
28035         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28036     },
28037     
28038     resizeTextArea: function () {
28039         
28040         var sh = 100;
28041         Roo.log([sh, this.getValue().split("\n").length * 30]);
28042         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28043     },
28044     setValue : function(val)
28045     {
28046         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28047         if (!this.editing) {
28048             this.updateMarkdown();
28049         }
28050         
28051     },
28052     focus : function()
28053     {
28054         if (!this.editing) {
28055             this.toggleTextEdit();
28056         }
28057         
28058     }
28059
28060
28061 });/*
28062  * Based on:
28063  * Ext JS Library 1.1.1
28064  * Copyright(c) 2006-2007, Ext JS, LLC.
28065  *
28066  * Originally Released Under LGPL - original licence link has changed is not relivant.
28067  *
28068  * Fork - LGPL
28069  * <script type="text/javascript">
28070  */
28071  
28072 /**
28073  * @class Roo.bootstrap.PagingToolbar
28074  * @extends Roo.bootstrap.NavSimplebar
28075  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28076  * @constructor
28077  * Create a new PagingToolbar
28078  * @param {Object} config The config object
28079  * @param {Roo.data.Store} store
28080  */
28081 Roo.bootstrap.PagingToolbar = function(config)
28082 {
28083     // old args format still supported... - xtype is prefered..
28084         // created from xtype...
28085     
28086     this.ds = config.dataSource;
28087     
28088     if (config.store && !this.ds) {
28089         this.store= Roo.factory(config.store, Roo.data);
28090         this.ds = this.store;
28091         this.ds.xmodule = this.xmodule || false;
28092     }
28093     
28094     this.toolbarItems = [];
28095     if (config.items) {
28096         this.toolbarItems = config.items;
28097     }
28098     
28099     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28100     
28101     this.cursor = 0;
28102     
28103     if (this.ds) { 
28104         this.bind(this.ds);
28105     }
28106     
28107     if (Roo.bootstrap.version == 4) {
28108         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28109     } else {
28110         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28111     }
28112     
28113 };
28114
28115 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28116     /**
28117      * @cfg {Roo.data.Store} dataSource
28118      * The underlying data store providing the paged data
28119      */
28120     /**
28121      * @cfg {String/HTMLElement/Element} container
28122      * container The id or element that will contain the toolbar
28123      */
28124     /**
28125      * @cfg {Boolean} displayInfo
28126      * True to display the displayMsg (defaults to false)
28127      */
28128     /**
28129      * @cfg {Number} pageSize
28130      * The number of records to display per page (defaults to 20)
28131      */
28132     pageSize: 20,
28133     /**
28134      * @cfg {String} displayMsg
28135      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28136      */
28137     displayMsg : 'Displaying {0} - {1} of {2}',
28138     /**
28139      * @cfg {String} emptyMsg
28140      * The message to display when no records are found (defaults to "No data to display")
28141      */
28142     emptyMsg : 'No data to display',
28143     /**
28144      * Customizable piece of the default paging text (defaults to "Page")
28145      * @type String
28146      */
28147     beforePageText : "Page",
28148     /**
28149      * Customizable piece of the default paging text (defaults to "of %0")
28150      * @type String
28151      */
28152     afterPageText : "of {0}",
28153     /**
28154      * Customizable piece of the default paging text (defaults to "First Page")
28155      * @type String
28156      */
28157     firstText : "First Page",
28158     /**
28159      * Customizable piece of the default paging text (defaults to "Previous Page")
28160      * @type String
28161      */
28162     prevText : "Previous Page",
28163     /**
28164      * Customizable piece of the default paging text (defaults to "Next Page")
28165      * @type String
28166      */
28167     nextText : "Next Page",
28168     /**
28169      * Customizable piece of the default paging text (defaults to "Last Page")
28170      * @type String
28171      */
28172     lastText : "Last Page",
28173     /**
28174      * Customizable piece of the default paging text (defaults to "Refresh")
28175      * @type String
28176      */
28177     refreshText : "Refresh",
28178
28179     buttons : false,
28180     // private
28181     onRender : function(ct, position) 
28182     {
28183         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28184         this.navgroup.parentId = this.id;
28185         this.navgroup.onRender(this.el, null);
28186         // add the buttons to the navgroup
28187         
28188         if(this.displayInfo){
28189             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28190             this.displayEl = this.el.select('.x-paging-info', true).first();
28191 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28192 //            this.displayEl = navel.el.select('span',true).first();
28193         }
28194         
28195         var _this = this;
28196         
28197         if(this.buttons){
28198             Roo.each(_this.buttons, function(e){ // this might need to use render????
28199                Roo.factory(e).render(_this.el);
28200             });
28201         }
28202             
28203         Roo.each(_this.toolbarItems, function(e) {
28204             _this.navgroup.addItem(e);
28205         });
28206         
28207         
28208         this.first = this.navgroup.addItem({
28209             tooltip: this.firstText,
28210             cls: "prev btn-outline-secondary",
28211             html : ' <i class="fa fa-step-backward"></i>',
28212             disabled: true,
28213             preventDefault: true,
28214             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28215         });
28216         
28217         this.prev =  this.navgroup.addItem({
28218             tooltip: this.prevText,
28219             cls: "prev btn-outline-secondary",
28220             html : ' <i class="fa fa-backward"></i>',
28221             disabled: true,
28222             preventDefault: true,
28223             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28224         });
28225     //this.addSeparator();
28226         
28227         
28228         var field = this.navgroup.addItem( {
28229             tagtype : 'span',
28230             cls : 'x-paging-position  btn-outline-secondary',
28231              disabled: true,
28232             html : this.beforePageText  +
28233                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28234                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28235          } ); //?? escaped?
28236         
28237         this.field = field.el.select('input', true).first();
28238         this.field.on("keydown", this.onPagingKeydown, this);
28239         this.field.on("focus", function(){this.dom.select();});
28240     
28241     
28242         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28243         //this.field.setHeight(18);
28244         //this.addSeparator();
28245         this.next = this.navgroup.addItem({
28246             tooltip: this.nextText,
28247             cls: "next btn-outline-secondary",
28248             html : ' <i class="fa fa-forward"></i>',
28249             disabled: true,
28250             preventDefault: true,
28251             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28252         });
28253         this.last = this.navgroup.addItem({
28254             tooltip: this.lastText,
28255             html : ' <i class="fa fa-step-forward"></i>',
28256             cls: "next btn-outline-secondary",
28257             disabled: true,
28258             preventDefault: true,
28259             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28260         });
28261     //this.addSeparator();
28262         this.loading = this.navgroup.addItem({
28263             tooltip: this.refreshText,
28264             cls: "btn-outline-secondary",
28265             html : ' <i class="fa fa-refresh"></i>',
28266             preventDefault: true,
28267             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28268         });
28269         
28270     },
28271
28272     // private
28273     updateInfo : function(){
28274         if(this.displayEl){
28275             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28276             var msg = count == 0 ?
28277                 this.emptyMsg :
28278                 String.format(
28279                     this.displayMsg,
28280                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28281                 );
28282             this.displayEl.update(msg);
28283         }
28284     },
28285
28286     // private
28287     onLoad : function(ds, r, o)
28288     {
28289         this.cursor = o.params && o.params.start ? o.params.start : 0;
28290         
28291         var d = this.getPageData(),
28292             ap = d.activePage,
28293             ps = d.pages;
28294         
28295         
28296         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28297         this.field.dom.value = ap;
28298         this.first.setDisabled(ap == 1);
28299         this.prev.setDisabled(ap == 1);
28300         this.next.setDisabled(ap == ps);
28301         this.last.setDisabled(ap == ps);
28302         this.loading.enable();
28303         this.updateInfo();
28304     },
28305
28306     // private
28307     getPageData : function(){
28308         var total = this.ds.getTotalCount();
28309         return {
28310             total : total,
28311             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28312             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28313         };
28314     },
28315
28316     // private
28317     onLoadError : function(){
28318         this.loading.enable();
28319     },
28320
28321     // private
28322     onPagingKeydown : function(e){
28323         var k = e.getKey();
28324         var d = this.getPageData();
28325         if(k == e.RETURN){
28326             var v = this.field.dom.value, pageNum;
28327             if(!v || isNaN(pageNum = parseInt(v, 10))){
28328                 this.field.dom.value = d.activePage;
28329                 return;
28330             }
28331             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28332             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28333             e.stopEvent();
28334         }
28335         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))
28336         {
28337           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28338           this.field.dom.value = pageNum;
28339           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28340           e.stopEvent();
28341         }
28342         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28343         {
28344           var v = this.field.dom.value, pageNum; 
28345           var increment = (e.shiftKey) ? 10 : 1;
28346           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28347                 increment *= -1;
28348           }
28349           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28350             this.field.dom.value = d.activePage;
28351             return;
28352           }
28353           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28354           {
28355             this.field.dom.value = parseInt(v, 10) + increment;
28356             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28357             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28358           }
28359           e.stopEvent();
28360         }
28361     },
28362
28363     // private
28364     beforeLoad : function(){
28365         if(this.loading){
28366             this.loading.disable();
28367         }
28368     },
28369
28370     // private
28371     onClick : function(which){
28372         
28373         var ds = this.ds;
28374         if (!ds) {
28375             return;
28376         }
28377         
28378         switch(which){
28379             case "first":
28380                 ds.load({params:{start: 0, limit: this.pageSize}});
28381             break;
28382             case "prev":
28383                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28384             break;
28385             case "next":
28386                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28387             break;
28388             case "last":
28389                 var total = ds.getTotalCount();
28390                 var extra = total % this.pageSize;
28391                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28392                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28393             break;
28394             case "refresh":
28395                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28396             break;
28397         }
28398     },
28399
28400     /**
28401      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28402      * @param {Roo.data.Store} store The data store to unbind
28403      */
28404     unbind : function(ds){
28405         ds.un("beforeload", this.beforeLoad, this);
28406         ds.un("load", this.onLoad, this);
28407         ds.un("loadexception", this.onLoadError, this);
28408         ds.un("remove", this.updateInfo, this);
28409         ds.un("add", this.updateInfo, this);
28410         this.ds = undefined;
28411     },
28412
28413     /**
28414      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28415      * @param {Roo.data.Store} store The data store to bind
28416      */
28417     bind : function(ds){
28418         ds.on("beforeload", this.beforeLoad, this);
28419         ds.on("load", this.onLoad, this);
28420         ds.on("loadexception", this.onLoadError, this);
28421         ds.on("remove", this.updateInfo, this);
28422         ds.on("add", this.updateInfo, this);
28423         this.ds = ds;
28424     }
28425 });/*
28426  * - LGPL
28427  *
28428  * element
28429  * 
28430  */
28431
28432 /**
28433  * @class Roo.bootstrap.MessageBar
28434  * @extends Roo.bootstrap.Component
28435  * Bootstrap MessageBar class
28436  * @cfg {String} html contents of the MessageBar
28437  * @cfg {String} weight (info | success | warning | danger) default info
28438  * @cfg {String} beforeClass insert the bar before the given class
28439  * @cfg {Boolean} closable (true | false) default false
28440  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28441  * 
28442  * @constructor
28443  * Create a new Element
28444  * @param {Object} config The config object
28445  */
28446
28447 Roo.bootstrap.MessageBar = function(config){
28448     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28449 };
28450
28451 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28452     
28453     html: '',
28454     weight: 'info',
28455     closable: false,
28456     fixed: false,
28457     beforeClass: 'bootstrap-sticky-wrap',
28458     
28459     getAutoCreate : function(){
28460         
28461         var cfg = {
28462             tag: 'div',
28463             cls: 'alert alert-dismissable alert-' + this.weight,
28464             cn: [
28465                 {
28466                     tag: 'span',
28467                     cls: 'message',
28468                     html: this.html || ''
28469                 }
28470             ]
28471         };
28472         
28473         if(this.fixed){
28474             cfg.cls += ' alert-messages-fixed';
28475         }
28476         
28477         if(this.closable){
28478             cfg.cn.push({
28479                 tag: 'button',
28480                 cls: 'close',
28481                 html: 'x'
28482             });
28483         }
28484         
28485         return cfg;
28486     },
28487     
28488     onRender : function(ct, position)
28489     {
28490         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28491         
28492         if(!this.el){
28493             var cfg = Roo.apply({},  this.getAutoCreate());
28494             cfg.id = Roo.id();
28495             
28496             if (this.cls) {
28497                 cfg.cls += ' ' + this.cls;
28498             }
28499             if (this.style) {
28500                 cfg.style = this.style;
28501             }
28502             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28503             
28504             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28505         }
28506         
28507         this.el.select('>button.close').on('click', this.hide, this);
28508         
28509     },
28510     
28511     show : function()
28512     {
28513         if (!this.rendered) {
28514             this.render();
28515         }
28516         
28517         this.el.show();
28518         
28519         this.fireEvent('show', this);
28520         
28521     },
28522     
28523     hide : function()
28524     {
28525         if (!this.rendered) {
28526             this.render();
28527         }
28528         
28529         this.el.hide();
28530         
28531         this.fireEvent('hide', this);
28532     },
28533     
28534     update : function()
28535     {
28536 //        var e = this.el.dom.firstChild;
28537 //        
28538 //        if(this.closable){
28539 //            e = e.nextSibling;
28540 //        }
28541 //        
28542 //        e.data = this.html || '';
28543
28544         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28545     }
28546    
28547 });
28548
28549  
28550
28551      /*
28552  * - LGPL
28553  *
28554  * Graph
28555  * 
28556  */
28557
28558
28559 /**
28560  * @class Roo.bootstrap.Graph
28561  * @extends Roo.bootstrap.Component
28562  * Bootstrap Graph class
28563 > Prameters
28564  -sm {number} sm 4
28565  -md {number} md 5
28566  @cfg {String} graphtype  bar | vbar | pie
28567  @cfg {number} g_x coodinator | centre x (pie)
28568  @cfg {number} g_y coodinator | centre y (pie)
28569  @cfg {number} g_r radius (pie)
28570  @cfg {number} g_height height of the chart (respected by all elements in the set)
28571  @cfg {number} g_width width of the chart (respected by all elements in the set)
28572  @cfg {Object} title The title of the chart
28573     
28574  -{Array}  values
28575  -opts (object) options for the chart 
28576      o {
28577      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28578      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28579      o vgutter (number)
28580      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.
28581      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28582      o to
28583      o stretch (boolean)
28584      o }
28585  -opts (object) options for the pie
28586      o{
28587      o cut
28588      o startAngle (number)
28589      o endAngle (number)
28590      } 
28591  *
28592  * @constructor
28593  * Create a new Input
28594  * @param {Object} config The config object
28595  */
28596
28597 Roo.bootstrap.Graph = function(config){
28598     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28599     
28600     this.addEvents({
28601         // img events
28602         /**
28603          * @event click
28604          * The img click event for the img.
28605          * @param {Roo.EventObject} e
28606          */
28607         "click" : true
28608     });
28609 };
28610
28611 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28612     
28613     sm: 4,
28614     md: 5,
28615     graphtype: 'bar',
28616     g_height: 250,
28617     g_width: 400,
28618     g_x: 50,
28619     g_y: 50,
28620     g_r: 30,
28621     opts:{
28622         //g_colors: this.colors,
28623         g_type: 'soft',
28624         g_gutter: '20%'
28625
28626     },
28627     title : false,
28628
28629     getAutoCreate : function(){
28630         
28631         var cfg = {
28632             tag: 'div',
28633             html : null
28634         };
28635         
28636         
28637         return  cfg;
28638     },
28639
28640     onRender : function(ct,position){
28641         
28642         
28643         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28644         
28645         if (typeof(Raphael) == 'undefined') {
28646             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28647             return;
28648         }
28649         
28650         this.raphael = Raphael(this.el.dom);
28651         
28652                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28653                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28654                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28655                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28656                 /*
28657                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28658                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28659                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28660                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28661                 
28662                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28663                 r.barchart(330, 10, 300, 220, data1);
28664                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28665                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28666                 */
28667                 
28668                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28669                 // r.barchart(30, 30, 560, 250,  xdata, {
28670                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28671                 //     axis : "0 0 1 1",
28672                 //     axisxlabels :  xdata
28673                 //     //yvalues : cols,
28674                    
28675                 // });
28676 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28677 //        
28678 //        this.load(null,xdata,{
28679 //                axis : "0 0 1 1",
28680 //                axisxlabels :  xdata
28681 //                });
28682
28683     },
28684
28685     load : function(graphtype,xdata,opts)
28686     {
28687         this.raphael.clear();
28688         if(!graphtype) {
28689             graphtype = this.graphtype;
28690         }
28691         if(!opts){
28692             opts = this.opts;
28693         }
28694         var r = this.raphael,
28695             fin = function () {
28696                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28697             },
28698             fout = function () {
28699                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28700             },
28701             pfin = function() {
28702                 this.sector.stop();
28703                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28704
28705                 if (this.label) {
28706                     this.label[0].stop();
28707                     this.label[0].attr({ r: 7.5 });
28708                     this.label[1].attr({ "font-weight": 800 });
28709                 }
28710             },
28711             pfout = function() {
28712                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28713
28714                 if (this.label) {
28715                     this.label[0].animate({ r: 5 }, 500, "bounce");
28716                     this.label[1].attr({ "font-weight": 400 });
28717                 }
28718             };
28719
28720         switch(graphtype){
28721             case 'bar':
28722                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28723                 break;
28724             case 'hbar':
28725                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28726                 break;
28727             case 'pie':
28728 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28729 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28730 //            
28731                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28732                 
28733                 break;
28734
28735         }
28736         
28737         if(this.title){
28738             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28739         }
28740         
28741     },
28742     
28743     setTitle: function(o)
28744     {
28745         this.title = o;
28746     },
28747     
28748     initEvents: function() {
28749         
28750         if(!this.href){
28751             this.el.on('click', this.onClick, this);
28752         }
28753     },
28754     
28755     onClick : function(e)
28756     {
28757         Roo.log('img onclick');
28758         this.fireEvent('click', this, e);
28759     }
28760    
28761 });
28762
28763  
28764 /*
28765  * - LGPL
28766  *
28767  * numberBox
28768  * 
28769  */
28770 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28771
28772 /**
28773  * @class Roo.bootstrap.dash.NumberBox
28774  * @extends Roo.bootstrap.Component
28775  * Bootstrap NumberBox class
28776  * @cfg {String} headline Box headline
28777  * @cfg {String} content Box content
28778  * @cfg {String} icon Box icon
28779  * @cfg {String} footer Footer text
28780  * @cfg {String} fhref Footer href
28781  * 
28782  * @constructor
28783  * Create a new NumberBox
28784  * @param {Object} config The config object
28785  */
28786
28787
28788 Roo.bootstrap.dash.NumberBox = function(config){
28789     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28790     
28791 };
28792
28793 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28794     
28795     headline : '',
28796     content : '',
28797     icon : '',
28798     footer : '',
28799     fhref : '',
28800     ficon : '',
28801     
28802     getAutoCreate : function(){
28803         
28804         var cfg = {
28805             tag : 'div',
28806             cls : 'small-box ',
28807             cn : [
28808                 {
28809                     tag : 'div',
28810                     cls : 'inner',
28811                     cn :[
28812                         {
28813                             tag : 'h3',
28814                             cls : 'roo-headline',
28815                             html : this.headline
28816                         },
28817                         {
28818                             tag : 'p',
28819                             cls : 'roo-content',
28820                             html : this.content
28821                         }
28822                     ]
28823                 }
28824             ]
28825         };
28826         
28827         if(this.icon){
28828             cfg.cn.push({
28829                 tag : 'div',
28830                 cls : 'icon',
28831                 cn :[
28832                     {
28833                         tag : 'i',
28834                         cls : 'ion ' + this.icon
28835                     }
28836                 ]
28837             });
28838         }
28839         
28840         if(this.footer){
28841             var footer = {
28842                 tag : 'a',
28843                 cls : 'small-box-footer',
28844                 href : this.fhref || '#',
28845                 html : this.footer
28846             };
28847             
28848             cfg.cn.push(footer);
28849             
28850         }
28851         
28852         return  cfg;
28853     },
28854
28855     onRender : function(ct,position){
28856         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28857
28858
28859        
28860                 
28861     },
28862
28863     setHeadline: function (value)
28864     {
28865         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28866     },
28867     
28868     setFooter: function (value, href)
28869     {
28870         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28871         
28872         if(href){
28873             this.el.select('a.small-box-footer',true).first().attr('href', href);
28874         }
28875         
28876     },
28877
28878     setContent: function (value)
28879     {
28880         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28881     },
28882
28883     initEvents: function() 
28884     {   
28885         
28886     }
28887     
28888 });
28889
28890  
28891 /*
28892  * - LGPL
28893  *
28894  * TabBox
28895  * 
28896  */
28897 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28898
28899 /**
28900  * @class Roo.bootstrap.dash.TabBox
28901  * @extends Roo.bootstrap.Component
28902  * Bootstrap TabBox class
28903  * @cfg {String} title Title of the TabBox
28904  * @cfg {String} icon Icon of the TabBox
28905  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28906  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28907  * 
28908  * @constructor
28909  * Create a new TabBox
28910  * @param {Object} config The config object
28911  */
28912
28913
28914 Roo.bootstrap.dash.TabBox = function(config){
28915     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28916     this.addEvents({
28917         // raw events
28918         /**
28919          * @event addpane
28920          * When a pane is added
28921          * @param {Roo.bootstrap.dash.TabPane} pane
28922          */
28923         "addpane" : true,
28924         /**
28925          * @event activatepane
28926          * When a pane is activated
28927          * @param {Roo.bootstrap.dash.TabPane} pane
28928          */
28929         "activatepane" : true
28930         
28931          
28932     });
28933     
28934     this.panes = [];
28935 };
28936
28937 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28938
28939     title : '',
28940     icon : false,
28941     showtabs : true,
28942     tabScrollable : false,
28943     
28944     getChildContainer : function()
28945     {
28946         return this.el.select('.tab-content', true).first();
28947     },
28948     
28949     getAutoCreate : function(){
28950         
28951         var header = {
28952             tag: 'li',
28953             cls: 'pull-left header',
28954             html: this.title,
28955             cn : []
28956         };
28957         
28958         if(this.icon){
28959             header.cn.push({
28960                 tag: 'i',
28961                 cls: 'fa ' + this.icon
28962             });
28963         }
28964         
28965         var h = {
28966             tag: 'ul',
28967             cls: 'nav nav-tabs pull-right',
28968             cn: [
28969                 header
28970             ]
28971         };
28972         
28973         if(this.tabScrollable){
28974             h = {
28975                 tag: 'div',
28976                 cls: 'tab-header',
28977                 cn: [
28978                     {
28979                         tag: 'ul',
28980                         cls: 'nav nav-tabs pull-right',
28981                         cn: [
28982                             header
28983                         ]
28984                     }
28985                 ]
28986             };
28987         }
28988         
28989         var cfg = {
28990             tag: 'div',
28991             cls: 'nav-tabs-custom',
28992             cn: [
28993                 h,
28994                 {
28995                     tag: 'div',
28996                     cls: 'tab-content no-padding',
28997                     cn: []
28998                 }
28999             ]
29000         };
29001
29002         return  cfg;
29003     },
29004     initEvents : function()
29005     {
29006         //Roo.log('add add pane handler');
29007         this.on('addpane', this.onAddPane, this);
29008     },
29009      /**
29010      * Updates the box title
29011      * @param {String} html to set the title to.
29012      */
29013     setTitle : function(value)
29014     {
29015         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29016     },
29017     onAddPane : function(pane)
29018     {
29019         this.panes.push(pane);
29020         //Roo.log('addpane');
29021         //Roo.log(pane);
29022         // tabs are rendere left to right..
29023         if(!this.showtabs){
29024             return;
29025         }
29026         
29027         var ctr = this.el.select('.nav-tabs', true).first();
29028          
29029          
29030         var existing = ctr.select('.nav-tab',true);
29031         var qty = existing.getCount();;
29032         
29033         
29034         var tab = ctr.createChild({
29035             tag : 'li',
29036             cls : 'nav-tab' + (qty ? '' : ' active'),
29037             cn : [
29038                 {
29039                     tag : 'a',
29040                     href:'#',
29041                     html : pane.title
29042                 }
29043             ]
29044         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29045         pane.tab = tab;
29046         
29047         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29048         if (!qty) {
29049             pane.el.addClass('active');
29050         }
29051         
29052                 
29053     },
29054     onTabClick : function(ev,un,ob,pane)
29055     {
29056         //Roo.log('tab - prev default');
29057         ev.preventDefault();
29058         
29059         
29060         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29061         pane.tab.addClass('active');
29062         //Roo.log(pane.title);
29063         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29064         // technically we should have a deactivate event.. but maybe add later.
29065         // and it should not de-activate the selected tab...
29066         this.fireEvent('activatepane', pane);
29067         pane.el.addClass('active');
29068         pane.fireEvent('activate');
29069         
29070         
29071     },
29072     
29073     getActivePane : function()
29074     {
29075         var r = false;
29076         Roo.each(this.panes, function(p) {
29077             if(p.el.hasClass('active')){
29078                 r = p;
29079                 return false;
29080             }
29081             
29082             return;
29083         });
29084         
29085         return r;
29086     }
29087     
29088     
29089 });
29090
29091  
29092 /*
29093  * - LGPL
29094  *
29095  * Tab pane
29096  * 
29097  */
29098 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29099 /**
29100  * @class Roo.bootstrap.TabPane
29101  * @extends Roo.bootstrap.Component
29102  * Bootstrap TabPane class
29103  * @cfg {Boolean} active (false | true) Default false
29104  * @cfg {String} title title of panel
29105
29106  * 
29107  * @constructor
29108  * Create a new TabPane
29109  * @param {Object} config The config object
29110  */
29111
29112 Roo.bootstrap.dash.TabPane = function(config){
29113     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29114     
29115     this.addEvents({
29116         // raw events
29117         /**
29118          * @event activate
29119          * When a pane is activated
29120          * @param {Roo.bootstrap.dash.TabPane} pane
29121          */
29122         "activate" : true
29123          
29124     });
29125 };
29126
29127 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29128     
29129     active : false,
29130     title : '',
29131     
29132     // the tabBox that this is attached to.
29133     tab : false,
29134      
29135     getAutoCreate : function() 
29136     {
29137         var cfg = {
29138             tag: 'div',
29139             cls: 'tab-pane'
29140         };
29141         
29142         if(this.active){
29143             cfg.cls += ' active';
29144         }
29145         
29146         return cfg;
29147     },
29148     initEvents  : function()
29149     {
29150         //Roo.log('trigger add pane handler');
29151         this.parent().fireEvent('addpane', this)
29152     },
29153     
29154      /**
29155      * Updates the tab title 
29156      * @param {String} html to set the title to.
29157      */
29158     setTitle: function(str)
29159     {
29160         if (!this.tab) {
29161             return;
29162         }
29163         this.title = str;
29164         this.tab.select('a', true).first().dom.innerHTML = str;
29165         
29166     }
29167     
29168     
29169     
29170 });
29171
29172  
29173
29174
29175  /*
29176  * - LGPL
29177  *
29178  * menu
29179  * 
29180  */
29181 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29182
29183 /**
29184  * @class Roo.bootstrap.menu.Menu
29185  * @extends Roo.bootstrap.Component
29186  * Bootstrap Menu class - container for Menu
29187  * @cfg {String} html Text of the menu
29188  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29189  * @cfg {String} icon Font awesome icon
29190  * @cfg {String} pos Menu align to (top | bottom) default bottom
29191  * 
29192  * 
29193  * @constructor
29194  * Create a new Menu
29195  * @param {Object} config The config object
29196  */
29197
29198
29199 Roo.bootstrap.menu.Menu = function(config){
29200     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29201     
29202     this.addEvents({
29203         /**
29204          * @event beforeshow
29205          * Fires before this menu is displayed
29206          * @param {Roo.bootstrap.menu.Menu} this
29207          */
29208         beforeshow : true,
29209         /**
29210          * @event beforehide
29211          * Fires before this menu is hidden
29212          * @param {Roo.bootstrap.menu.Menu} this
29213          */
29214         beforehide : true,
29215         /**
29216          * @event show
29217          * Fires after this menu is displayed
29218          * @param {Roo.bootstrap.menu.Menu} this
29219          */
29220         show : true,
29221         /**
29222          * @event hide
29223          * Fires after this menu is hidden
29224          * @param {Roo.bootstrap.menu.Menu} this
29225          */
29226         hide : true,
29227         /**
29228          * @event click
29229          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29230          * @param {Roo.bootstrap.menu.Menu} this
29231          * @param {Roo.EventObject} e
29232          */
29233         click : true
29234     });
29235     
29236 };
29237
29238 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29239     
29240     submenu : false,
29241     html : '',
29242     weight : 'default',
29243     icon : false,
29244     pos : 'bottom',
29245     
29246     
29247     getChildContainer : function() {
29248         if(this.isSubMenu){
29249             return this.el;
29250         }
29251         
29252         return this.el.select('ul.dropdown-menu', true).first();  
29253     },
29254     
29255     getAutoCreate : function()
29256     {
29257         var text = [
29258             {
29259                 tag : 'span',
29260                 cls : 'roo-menu-text',
29261                 html : this.html
29262             }
29263         ];
29264         
29265         if(this.icon){
29266             text.unshift({
29267                 tag : 'i',
29268                 cls : 'fa ' + this.icon
29269             })
29270         }
29271         
29272         
29273         var cfg = {
29274             tag : 'div',
29275             cls : 'btn-group',
29276             cn : [
29277                 {
29278                     tag : 'button',
29279                     cls : 'dropdown-button btn btn-' + this.weight,
29280                     cn : text
29281                 },
29282                 {
29283                     tag : 'button',
29284                     cls : 'dropdown-toggle btn btn-' + this.weight,
29285                     cn : [
29286                         {
29287                             tag : 'span',
29288                             cls : 'caret'
29289                         }
29290                     ]
29291                 },
29292                 {
29293                     tag : 'ul',
29294                     cls : 'dropdown-menu'
29295                 }
29296             ]
29297             
29298         };
29299         
29300         if(this.pos == 'top'){
29301             cfg.cls += ' dropup';
29302         }
29303         
29304         if(this.isSubMenu){
29305             cfg = {
29306                 tag : 'ul',
29307                 cls : 'dropdown-menu'
29308             }
29309         }
29310         
29311         return cfg;
29312     },
29313     
29314     onRender : function(ct, position)
29315     {
29316         this.isSubMenu = ct.hasClass('dropdown-submenu');
29317         
29318         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29319     },
29320     
29321     initEvents : function() 
29322     {
29323         if(this.isSubMenu){
29324             return;
29325         }
29326         
29327         this.hidden = true;
29328         
29329         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29330         this.triggerEl.on('click', this.onTriggerPress, this);
29331         
29332         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29333         this.buttonEl.on('click', this.onClick, this);
29334         
29335     },
29336     
29337     list : function()
29338     {
29339         if(this.isSubMenu){
29340             return this.el;
29341         }
29342         
29343         return this.el.select('ul.dropdown-menu', true).first();
29344     },
29345     
29346     onClick : function(e)
29347     {
29348         this.fireEvent("click", this, e);
29349     },
29350     
29351     onTriggerPress  : function(e)
29352     {   
29353         if (this.isVisible()) {
29354             this.hide();
29355         } else {
29356             this.show();
29357         }
29358     },
29359     
29360     isVisible : function(){
29361         return !this.hidden;
29362     },
29363     
29364     show : function()
29365     {
29366         this.fireEvent("beforeshow", this);
29367         
29368         this.hidden = false;
29369         this.el.addClass('open');
29370         
29371         Roo.get(document).on("mouseup", this.onMouseUp, this);
29372         
29373         this.fireEvent("show", this);
29374         
29375         
29376     },
29377     
29378     hide : function()
29379     {
29380         this.fireEvent("beforehide", this);
29381         
29382         this.hidden = true;
29383         this.el.removeClass('open');
29384         
29385         Roo.get(document).un("mouseup", this.onMouseUp);
29386         
29387         this.fireEvent("hide", this);
29388     },
29389     
29390     onMouseUp : function()
29391     {
29392         this.hide();
29393     }
29394     
29395 });
29396
29397  
29398  /*
29399  * - LGPL
29400  *
29401  * menu item
29402  * 
29403  */
29404 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29405
29406 /**
29407  * @class Roo.bootstrap.menu.Item
29408  * @extends Roo.bootstrap.Component
29409  * Bootstrap MenuItem class
29410  * @cfg {Boolean} submenu (true | false) default false
29411  * @cfg {String} html text of the item
29412  * @cfg {String} href the link
29413  * @cfg {Boolean} disable (true | false) default false
29414  * @cfg {Boolean} preventDefault (true | false) default true
29415  * @cfg {String} icon Font awesome icon
29416  * @cfg {String} pos Submenu align to (left | right) default right 
29417  * 
29418  * 
29419  * @constructor
29420  * Create a new Item
29421  * @param {Object} config The config object
29422  */
29423
29424
29425 Roo.bootstrap.menu.Item = function(config){
29426     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29427     this.addEvents({
29428         /**
29429          * @event mouseover
29430          * Fires when the mouse is hovering over this menu
29431          * @param {Roo.bootstrap.menu.Item} this
29432          * @param {Roo.EventObject} e
29433          */
29434         mouseover : true,
29435         /**
29436          * @event mouseout
29437          * Fires when the mouse exits this menu
29438          * @param {Roo.bootstrap.menu.Item} this
29439          * @param {Roo.EventObject} e
29440          */
29441         mouseout : true,
29442         // raw events
29443         /**
29444          * @event click
29445          * The raw click event for the entire grid.
29446          * @param {Roo.EventObject} e
29447          */
29448         click : true
29449     });
29450 };
29451
29452 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29453     
29454     submenu : false,
29455     href : '',
29456     html : '',
29457     preventDefault: true,
29458     disable : false,
29459     icon : false,
29460     pos : 'right',
29461     
29462     getAutoCreate : function()
29463     {
29464         var text = [
29465             {
29466                 tag : 'span',
29467                 cls : 'roo-menu-item-text',
29468                 html : this.html
29469             }
29470         ];
29471         
29472         if(this.icon){
29473             text.unshift({
29474                 tag : 'i',
29475                 cls : 'fa ' + this.icon
29476             })
29477         }
29478         
29479         var cfg = {
29480             tag : 'li',
29481             cn : [
29482                 {
29483                     tag : 'a',
29484                     href : this.href || '#',
29485                     cn : text
29486                 }
29487             ]
29488         };
29489         
29490         if(this.disable){
29491             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29492         }
29493         
29494         if(this.submenu){
29495             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29496             
29497             if(this.pos == 'left'){
29498                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29499             }
29500         }
29501         
29502         return cfg;
29503     },
29504     
29505     initEvents : function() 
29506     {
29507         this.el.on('mouseover', this.onMouseOver, this);
29508         this.el.on('mouseout', this.onMouseOut, this);
29509         
29510         this.el.select('a', true).first().on('click', this.onClick, this);
29511         
29512     },
29513     
29514     onClick : function(e)
29515     {
29516         if(this.preventDefault){
29517             e.preventDefault();
29518         }
29519         
29520         this.fireEvent("click", this, e);
29521     },
29522     
29523     onMouseOver : function(e)
29524     {
29525         if(this.submenu && this.pos == 'left'){
29526             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29527         }
29528         
29529         this.fireEvent("mouseover", this, e);
29530     },
29531     
29532     onMouseOut : function(e)
29533     {
29534         this.fireEvent("mouseout", this, e);
29535     }
29536 });
29537
29538  
29539
29540  /*
29541  * - LGPL
29542  *
29543  * menu separator
29544  * 
29545  */
29546 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29547
29548 /**
29549  * @class Roo.bootstrap.menu.Separator
29550  * @extends Roo.bootstrap.Component
29551  * Bootstrap Separator class
29552  * 
29553  * @constructor
29554  * Create a new Separator
29555  * @param {Object} config The config object
29556  */
29557
29558
29559 Roo.bootstrap.menu.Separator = function(config){
29560     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29561 };
29562
29563 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29564     
29565     getAutoCreate : function(){
29566         var cfg = {
29567             tag : 'li',
29568             cls: 'dropdown-divider divider'
29569         };
29570         
29571         return cfg;
29572     }
29573    
29574 });
29575
29576  
29577
29578  /*
29579  * - LGPL
29580  *
29581  * Tooltip
29582  * 
29583  */
29584
29585 /**
29586  * @class Roo.bootstrap.Tooltip
29587  * Bootstrap Tooltip class
29588  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29589  * to determine which dom element triggers the tooltip.
29590  * 
29591  * It needs to add support for additional attributes like tooltip-position
29592  * 
29593  * @constructor
29594  * Create a new Toolti
29595  * @param {Object} config The config object
29596  */
29597
29598 Roo.bootstrap.Tooltip = function(config){
29599     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29600     
29601     this.alignment = Roo.bootstrap.Tooltip.alignment;
29602     
29603     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29604         this.alignment = config.alignment;
29605     }
29606     
29607 };
29608
29609 Roo.apply(Roo.bootstrap.Tooltip, {
29610     /**
29611      * @function init initialize tooltip monitoring.
29612      * @static
29613      */
29614     currentEl : false,
29615     currentTip : false,
29616     currentRegion : false,
29617     
29618     //  init : delay?
29619     
29620     init : function()
29621     {
29622         Roo.get(document).on('mouseover', this.enter ,this);
29623         Roo.get(document).on('mouseout', this.leave, this);
29624          
29625         
29626         this.currentTip = new Roo.bootstrap.Tooltip();
29627     },
29628     
29629     enter : function(ev)
29630     {
29631         var dom = ev.getTarget();
29632         
29633         //Roo.log(['enter',dom]);
29634         var el = Roo.fly(dom);
29635         if (this.currentEl) {
29636             //Roo.log(dom);
29637             //Roo.log(this.currentEl);
29638             //Roo.log(this.currentEl.contains(dom));
29639             if (this.currentEl == el) {
29640                 return;
29641             }
29642             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29643                 return;
29644             }
29645
29646         }
29647         
29648         if (this.currentTip.el) {
29649             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29650         }    
29651         //Roo.log(ev);
29652         
29653         if(!el || el.dom == document){
29654             return;
29655         }
29656         
29657         var bindEl = el; 
29658         var pel = false;
29659         if (!el.attr('tooltip')) {
29660             pel = el.findParent("[tooltip]");
29661             if (pel) {
29662                 bindEl = Roo.get(pel);
29663             }
29664         }
29665         
29666        
29667         
29668         // you can not look for children, as if el is the body.. then everythign is the child..
29669         if (!pel && !el.attr('tooltip')) { //
29670             if (!el.select("[tooltip]").elements.length) {
29671                 return;
29672             }
29673             // is the mouse over this child...?
29674             bindEl = el.select("[tooltip]").first();
29675             var xy = ev.getXY();
29676             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29677                 //Roo.log("not in region.");
29678                 return;
29679             }
29680             //Roo.log("child element over..");
29681             
29682         }
29683         this.currentEl = el;
29684         this.currentTip.bind(bindEl);
29685         this.currentRegion = Roo.lib.Region.getRegion(dom);
29686         this.currentTip.enter();
29687         
29688     },
29689     leave : function(ev)
29690     {
29691         var dom = ev.getTarget();
29692         //Roo.log(['leave',dom]);
29693         if (!this.currentEl) {
29694             return;
29695         }
29696         
29697         
29698         if (dom != this.currentEl.dom) {
29699             return;
29700         }
29701         var xy = ev.getXY();
29702         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29703             return;
29704         }
29705         // only activate leave if mouse cursor is outside... bounding box..
29706         
29707         
29708         
29709         
29710         if (this.currentTip) {
29711             this.currentTip.leave();
29712         }
29713         //Roo.log('clear currentEl');
29714         this.currentEl = false;
29715         
29716         
29717     },
29718     alignment : {
29719         'left' : ['r-l', [-2,0], 'right'],
29720         'right' : ['l-r', [2,0], 'left'],
29721         'bottom' : ['t-b', [0,2], 'top'],
29722         'top' : [ 'b-t', [0,-2], 'bottom']
29723     }
29724     
29725 });
29726
29727
29728 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29729     
29730     
29731     bindEl : false,
29732     
29733     delay : null, // can be { show : 300 , hide: 500}
29734     
29735     timeout : null,
29736     
29737     hoverState : null, //???
29738     
29739     placement : 'bottom', 
29740     
29741     alignment : false,
29742     
29743     getAutoCreate : function(){
29744     
29745         var cfg = {
29746            cls : 'tooltip',   
29747            role : 'tooltip',
29748            cn : [
29749                 {
29750                     cls : 'tooltip-arrow arrow'
29751                 },
29752                 {
29753                     cls : 'tooltip-inner'
29754                 }
29755            ]
29756         };
29757         
29758         return cfg;
29759     },
29760     bind : function(el)
29761     {
29762         this.bindEl = el;
29763     },
29764     
29765     initEvents : function()
29766     {
29767         this.arrowEl = this.el.select('.arrow', true).first();
29768         this.innerEl = this.el.select('.tooltip-inner', true).first();
29769     },
29770     
29771     enter : function () {
29772        
29773         if (this.timeout != null) {
29774             clearTimeout(this.timeout);
29775         }
29776         
29777         this.hoverState = 'in';
29778          //Roo.log("enter - show");
29779         if (!this.delay || !this.delay.show) {
29780             this.show();
29781             return;
29782         }
29783         var _t = this;
29784         this.timeout = setTimeout(function () {
29785             if (_t.hoverState == 'in') {
29786                 _t.show();
29787             }
29788         }, this.delay.show);
29789     },
29790     leave : function()
29791     {
29792         clearTimeout(this.timeout);
29793     
29794         this.hoverState = 'out';
29795          if (!this.delay || !this.delay.hide) {
29796             this.hide();
29797             return;
29798         }
29799        
29800         var _t = this;
29801         this.timeout = setTimeout(function () {
29802             //Roo.log("leave - timeout");
29803             
29804             if (_t.hoverState == 'out') {
29805                 _t.hide();
29806                 Roo.bootstrap.Tooltip.currentEl = false;
29807             }
29808         }, delay);
29809     },
29810     
29811     show : function (msg)
29812     {
29813         if (!this.el) {
29814             this.render(document.body);
29815         }
29816         // set content.
29817         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29818         
29819         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29820         
29821         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29822         
29823         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29824                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29825         
29826         var placement = typeof this.placement == 'function' ?
29827             this.placement.call(this, this.el, on_el) :
29828             this.placement;
29829             
29830         var autoToken = /\s?auto?\s?/i;
29831         var autoPlace = autoToken.test(placement);
29832         if (autoPlace) {
29833             placement = placement.replace(autoToken, '') || 'top';
29834         }
29835         
29836         //this.el.detach()
29837         //this.el.setXY([0,0]);
29838         this.el.show();
29839         //this.el.dom.style.display='block';
29840         
29841         //this.el.appendTo(on_el);
29842         
29843         var p = this.getPosition();
29844         var box = this.el.getBox();
29845         
29846         if (autoPlace) {
29847             // fixme..
29848         }
29849         
29850         var align = this.alignment[placement];
29851         
29852         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29853         
29854         if(placement == 'top' || placement == 'bottom'){
29855             if(xy[0] < 0){
29856                 placement = 'right';
29857             }
29858             
29859             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29860                 placement = 'left';
29861             }
29862             
29863             var scroll = Roo.select('body', true).first().getScroll();
29864             
29865             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29866                 placement = 'top';
29867             }
29868             
29869             align = this.alignment[placement];
29870             
29871             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29872             
29873         }
29874         
29875         var elems = document.getElementsByTagName('div');
29876         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29877         for (var i = 0; i < elems.length; i++) {
29878           var zindex = Number.parseInt(
29879                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29880                 10
29881           );
29882           if (zindex > highest) {
29883             highest = zindex;
29884           }
29885         }
29886         
29887         
29888         
29889         this.el.dom.style.zIndex = highest;
29890         
29891         this.el.alignTo(this.bindEl, align[0],align[1]);
29892         //var arrow = this.el.select('.arrow',true).first();
29893         //arrow.set(align[2], 
29894         
29895         this.el.addClass(placement);
29896         this.el.addClass("bs-tooltip-"+ placement);
29897         
29898         this.el.addClass('in fade show');
29899         
29900         this.hoverState = null;
29901         
29902         if (this.el.hasClass('fade')) {
29903             // fade it?
29904         }
29905         
29906         
29907         
29908         
29909         
29910     },
29911     hide : function()
29912     {
29913          
29914         if (!this.el) {
29915             return;
29916         }
29917         //this.el.setXY([0,0]);
29918         this.el.removeClass(['show', 'in']);
29919         //this.el.hide();
29920         
29921     }
29922     
29923 });
29924  
29925
29926  /*
29927  * - LGPL
29928  *
29929  * Location Picker
29930  * 
29931  */
29932
29933 /**
29934  * @class Roo.bootstrap.LocationPicker
29935  * @extends Roo.bootstrap.Component
29936  * Bootstrap LocationPicker class
29937  * @cfg {Number} latitude Position when init default 0
29938  * @cfg {Number} longitude Position when init default 0
29939  * @cfg {Number} zoom default 15
29940  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29941  * @cfg {Boolean} mapTypeControl default false
29942  * @cfg {Boolean} disableDoubleClickZoom default false
29943  * @cfg {Boolean} scrollwheel default true
29944  * @cfg {Boolean} streetViewControl default false
29945  * @cfg {Number} radius default 0
29946  * @cfg {String} locationName
29947  * @cfg {Boolean} draggable default true
29948  * @cfg {Boolean} enableAutocomplete default false
29949  * @cfg {Boolean} enableReverseGeocode default true
29950  * @cfg {String} markerTitle
29951  * 
29952  * @constructor
29953  * Create a new LocationPicker
29954  * @param {Object} config The config object
29955  */
29956
29957
29958 Roo.bootstrap.LocationPicker = function(config){
29959     
29960     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29961     
29962     this.addEvents({
29963         /**
29964          * @event initial
29965          * Fires when the picker initialized.
29966          * @param {Roo.bootstrap.LocationPicker} this
29967          * @param {Google Location} location
29968          */
29969         initial : true,
29970         /**
29971          * @event positionchanged
29972          * Fires when the picker position changed.
29973          * @param {Roo.bootstrap.LocationPicker} this
29974          * @param {Google Location} location
29975          */
29976         positionchanged : true,
29977         /**
29978          * @event resize
29979          * Fires when the map resize.
29980          * @param {Roo.bootstrap.LocationPicker} this
29981          */
29982         resize : true,
29983         /**
29984          * @event show
29985          * Fires when the map show.
29986          * @param {Roo.bootstrap.LocationPicker} this
29987          */
29988         show : true,
29989         /**
29990          * @event hide
29991          * Fires when the map hide.
29992          * @param {Roo.bootstrap.LocationPicker} this
29993          */
29994         hide : true,
29995         /**
29996          * @event mapClick
29997          * Fires when click the map.
29998          * @param {Roo.bootstrap.LocationPicker} this
29999          * @param {Map event} e
30000          */
30001         mapClick : true,
30002         /**
30003          * @event mapRightClick
30004          * Fires when right click the map.
30005          * @param {Roo.bootstrap.LocationPicker} this
30006          * @param {Map event} e
30007          */
30008         mapRightClick : true,
30009         /**
30010          * @event markerClick
30011          * Fires when click the marker.
30012          * @param {Roo.bootstrap.LocationPicker} this
30013          * @param {Map event} e
30014          */
30015         markerClick : true,
30016         /**
30017          * @event markerRightClick
30018          * Fires when right click the marker.
30019          * @param {Roo.bootstrap.LocationPicker} this
30020          * @param {Map event} e
30021          */
30022         markerRightClick : true,
30023         /**
30024          * @event OverlayViewDraw
30025          * Fires when OverlayView Draw
30026          * @param {Roo.bootstrap.LocationPicker} this
30027          */
30028         OverlayViewDraw : true,
30029         /**
30030          * @event OverlayViewOnAdd
30031          * Fires when OverlayView Draw
30032          * @param {Roo.bootstrap.LocationPicker} this
30033          */
30034         OverlayViewOnAdd : true,
30035         /**
30036          * @event OverlayViewOnRemove
30037          * Fires when OverlayView Draw
30038          * @param {Roo.bootstrap.LocationPicker} this
30039          */
30040         OverlayViewOnRemove : true,
30041         /**
30042          * @event OverlayViewShow
30043          * Fires when OverlayView Draw
30044          * @param {Roo.bootstrap.LocationPicker} this
30045          * @param {Pixel} cpx
30046          */
30047         OverlayViewShow : true,
30048         /**
30049          * @event OverlayViewHide
30050          * Fires when OverlayView Draw
30051          * @param {Roo.bootstrap.LocationPicker} this
30052          */
30053         OverlayViewHide : true,
30054         /**
30055          * @event loadexception
30056          * Fires when load google lib failed.
30057          * @param {Roo.bootstrap.LocationPicker} this
30058          */
30059         loadexception : true
30060     });
30061         
30062 };
30063
30064 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30065     
30066     gMapContext: false,
30067     
30068     latitude: 0,
30069     longitude: 0,
30070     zoom: 15,
30071     mapTypeId: false,
30072     mapTypeControl: false,
30073     disableDoubleClickZoom: false,
30074     scrollwheel: true,
30075     streetViewControl: false,
30076     radius: 0,
30077     locationName: '',
30078     draggable: true,
30079     enableAutocomplete: false,
30080     enableReverseGeocode: true,
30081     markerTitle: '',
30082     
30083     getAutoCreate: function()
30084     {
30085
30086         var cfg = {
30087             tag: 'div',
30088             cls: 'roo-location-picker'
30089         };
30090         
30091         return cfg
30092     },
30093     
30094     initEvents: function(ct, position)
30095     {       
30096         if(!this.el.getWidth() || this.isApplied()){
30097             return;
30098         }
30099         
30100         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30101         
30102         this.initial();
30103     },
30104     
30105     initial: function()
30106     {
30107         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30108             this.fireEvent('loadexception', this);
30109             return;
30110         }
30111         
30112         if(!this.mapTypeId){
30113             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30114         }
30115         
30116         this.gMapContext = this.GMapContext();
30117         
30118         this.initOverlayView();
30119         
30120         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30121         
30122         var _this = this;
30123                 
30124         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30125             _this.setPosition(_this.gMapContext.marker.position);
30126         });
30127         
30128         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30129             _this.fireEvent('mapClick', this, event);
30130             
30131         });
30132
30133         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30134             _this.fireEvent('mapRightClick', this, event);
30135             
30136         });
30137         
30138         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30139             _this.fireEvent('markerClick', this, event);
30140             
30141         });
30142
30143         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30144             _this.fireEvent('markerRightClick', this, event);
30145             
30146         });
30147         
30148         this.setPosition(this.gMapContext.location);
30149         
30150         this.fireEvent('initial', this, this.gMapContext.location);
30151     },
30152     
30153     initOverlayView: function()
30154     {
30155         var _this = this;
30156         
30157         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30158             
30159             draw: function()
30160             {
30161                 _this.fireEvent('OverlayViewDraw', _this);
30162             },
30163             
30164             onAdd: function()
30165             {
30166                 _this.fireEvent('OverlayViewOnAdd', _this);
30167             },
30168             
30169             onRemove: function()
30170             {
30171                 _this.fireEvent('OverlayViewOnRemove', _this);
30172             },
30173             
30174             show: function(cpx)
30175             {
30176                 _this.fireEvent('OverlayViewShow', _this, cpx);
30177             },
30178             
30179             hide: function()
30180             {
30181                 _this.fireEvent('OverlayViewHide', _this);
30182             }
30183             
30184         });
30185     },
30186     
30187     fromLatLngToContainerPixel: function(event)
30188     {
30189         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30190     },
30191     
30192     isApplied: function() 
30193     {
30194         return this.getGmapContext() == false ? false : true;
30195     },
30196     
30197     getGmapContext: function() 
30198     {
30199         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30200     },
30201     
30202     GMapContext: function() 
30203     {
30204         var position = new google.maps.LatLng(this.latitude, this.longitude);
30205         
30206         var _map = new google.maps.Map(this.el.dom, {
30207             center: position,
30208             zoom: this.zoom,
30209             mapTypeId: this.mapTypeId,
30210             mapTypeControl: this.mapTypeControl,
30211             disableDoubleClickZoom: this.disableDoubleClickZoom,
30212             scrollwheel: this.scrollwheel,
30213             streetViewControl: this.streetViewControl,
30214             locationName: this.locationName,
30215             draggable: this.draggable,
30216             enableAutocomplete: this.enableAutocomplete,
30217             enableReverseGeocode: this.enableReverseGeocode
30218         });
30219         
30220         var _marker = new google.maps.Marker({
30221             position: position,
30222             map: _map,
30223             title: this.markerTitle,
30224             draggable: this.draggable
30225         });
30226         
30227         return {
30228             map: _map,
30229             marker: _marker,
30230             circle: null,
30231             location: position,
30232             radius: this.radius,
30233             locationName: this.locationName,
30234             addressComponents: {
30235                 formatted_address: null,
30236                 addressLine1: null,
30237                 addressLine2: null,
30238                 streetName: null,
30239                 streetNumber: null,
30240                 city: null,
30241                 district: null,
30242                 state: null,
30243                 stateOrProvince: null
30244             },
30245             settings: this,
30246             domContainer: this.el.dom,
30247             geodecoder: new google.maps.Geocoder()
30248         };
30249     },
30250     
30251     drawCircle: function(center, radius, options) 
30252     {
30253         if (this.gMapContext.circle != null) {
30254             this.gMapContext.circle.setMap(null);
30255         }
30256         if (radius > 0) {
30257             radius *= 1;
30258             options = Roo.apply({}, options, {
30259                 strokeColor: "#0000FF",
30260                 strokeOpacity: .35,
30261                 strokeWeight: 2,
30262                 fillColor: "#0000FF",
30263                 fillOpacity: .2
30264             });
30265             
30266             options.map = this.gMapContext.map;
30267             options.radius = radius;
30268             options.center = center;
30269             this.gMapContext.circle = new google.maps.Circle(options);
30270             return this.gMapContext.circle;
30271         }
30272         
30273         return null;
30274     },
30275     
30276     setPosition: function(location) 
30277     {
30278         this.gMapContext.location = location;
30279         this.gMapContext.marker.setPosition(location);
30280         this.gMapContext.map.panTo(location);
30281         this.drawCircle(location, this.gMapContext.radius, {});
30282         
30283         var _this = this;
30284         
30285         if (this.gMapContext.settings.enableReverseGeocode) {
30286             this.gMapContext.geodecoder.geocode({
30287                 latLng: this.gMapContext.location
30288             }, function(results, status) {
30289                 
30290                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30291                     _this.gMapContext.locationName = results[0].formatted_address;
30292                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30293                     
30294                     _this.fireEvent('positionchanged', this, location);
30295                 }
30296             });
30297             
30298             return;
30299         }
30300         
30301         this.fireEvent('positionchanged', this, location);
30302     },
30303     
30304     resize: function()
30305     {
30306         google.maps.event.trigger(this.gMapContext.map, "resize");
30307         
30308         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30309         
30310         this.fireEvent('resize', this);
30311     },
30312     
30313     setPositionByLatLng: function(latitude, longitude)
30314     {
30315         this.setPosition(new google.maps.LatLng(latitude, longitude));
30316     },
30317     
30318     getCurrentPosition: function() 
30319     {
30320         return {
30321             latitude: this.gMapContext.location.lat(),
30322             longitude: this.gMapContext.location.lng()
30323         };
30324     },
30325     
30326     getAddressName: function() 
30327     {
30328         return this.gMapContext.locationName;
30329     },
30330     
30331     getAddressComponents: function() 
30332     {
30333         return this.gMapContext.addressComponents;
30334     },
30335     
30336     address_component_from_google_geocode: function(address_components) 
30337     {
30338         var result = {};
30339         
30340         for (var i = 0; i < address_components.length; i++) {
30341             var component = address_components[i];
30342             if (component.types.indexOf("postal_code") >= 0) {
30343                 result.postalCode = component.short_name;
30344             } else if (component.types.indexOf("street_number") >= 0) {
30345                 result.streetNumber = component.short_name;
30346             } else if (component.types.indexOf("route") >= 0) {
30347                 result.streetName = component.short_name;
30348             } else if (component.types.indexOf("neighborhood") >= 0) {
30349                 result.city = component.short_name;
30350             } else if (component.types.indexOf("locality") >= 0) {
30351                 result.city = component.short_name;
30352             } else if (component.types.indexOf("sublocality") >= 0) {
30353                 result.district = component.short_name;
30354             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30355                 result.stateOrProvince = component.short_name;
30356             } else if (component.types.indexOf("country") >= 0) {
30357                 result.country = component.short_name;
30358             }
30359         }
30360         
30361         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30362         result.addressLine2 = "";
30363         return result;
30364     },
30365     
30366     setZoomLevel: function(zoom)
30367     {
30368         this.gMapContext.map.setZoom(zoom);
30369     },
30370     
30371     show: function()
30372     {
30373         if(!this.el){
30374             return;
30375         }
30376         
30377         this.el.show();
30378         
30379         this.resize();
30380         
30381         this.fireEvent('show', this);
30382     },
30383     
30384     hide: function()
30385     {
30386         if(!this.el){
30387             return;
30388         }
30389         
30390         this.el.hide();
30391         
30392         this.fireEvent('hide', this);
30393     }
30394     
30395 });
30396
30397 Roo.apply(Roo.bootstrap.LocationPicker, {
30398     
30399     OverlayView : function(map, options)
30400     {
30401         options = options || {};
30402         
30403         this.setMap(map);
30404     }
30405     
30406     
30407 });/**
30408  * @class Roo.bootstrap.Alert
30409  * @extends Roo.bootstrap.Component
30410  * Bootstrap Alert class - shows an alert area box
30411  * eg
30412  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30413   Enter a valid email address
30414 </div>
30415  * @licence LGPL
30416  * @cfg {String} title The title of alert
30417  * @cfg {String} html The content of alert
30418  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30419  * @cfg {String} fa font-awesomeicon
30420  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30421  * @cfg {Boolean} close true to show a x closer
30422  * 
30423  * 
30424  * @constructor
30425  * Create a new alert
30426  * @param {Object} config The config object
30427  */
30428
30429
30430 Roo.bootstrap.Alert = function(config){
30431     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30432     
30433 };
30434
30435 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30436     
30437     title: '',
30438     html: '',
30439     weight: false,
30440     fa: false,
30441     faicon: false, // BC
30442     close : false,
30443     
30444     
30445     getAutoCreate : function()
30446     {
30447         
30448         var cfg = {
30449             tag : 'div',
30450             cls : 'alert',
30451             cn : [
30452                 {
30453                     tag: 'button',
30454                     type :  "button",
30455                     cls: "close",
30456                     html : '×',
30457                     style : this.close ? '' : 'display:none'
30458                 },
30459                 {
30460                     tag : 'i',
30461                     cls : 'roo-alert-icon'
30462                     
30463                 },
30464                 {
30465                     tag : 'b',
30466                     cls : 'roo-alert-title',
30467                     html : this.title
30468                 },
30469                 {
30470                     tag : 'span',
30471                     cls : 'roo-alert-text',
30472                     html : this.html
30473                 }
30474             ]
30475         };
30476         
30477         if(this.faicon){
30478             cfg.cn[0].cls += ' fa ' + this.faicon;
30479         }
30480         if(this.fa){
30481             cfg.cn[0].cls += ' fa ' + this.fa;
30482         }
30483         
30484         if(this.weight){
30485             cfg.cls += ' alert-' + this.weight;
30486         }
30487         
30488         return cfg;
30489     },
30490     
30491     initEvents: function() 
30492     {
30493         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30494         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30495         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30496         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30497         if (this.seconds > 0) {
30498             this.hide.defer(this.seconds, this);
30499         }
30500     },
30501     /**
30502      * Set the Title Message HTML
30503      * @param {String} html
30504      */
30505     setTitle : function(str)
30506     {
30507         this.titleEl.dom.innerHTML = str;
30508     },
30509      
30510      /**
30511      * Set the Body Message HTML
30512      * @param {String} html
30513      */
30514     setHtml : function(str)
30515     {
30516         this.htmlEl.dom.innerHTML = str;
30517     },
30518     /**
30519      * Set the Weight of the alert
30520      * @param {String} (success|info|warning|danger) weight
30521      */
30522     
30523     setWeight : function(weight)
30524     {
30525         if(this.weight){
30526             this.el.removeClass('alert-' + this.weight);
30527         }
30528         
30529         this.weight = weight;
30530         
30531         this.el.addClass('alert-' + this.weight);
30532     },
30533       /**
30534      * Set the Icon of the alert
30535      * @param {String} see fontawsome names (name without the 'fa-' bit)
30536      */
30537     setIcon : function(icon)
30538     {
30539         if(this.faicon){
30540             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30541         }
30542         
30543         this.faicon = icon;
30544         
30545         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30546     },
30547     /**
30548      * Hide the Alert
30549      */
30550     hide: function() 
30551     {
30552         this.el.hide();   
30553     },
30554     /**
30555      * Show the Alert
30556      */
30557     show: function() 
30558     {  
30559         this.el.show();   
30560     }
30561     
30562 });
30563
30564  
30565 /*
30566 * Licence: LGPL
30567 */
30568
30569 /**
30570  * @class Roo.bootstrap.UploadCropbox
30571  * @extends Roo.bootstrap.Component
30572  * Bootstrap UploadCropbox class
30573  * @cfg {String} emptyText show when image has been loaded
30574  * @cfg {String} rotateNotify show when image too small to rotate
30575  * @cfg {Number} errorTimeout default 3000
30576  * @cfg {Number} minWidth default 300
30577  * @cfg {Number} minHeight default 300
30578  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30579  * @cfg {Boolean} isDocument (true|false) default false
30580  * @cfg {String} url action url
30581  * @cfg {String} paramName default 'imageUpload'
30582  * @cfg {String} method default POST
30583  * @cfg {Boolean} loadMask (true|false) default true
30584  * @cfg {Boolean} loadingText default 'Loading...'
30585  * 
30586  * @constructor
30587  * Create a new UploadCropbox
30588  * @param {Object} config The config object
30589  */
30590
30591 Roo.bootstrap.UploadCropbox = function(config){
30592     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30593     
30594     this.addEvents({
30595         /**
30596          * @event beforeselectfile
30597          * Fire before select file
30598          * @param {Roo.bootstrap.UploadCropbox} this
30599          */
30600         "beforeselectfile" : true,
30601         /**
30602          * @event initial
30603          * Fire after initEvent
30604          * @param {Roo.bootstrap.UploadCropbox} this
30605          */
30606         "initial" : true,
30607         /**
30608          * @event crop
30609          * Fire after initEvent
30610          * @param {Roo.bootstrap.UploadCropbox} this
30611          * @param {String} data
30612          */
30613         "crop" : true,
30614         /**
30615          * @event prepare
30616          * Fire when preparing the file data
30617          * @param {Roo.bootstrap.UploadCropbox} this
30618          * @param {Object} file
30619          */
30620         "prepare" : true,
30621         /**
30622          * @event exception
30623          * Fire when get exception
30624          * @param {Roo.bootstrap.UploadCropbox} this
30625          * @param {XMLHttpRequest} xhr
30626          */
30627         "exception" : true,
30628         /**
30629          * @event beforeloadcanvas
30630          * Fire before load the canvas
30631          * @param {Roo.bootstrap.UploadCropbox} this
30632          * @param {String} src
30633          */
30634         "beforeloadcanvas" : true,
30635         /**
30636          * @event trash
30637          * Fire when trash image
30638          * @param {Roo.bootstrap.UploadCropbox} this
30639          */
30640         "trash" : true,
30641         /**
30642          * @event download
30643          * Fire when download the image
30644          * @param {Roo.bootstrap.UploadCropbox} this
30645          */
30646         "download" : true,
30647         /**
30648          * @event footerbuttonclick
30649          * Fire when footerbuttonclick
30650          * @param {Roo.bootstrap.UploadCropbox} this
30651          * @param {String} type
30652          */
30653         "footerbuttonclick" : true,
30654         /**
30655          * @event resize
30656          * Fire when resize
30657          * @param {Roo.bootstrap.UploadCropbox} this
30658          */
30659         "resize" : true,
30660         /**
30661          * @event rotate
30662          * Fire when rotate the image
30663          * @param {Roo.bootstrap.UploadCropbox} this
30664          * @param {String} pos
30665          */
30666         "rotate" : true,
30667         /**
30668          * @event inspect
30669          * Fire when inspect the file
30670          * @param {Roo.bootstrap.UploadCropbox} this
30671          * @param {Object} file
30672          */
30673         "inspect" : true,
30674         /**
30675          * @event upload
30676          * Fire when xhr upload the file
30677          * @param {Roo.bootstrap.UploadCropbox} this
30678          * @param {Object} data
30679          */
30680         "upload" : true,
30681         /**
30682          * @event arrange
30683          * Fire when arrange the file data
30684          * @param {Roo.bootstrap.UploadCropbox} this
30685          * @param {Object} formData
30686          */
30687         "arrange" : true
30688     });
30689     
30690     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30691 };
30692
30693 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30694     
30695     emptyText : 'Click to upload image',
30696     rotateNotify : 'Image is too small to rotate',
30697     errorTimeout : 3000,
30698     scale : 0,
30699     baseScale : 1,
30700     rotate : 0,
30701     dragable : false,
30702     pinching : false,
30703     mouseX : 0,
30704     mouseY : 0,
30705     cropData : false,
30706     minWidth : 300,
30707     minHeight : 300,
30708     file : false,
30709     exif : {},
30710     baseRotate : 1,
30711     cropType : 'image/jpeg',
30712     buttons : false,
30713     canvasLoaded : false,
30714     isDocument : false,
30715     method : 'POST',
30716     paramName : 'imageUpload',
30717     loadMask : true,
30718     loadingText : 'Loading...',
30719     maskEl : false,
30720     
30721     getAutoCreate : function()
30722     {
30723         var cfg = {
30724             tag : 'div',
30725             cls : 'roo-upload-cropbox',
30726             cn : [
30727                 {
30728                     tag : 'input',
30729                     cls : 'roo-upload-cropbox-selector',
30730                     type : 'file'
30731                 },
30732                 {
30733                     tag : 'div',
30734                     cls : 'roo-upload-cropbox-body',
30735                     style : 'cursor:pointer',
30736                     cn : [
30737                         {
30738                             tag : 'div',
30739                             cls : 'roo-upload-cropbox-preview'
30740                         },
30741                         {
30742                             tag : 'div',
30743                             cls : 'roo-upload-cropbox-thumb'
30744                         },
30745                         {
30746                             tag : 'div',
30747                             cls : 'roo-upload-cropbox-empty-notify',
30748                             html : this.emptyText
30749                         },
30750                         {
30751                             tag : 'div',
30752                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30753                             html : this.rotateNotify
30754                         }
30755                     ]
30756                 },
30757                 {
30758                     tag : 'div',
30759                     cls : 'roo-upload-cropbox-footer',
30760                     cn : {
30761                         tag : 'div',
30762                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30763                         cn : []
30764                     }
30765                 }
30766             ]
30767         };
30768         
30769         return cfg;
30770     },
30771     
30772     onRender : function(ct, position)
30773     {
30774         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30775         
30776         if (this.buttons.length) {
30777             
30778             Roo.each(this.buttons, function(bb) {
30779                 
30780                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30781                 
30782                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30783                 
30784             }, this);
30785         }
30786         
30787         if(this.loadMask){
30788             this.maskEl = this.el;
30789         }
30790     },
30791     
30792     initEvents : function()
30793     {
30794         this.urlAPI = (window.createObjectURL && window) || 
30795                                 (window.URL && URL.revokeObjectURL && URL) || 
30796                                 (window.webkitURL && webkitURL);
30797                         
30798         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30799         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30800         
30801         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30802         this.selectorEl.hide();
30803         
30804         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30805         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30806         
30807         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30808         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30809         this.thumbEl.hide();
30810         
30811         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30812         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30813         
30814         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30815         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30816         this.errorEl.hide();
30817         
30818         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30819         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30820         this.footerEl.hide();
30821         
30822         this.setThumbBoxSize();
30823         
30824         this.bind();
30825         
30826         this.resize();
30827         
30828         this.fireEvent('initial', this);
30829     },
30830
30831     bind : function()
30832     {
30833         var _this = this;
30834         
30835         window.addEventListener("resize", function() { _this.resize(); } );
30836         
30837         this.bodyEl.on('click', this.beforeSelectFile, this);
30838         
30839         if(Roo.isTouch){
30840             this.bodyEl.on('touchstart', this.onTouchStart, this);
30841             this.bodyEl.on('touchmove', this.onTouchMove, this);
30842             this.bodyEl.on('touchend', this.onTouchEnd, this);
30843         }
30844         
30845         if(!Roo.isTouch){
30846             this.bodyEl.on('mousedown', this.onMouseDown, this);
30847             this.bodyEl.on('mousemove', this.onMouseMove, this);
30848             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30849             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30850             Roo.get(document).on('mouseup', this.onMouseUp, this);
30851         }
30852         
30853         this.selectorEl.on('change', this.onFileSelected, this);
30854     },
30855     
30856     reset : function()
30857     {    
30858         this.scale = 0;
30859         this.baseScale = 1;
30860         this.rotate = 0;
30861         this.baseRotate = 1;
30862         this.dragable = false;
30863         this.pinching = false;
30864         this.mouseX = 0;
30865         this.mouseY = 0;
30866         this.cropData = false;
30867         this.notifyEl.dom.innerHTML = this.emptyText;
30868         
30869         this.selectorEl.dom.value = '';
30870         
30871     },
30872     
30873     resize : function()
30874     {
30875         if(this.fireEvent('resize', this) != false){
30876             this.setThumbBoxPosition();
30877             this.setCanvasPosition();
30878         }
30879     },
30880     
30881     onFooterButtonClick : function(e, el, o, type)
30882     {
30883         switch (type) {
30884             case 'rotate-left' :
30885                 this.onRotateLeft(e);
30886                 break;
30887             case 'rotate-right' :
30888                 this.onRotateRight(e);
30889                 break;
30890             case 'picture' :
30891                 this.beforeSelectFile(e);
30892                 break;
30893             case 'trash' :
30894                 this.trash(e);
30895                 break;
30896             case 'crop' :
30897                 this.crop(e);
30898                 break;
30899             case 'download' :
30900                 this.download(e);
30901                 break;
30902             default :
30903                 break;
30904         }
30905         
30906         this.fireEvent('footerbuttonclick', this, type);
30907     },
30908     
30909     beforeSelectFile : function(e)
30910     {
30911         e.preventDefault();
30912         
30913         if(this.fireEvent('beforeselectfile', this) != false){
30914             this.selectorEl.dom.click();
30915         }
30916     },
30917     
30918     onFileSelected : function(e)
30919     {
30920         e.preventDefault();
30921         
30922         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30923             return;
30924         }
30925         
30926         var file = this.selectorEl.dom.files[0];
30927         
30928         if(this.fireEvent('inspect', this, file) != false){
30929             this.prepare(file);
30930         }
30931         
30932     },
30933     
30934     trash : function(e)
30935     {
30936         this.fireEvent('trash', this);
30937     },
30938     
30939     download : function(e)
30940     {
30941         this.fireEvent('download', this);
30942     },
30943     
30944     loadCanvas : function(src)
30945     {   
30946         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30947             
30948             this.reset();
30949             
30950             this.imageEl = document.createElement('img');
30951             
30952             var _this = this;
30953             
30954             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30955             
30956             this.imageEl.src = src;
30957         }
30958     },
30959     
30960     onLoadCanvas : function()
30961     {   
30962         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30963         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30964         
30965         this.bodyEl.un('click', this.beforeSelectFile, this);
30966         
30967         this.notifyEl.hide();
30968         this.thumbEl.show();
30969         this.footerEl.show();
30970         
30971         this.baseRotateLevel();
30972         
30973         if(this.isDocument){
30974             this.setThumbBoxSize();
30975         }
30976         
30977         this.setThumbBoxPosition();
30978         
30979         this.baseScaleLevel();
30980         
30981         this.draw();
30982         
30983         this.resize();
30984         
30985         this.canvasLoaded = true;
30986         
30987         if(this.loadMask){
30988             this.maskEl.unmask();
30989         }
30990         
30991     },
30992     
30993     setCanvasPosition : function()
30994     {   
30995         if(!this.canvasEl){
30996             return;
30997         }
30998         
30999         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31000         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31001         
31002         this.previewEl.setLeft(pw);
31003         this.previewEl.setTop(ph);
31004         
31005     },
31006     
31007     onMouseDown : function(e)
31008     {   
31009         e.stopEvent();
31010         
31011         this.dragable = true;
31012         this.pinching = false;
31013         
31014         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31015             this.dragable = false;
31016             return;
31017         }
31018         
31019         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31020         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31021         
31022     },
31023     
31024     onMouseMove : function(e)
31025     {   
31026         e.stopEvent();
31027         
31028         if(!this.canvasLoaded){
31029             return;
31030         }
31031         
31032         if (!this.dragable){
31033             return;
31034         }
31035         
31036         var minX = Math.ceil(this.thumbEl.getLeft(true));
31037         var minY = Math.ceil(this.thumbEl.getTop(true));
31038         
31039         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31040         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31041         
31042         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31043         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31044         
31045         x = x - this.mouseX;
31046         y = y - this.mouseY;
31047         
31048         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31049         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31050         
31051         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31052         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31053         
31054         this.previewEl.setLeft(bgX);
31055         this.previewEl.setTop(bgY);
31056         
31057         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31058         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31059     },
31060     
31061     onMouseUp : function(e)
31062     {   
31063         e.stopEvent();
31064         
31065         this.dragable = false;
31066     },
31067     
31068     onMouseWheel : function(e)
31069     {   
31070         e.stopEvent();
31071         
31072         this.startScale = this.scale;
31073         
31074         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31075         
31076         if(!this.zoomable()){
31077             this.scale = this.startScale;
31078             return;
31079         }
31080         
31081         this.draw();
31082         
31083         return;
31084     },
31085     
31086     zoomable : function()
31087     {
31088         var minScale = this.thumbEl.getWidth() / this.minWidth;
31089         
31090         if(this.minWidth < this.minHeight){
31091             minScale = this.thumbEl.getHeight() / this.minHeight;
31092         }
31093         
31094         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31095         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31096         
31097         if(
31098                 this.isDocument &&
31099                 (this.rotate == 0 || this.rotate == 180) && 
31100                 (
31101                     width > this.imageEl.OriginWidth || 
31102                     height > this.imageEl.OriginHeight ||
31103                     (width < this.minWidth && height < this.minHeight)
31104                 )
31105         ){
31106             return false;
31107         }
31108         
31109         if(
31110                 this.isDocument &&
31111                 (this.rotate == 90 || this.rotate == 270) && 
31112                 (
31113                     width > this.imageEl.OriginWidth || 
31114                     height > this.imageEl.OriginHeight ||
31115                     (width < this.minHeight && height < this.minWidth)
31116                 )
31117         ){
31118             return false;
31119         }
31120         
31121         if(
31122                 !this.isDocument &&
31123                 (this.rotate == 0 || this.rotate == 180) && 
31124                 (
31125                     width < this.minWidth || 
31126                     width > this.imageEl.OriginWidth || 
31127                     height < this.minHeight || 
31128                     height > this.imageEl.OriginHeight
31129                 )
31130         ){
31131             return false;
31132         }
31133         
31134         if(
31135                 !this.isDocument &&
31136                 (this.rotate == 90 || this.rotate == 270) && 
31137                 (
31138                     width < this.minHeight || 
31139                     width > this.imageEl.OriginWidth || 
31140                     height < this.minWidth || 
31141                     height > this.imageEl.OriginHeight
31142                 )
31143         ){
31144             return false;
31145         }
31146         
31147         return true;
31148         
31149     },
31150     
31151     onRotateLeft : function(e)
31152     {   
31153         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31154             
31155             var minScale = this.thumbEl.getWidth() / this.minWidth;
31156             
31157             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31158             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31159             
31160             this.startScale = this.scale;
31161             
31162             while (this.getScaleLevel() < minScale){
31163             
31164                 this.scale = this.scale + 1;
31165                 
31166                 if(!this.zoomable()){
31167                     break;
31168                 }
31169                 
31170                 if(
31171                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31172                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31173                 ){
31174                     continue;
31175                 }
31176                 
31177                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31178
31179                 this.draw();
31180                 
31181                 return;
31182             }
31183             
31184             this.scale = this.startScale;
31185             
31186             this.onRotateFail();
31187             
31188             return false;
31189         }
31190         
31191         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31192
31193         if(this.isDocument){
31194             this.setThumbBoxSize();
31195             this.setThumbBoxPosition();
31196             this.setCanvasPosition();
31197         }
31198         
31199         this.draw();
31200         
31201         this.fireEvent('rotate', this, 'left');
31202         
31203     },
31204     
31205     onRotateRight : function(e)
31206     {
31207         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31208             
31209             var minScale = this.thumbEl.getWidth() / this.minWidth;
31210         
31211             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31212             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31213             
31214             this.startScale = this.scale;
31215             
31216             while (this.getScaleLevel() < minScale){
31217             
31218                 this.scale = this.scale + 1;
31219                 
31220                 if(!this.zoomable()){
31221                     break;
31222                 }
31223                 
31224                 if(
31225                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31226                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31227                 ){
31228                     continue;
31229                 }
31230                 
31231                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31232
31233                 this.draw();
31234                 
31235                 return;
31236             }
31237             
31238             this.scale = this.startScale;
31239             
31240             this.onRotateFail();
31241             
31242             return false;
31243         }
31244         
31245         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31246
31247         if(this.isDocument){
31248             this.setThumbBoxSize();
31249             this.setThumbBoxPosition();
31250             this.setCanvasPosition();
31251         }
31252         
31253         this.draw();
31254         
31255         this.fireEvent('rotate', this, 'right');
31256     },
31257     
31258     onRotateFail : function()
31259     {
31260         this.errorEl.show(true);
31261         
31262         var _this = this;
31263         
31264         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31265     },
31266     
31267     draw : function()
31268     {
31269         this.previewEl.dom.innerHTML = '';
31270         
31271         var canvasEl = document.createElement("canvas");
31272         
31273         var contextEl = canvasEl.getContext("2d");
31274         
31275         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31276         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31277         var center = this.imageEl.OriginWidth / 2;
31278         
31279         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31280             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31281             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31282             center = this.imageEl.OriginHeight / 2;
31283         }
31284         
31285         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31286         
31287         contextEl.translate(center, center);
31288         contextEl.rotate(this.rotate * Math.PI / 180);
31289
31290         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31291         
31292         this.canvasEl = document.createElement("canvas");
31293         
31294         this.contextEl = this.canvasEl.getContext("2d");
31295         
31296         switch (this.rotate) {
31297             case 0 :
31298                 
31299                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31300                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31301                 
31302                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31303                 
31304                 break;
31305             case 90 : 
31306                 
31307                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31308                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31309                 
31310                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31311                     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);
31312                     break;
31313                 }
31314                 
31315                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31316                 
31317                 break;
31318             case 180 :
31319                 
31320                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31321                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31322                 
31323                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31324                     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);
31325                     break;
31326                 }
31327                 
31328                 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);
31329                 
31330                 break;
31331             case 270 :
31332                 
31333                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31334                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31335         
31336                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31337                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31338                     break;
31339                 }
31340                 
31341                 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);
31342                 
31343                 break;
31344             default : 
31345                 break;
31346         }
31347         
31348         this.previewEl.appendChild(this.canvasEl);
31349         
31350         this.setCanvasPosition();
31351     },
31352     
31353     crop : function()
31354     {
31355         if(!this.canvasLoaded){
31356             return;
31357         }
31358         
31359         var imageCanvas = document.createElement("canvas");
31360         
31361         var imageContext = imageCanvas.getContext("2d");
31362         
31363         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31364         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31365         
31366         var center = imageCanvas.width / 2;
31367         
31368         imageContext.translate(center, center);
31369         
31370         imageContext.rotate(this.rotate * Math.PI / 180);
31371         
31372         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31373         
31374         var canvas = document.createElement("canvas");
31375         
31376         var context = canvas.getContext("2d");
31377                 
31378         canvas.width = this.minWidth;
31379         canvas.height = this.minHeight;
31380
31381         switch (this.rotate) {
31382             case 0 :
31383                 
31384                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31385                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31386                 
31387                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31388                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31389                 
31390                 var targetWidth = this.minWidth - 2 * x;
31391                 var targetHeight = this.minHeight - 2 * y;
31392                 
31393                 var scale = 1;
31394                 
31395                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31396                     scale = targetWidth / width;
31397                 }
31398                 
31399                 if(x > 0 && y == 0){
31400                     scale = targetHeight / height;
31401                 }
31402                 
31403                 if(x > 0 && y > 0){
31404                     scale = targetWidth / width;
31405                     
31406                     if(width < height){
31407                         scale = targetHeight / height;
31408                     }
31409                 }
31410                 
31411                 context.scale(scale, scale);
31412                 
31413                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31414                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31415
31416                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31417                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31418
31419                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31420                 
31421                 break;
31422             case 90 : 
31423                 
31424                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31425                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31426                 
31427                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31428                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31429                 
31430                 var targetWidth = this.minWidth - 2 * x;
31431                 var targetHeight = this.minHeight - 2 * y;
31432                 
31433                 var scale = 1;
31434                 
31435                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31436                     scale = targetWidth / width;
31437                 }
31438                 
31439                 if(x > 0 && y == 0){
31440                     scale = targetHeight / height;
31441                 }
31442                 
31443                 if(x > 0 && y > 0){
31444                     scale = targetWidth / width;
31445                     
31446                     if(width < height){
31447                         scale = targetHeight / height;
31448                     }
31449                 }
31450                 
31451                 context.scale(scale, scale);
31452                 
31453                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31454                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31455
31456                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31457                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31458                 
31459                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31460                 
31461                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31462                 
31463                 break;
31464             case 180 :
31465                 
31466                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31467                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31468                 
31469                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31470                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31471                 
31472                 var targetWidth = this.minWidth - 2 * x;
31473                 var targetHeight = this.minHeight - 2 * y;
31474                 
31475                 var scale = 1;
31476                 
31477                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31478                     scale = targetWidth / width;
31479                 }
31480                 
31481                 if(x > 0 && y == 0){
31482                     scale = targetHeight / height;
31483                 }
31484                 
31485                 if(x > 0 && y > 0){
31486                     scale = targetWidth / width;
31487                     
31488                     if(width < height){
31489                         scale = targetHeight / height;
31490                     }
31491                 }
31492                 
31493                 context.scale(scale, scale);
31494                 
31495                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31496                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31497
31498                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31499                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31500
31501                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31502                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31503                 
31504                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31505                 
31506                 break;
31507             case 270 :
31508                 
31509                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31510                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31511                 
31512                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31513                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31514                 
31515                 var targetWidth = this.minWidth - 2 * x;
31516                 var targetHeight = this.minHeight - 2 * y;
31517                 
31518                 var scale = 1;
31519                 
31520                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31521                     scale = targetWidth / width;
31522                 }
31523                 
31524                 if(x > 0 && y == 0){
31525                     scale = targetHeight / height;
31526                 }
31527                 
31528                 if(x > 0 && y > 0){
31529                     scale = targetWidth / width;
31530                     
31531                     if(width < height){
31532                         scale = targetHeight / height;
31533                     }
31534                 }
31535                 
31536                 context.scale(scale, scale);
31537                 
31538                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31539                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31540
31541                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31542                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31543                 
31544                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31545                 
31546                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31547                 
31548                 break;
31549             default : 
31550                 break;
31551         }
31552         
31553         this.cropData = canvas.toDataURL(this.cropType);
31554         
31555         if(this.fireEvent('crop', this, this.cropData) !== false){
31556             this.process(this.file, this.cropData);
31557         }
31558         
31559         return;
31560         
31561     },
31562     
31563     setThumbBoxSize : function()
31564     {
31565         var width, height;
31566         
31567         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31568             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31569             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31570             
31571             this.minWidth = width;
31572             this.minHeight = height;
31573             
31574             if(this.rotate == 90 || this.rotate == 270){
31575                 this.minWidth = height;
31576                 this.minHeight = width;
31577             }
31578         }
31579         
31580         height = 300;
31581         width = Math.ceil(this.minWidth * height / this.minHeight);
31582         
31583         if(this.minWidth > this.minHeight){
31584             width = 300;
31585             height = Math.ceil(this.minHeight * width / this.minWidth);
31586         }
31587         
31588         this.thumbEl.setStyle({
31589             width : width + 'px',
31590             height : height + 'px'
31591         });
31592
31593         return;
31594             
31595     },
31596     
31597     setThumbBoxPosition : function()
31598     {
31599         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31600         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31601         
31602         this.thumbEl.setLeft(x);
31603         this.thumbEl.setTop(y);
31604         
31605     },
31606     
31607     baseRotateLevel : function()
31608     {
31609         this.baseRotate = 1;
31610         
31611         if(
31612                 typeof(this.exif) != 'undefined' &&
31613                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31614                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31615         ){
31616             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31617         }
31618         
31619         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31620         
31621     },
31622     
31623     baseScaleLevel : function()
31624     {
31625         var width, height;
31626         
31627         if(this.isDocument){
31628             
31629             if(this.baseRotate == 6 || this.baseRotate == 8){
31630             
31631                 height = this.thumbEl.getHeight();
31632                 this.baseScale = height / this.imageEl.OriginWidth;
31633
31634                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31635                     width = this.thumbEl.getWidth();
31636                     this.baseScale = width / this.imageEl.OriginHeight;
31637                 }
31638
31639                 return;
31640             }
31641
31642             height = this.thumbEl.getHeight();
31643             this.baseScale = height / this.imageEl.OriginHeight;
31644
31645             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31646                 width = this.thumbEl.getWidth();
31647                 this.baseScale = width / this.imageEl.OriginWidth;
31648             }
31649
31650             return;
31651         }
31652         
31653         if(this.baseRotate == 6 || this.baseRotate == 8){
31654             
31655             width = this.thumbEl.getHeight();
31656             this.baseScale = width / this.imageEl.OriginHeight;
31657             
31658             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31659                 height = this.thumbEl.getWidth();
31660                 this.baseScale = height / this.imageEl.OriginHeight;
31661             }
31662             
31663             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31664                 height = this.thumbEl.getWidth();
31665                 this.baseScale = height / this.imageEl.OriginHeight;
31666                 
31667                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31668                     width = this.thumbEl.getHeight();
31669                     this.baseScale = width / this.imageEl.OriginWidth;
31670                 }
31671             }
31672             
31673             return;
31674         }
31675         
31676         width = this.thumbEl.getWidth();
31677         this.baseScale = width / this.imageEl.OriginWidth;
31678         
31679         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31680             height = this.thumbEl.getHeight();
31681             this.baseScale = height / this.imageEl.OriginHeight;
31682         }
31683         
31684         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31685             
31686             height = this.thumbEl.getHeight();
31687             this.baseScale = height / this.imageEl.OriginHeight;
31688             
31689             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31690                 width = this.thumbEl.getWidth();
31691                 this.baseScale = width / this.imageEl.OriginWidth;
31692             }
31693             
31694         }
31695         
31696         return;
31697     },
31698     
31699     getScaleLevel : function()
31700     {
31701         return this.baseScale * Math.pow(1.1, this.scale);
31702     },
31703     
31704     onTouchStart : function(e)
31705     {
31706         if(!this.canvasLoaded){
31707             this.beforeSelectFile(e);
31708             return;
31709         }
31710         
31711         var touches = e.browserEvent.touches;
31712         
31713         if(!touches){
31714             return;
31715         }
31716         
31717         if(touches.length == 1){
31718             this.onMouseDown(e);
31719             return;
31720         }
31721         
31722         if(touches.length != 2){
31723             return;
31724         }
31725         
31726         var coords = [];
31727         
31728         for(var i = 0, finger; finger = touches[i]; i++){
31729             coords.push(finger.pageX, finger.pageY);
31730         }
31731         
31732         var x = Math.pow(coords[0] - coords[2], 2);
31733         var y = Math.pow(coords[1] - coords[3], 2);
31734         
31735         this.startDistance = Math.sqrt(x + y);
31736         
31737         this.startScale = this.scale;
31738         
31739         this.pinching = true;
31740         this.dragable = false;
31741         
31742     },
31743     
31744     onTouchMove : function(e)
31745     {
31746         if(!this.pinching && !this.dragable){
31747             return;
31748         }
31749         
31750         var touches = e.browserEvent.touches;
31751         
31752         if(!touches){
31753             return;
31754         }
31755         
31756         if(this.dragable){
31757             this.onMouseMove(e);
31758             return;
31759         }
31760         
31761         var coords = [];
31762         
31763         for(var i = 0, finger; finger = touches[i]; i++){
31764             coords.push(finger.pageX, finger.pageY);
31765         }
31766         
31767         var x = Math.pow(coords[0] - coords[2], 2);
31768         var y = Math.pow(coords[1] - coords[3], 2);
31769         
31770         this.endDistance = Math.sqrt(x + y);
31771         
31772         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31773         
31774         if(!this.zoomable()){
31775             this.scale = this.startScale;
31776             return;
31777         }
31778         
31779         this.draw();
31780         
31781     },
31782     
31783     onTouchEnd : function(e)
31784     {
31785         this.pinching = false;
31786         this.dragable = false;
31787         
31788     },
31789     
31790     process : function(file, crop)
31791     {
31792         if(this.loadMask){
31793             this.maskEl.mask(this.loadingText);
31794         }
31795         
31796         this.xhr = new XMLHttpRequest();
31797         
31798         file.xhr = this.xhr;
31799
31800         this.xhr.open(this.method, this.url, true);
31801         
31802         var headers = {
31803             "Accept": "application/json",
31804             "Cache-Control": "no-cache",
31805             "X-Requested-With": "XMLHttpRequest"
31806         };
31807         
31808         for (var headerName in headers) {
31809             var headerValue = headers[headerName];
31810             if (headerValue) {
31811                 this.xhr.setRequestHeader(headerName, headerValue);
31812             }
31813         }
31814         
31815         var _this = this;
31816         
31817         this.xhr.onload = function()
31818         {
31819             _this.xhrOnLoad(_this.xhr);
31820         }
31821         
31822         this.xhr.onerror = function()
31823         {
31824             _this.xhrOnError(_this.xhr);
31825         }
31826         
31827         var formData = new FormData();
31828
31829         formData.append('returnHTML', 'NO');
31830         
31831         if(crop){
31832             formData.append('crop', crop);
31833         }
31834         
31835         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31836             formData.append(this.paramName, file, file.name);
31837         }
31838         
31839         if(typeof(file.filename) != 'undefined'){
31840             formData.append('filename', file.filename);
31841         }
31842         
31843         if(typeof(file.mimetype) != 'undefined'){
31844             formData.append('mimetype', file.mimetype);
31845         }
31846         
31847         if(this.fireEvent('arrange', this, formData) != false){
31848             this.xhr.send(formData);
31849         };
31850     },
31851     
31852     xhrOnLoad : function(xhr)
31853     {
31854         if(this.loadMask){
31855             this.maskEl.unmask();
31856         }
31857         
31858         if (xhr.readyState !== 4) {
31859             this.fireEvent('exception', this, xhr);
31860             return;
31861         }
31862
31863         var response = Roo.decode(xhr.responseText);
31864         
31865         if(!response.success){
31866             this.fireEvent('exception', this, xhr);
31867             return;
31868         }
31869         
31870         var response = Roo.decode(xhr.responseText);
31871         
31872         this.fireEvent('upload', this, response);
31873         
31874     },
31875     
31876     xhrOnError : function()
31877     {
31878         if(this.loadMask){
31879             this.maskEl.unmask();
31880         }
31881         
31882         Roo.log('xhr on error');
31883         
31884         var response = Roo.decode(xhr.responseText);
31885           
31886         Roo.log(response);
31887         
31888     },
31889     
31890     prepare : function(file)
31891     {   
31892         if(this.loadMask){
31893             this.maskEl.mask(this.loadingText);
31894         }
31895         
31896         this.file = false;
31897         this.exif = {};
31898         
31899         if(typeof(file) === 'string'){
31900             this.loadCanvas(file);
31901             return;
31902         }
31903         
31904         if(!file || !this.urlAPI){
31905             return;
31906         }
31907         
31908         this.file = file;
31909         this.cropType = file.type;
31910         
31911         var _this = this;
31912         
31913         if(this.fireEvent('prepare', this, this.file) != false){
31914             
31915             var reader = new FileReader();
31916             
31917             reader.onload = function (e) {
31918                 if (e.target.error) {
31919                     Roo.log(e.target.error);
31920                     return;
31921                 }
31922                 
31923                 var buffer = e.target.result,
31924                     dataView = new DataView(buffer),
31925                     offset = 2,
31926                     maxOffset = dataView.byteLength - 4,
31927                     markerBytes,
31928                     markerLength;
31929                 
31930                 if (dataView.getUint16(0) === 0xffd8) {
31931                     while (offset < maxOffset) {
31932                         markerBytes = dataView.getUint16(offset);
31933                         
31934                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31935                             markerLength = dataView.getUint16(offset + 2) + 2;
31936                             if (offset + markerLength > dataView.byteLength) {
31937                                 Roo.log('Invalid meta data: Invalid segment size.');
31938                                 break;
31939                             }
31940                             
31941                             if(markerBytes == 0xffe1){
31942                                 _this.parseExifData(
31943                                     dataView,
31944                                     offset,
31945                                     markerLength
31946                                 );
31947                             }
31948                             
31949                             offset += markerLength;
31950                             
31951                             continue;
31952                         }
31953                         
31954                         break;
31955                     }
31956                     
31957                 }
31958                 
31959                 var url = _this.urlAPI.createObjectURL(_this.file);
31960                 
31961                 _this.loadCanvas(url);
31962                 
31963                 return;
31964             }
31965             
31966             reader.readAsArrayBuffer(this.file);
31967             
31968         }
31969         
31970     },
31971     
31972     parseExifData : function(dataView, offset, length)
31973     {
31974         var tiffOffset = offset + 10,
31975             littleEndian,
31976             dirOffset;
31977     
31978         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31979             // No Exif data, might be XMP data instead
31980             return;
31981         }
31982         
31983         // Check for the ASCII code for "Exif" (0x45786966):
31984         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31985             // No Exif data, might be XMP data instead
31986             return;
31987         }
31988         if (tiffOffset + 8 > dataView.byteLength) {
31989             Roo.log('Invalid Exif data: Invalid segment size.');
31990             return;
31991         }
31992         // Check for the two null bytes:
31993         if (dataView.getUint16(offset + 8) !== 0x0000) {
31994             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31995             return;
31996         }
31997         // Check the byte alignment:
31998         switch (dataView.getUint16(tiffOffset)) {
31999         case 0x4949:
32000             littleEndian = true;
32001             break;
32002         case 0x4D4D:
32003             littleEndian = false;
32004             break;
32005         default:
32006             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32007             return;
32008         }
32009         // Check for the TIFF tag marker (0x002A):
32010         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32011             Roo.log('Invalid Exif data: Missing TIFF marker.');
32012             return;
32013         }
32014         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32015         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32016         
32017         this.parseExifTags(
32018             dataView,
32019             tiffOffset,
32020             tiffOffset + dirOffset,
32021             littleEndian
32022         );
32023     },
32024     
32025     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32026     {
32027         var tagsNumber,
32028             dirEndOffset,
32029             i;
32030         if (dirOffset + 6 > dataView.byteLength) {
32031             Roo.log('Invalid Exif data: Invalid directory offset.');
32032             return;
32033         }
32034         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32035         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32036         if (dirEndOffset + 4 > dataView.byteLength) {
32037             Roo.log('Invalid Exif data: Invalid directory size.');
32038             return;
32039         }
32040         for (i = 0; i < tagsNumber; i += 1) {
32041             this.parseExifTag(
32042                 dataView,
32043                 tiffOffset,
32044                 dirOffset + 2 + 12 * i, // tag offset
32045                 littleEndian
32046             );
32047         }
32048         // Return the offset to the next directory:
32049         return dataView.getUint32(dirEndOffset, littleEndian);
32050     },
32051     
32052     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32053     {
32054         var tag = dataView.getUint16(offset, littleEndian);
32055         
32056         this.exif[tag] = this.getExifValue(
32057             dataView,
32058             tiffOffset,
32059             offset,
32060             dataView.getUint16(offset + 2, littleEndian), // tag type
32061             dataView.getUint32(offset + 4, littleEndian), // tag length
32062             littleEndian
32063         );
32064     },
32065     
32066     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32067     {
32068         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32069             tagSize,
32070             dataOffset,
32071             values,
32072             i,
32073             str,
32074             c;
32075     
32076         if (!tagType) {
32077             Roo.log('Invalid Exif data: Invalid tag type.');
32078             return;
32079         }
32080         
32081         tagSize = tagType.size * length;
32082         // Determine if the value is contained in the dataOffset bytes,
32083         // or if the value at the dataOffset is a pointer to the actual data:
32084         dataOffset = tagSize > 4 ?
32085                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32086         if (dataOffset + tagSize > dataView.byteLength) {
32087             Roo.log('Invalid Exif data: Invalid data offset.');
32088             return;
32089         }
32090         if (length === 1) {
32091             return tagType.getValue(dataView, dataOffset, littleEndian);
32092         }
32093         values = [];
32094         for (i = 0; i < length; i += 1) {
32095             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32096         }
32097         
32098         if (tagType.ascii) {
32099             str = '';
32100             // Concatenate the chars:
32101             for (i = 0; i < values.length; i += 1) {
32102                 c = values[i];
32103                 // Ignore the terminating NULL byte(s):
32104                 if (c === '\u0000') {
32105                     break;
32106                 }
32107                 str += c;
32108             }
32109             return str;
32110         }
32111         return values;
32112     }
32113     
32114 });
32115
32116 Roo.apply(Roo.bootstrap.UploadCropbox, {
32117     tags : {
32118         'Orientation': 0x0112
32119     },
32120     
32121     Orientation: {
32122             1: 0, //'top-left',
32123 //            2: 'top-right',
32124             3: 180, //'bottom-right',
32125 //            4: 'bottom-left',
32126 //            5: 'left-top',
32127             6: 90, //'right-top',
32128 //            7: 'right-bottom',
32129             8: 270 //'left-bottom'
32130     },
32131     
32132     exifTagTypes : {
32133         // byte, 8-bit unsigned int:
32134         1: {
32135             getValue: function (dataView, dataOffset) {
32136                 return dataView.getUint8(dataOffset);
32137             },
32138             size: 1
32139         },
32140         // ascii, 8-bit byte:
32141         2: {
32142             getValue: function (dataView, dataOffset) {
32143                 return String.fromCharCode(dataView.getUint8(dataOffset));
32144             },
32145             size: 1,
32146             ascii: true
32147         },
32148         // short, 16 bit int:
32149         3: {
32150             getValue: function (dataView, dataOffset, littleEndian) {
32151                 return dataView.getUint16(dataOffset, littleEndian);
32152             },
32153             size: 2
32154         },
32155         // long, 32 bit int:
32156         4: {
32157             getValue: function (dataView, dataOffset, littleEndian) {
32158                 return dataView.getUint32(dataOffset, littleEndian);
32159             },
32160             size: 4
32161         },
32162         // rational = two long values, first is numerator, second is denominator:
32163         5: {
32164             getValue: function (dataView, dataOffset, littleEndian) {
32165                 return dataView.getUint32(dataOffset, littleEndian) /
32166                     dataView.getUint32(dataOffset + 4, littleEndian);
32167             },
32168             size: 8
32169         },
32170         // slong, 32 bit signed int:
32171         9: {
32172             getValue: function (dataView, dataOffset, littleEndian) {
32173                 return dataView.getInt32(dataOffset, littleEndian);
32174             },
32175             size: 4
32176         },
32177         // srational, two slongs, first is numerator, second is denominator:
32178         10: {
32179             getValue: function (dataView, dataOffset, littleEndian) {
32180                 return dataView.getInt32(dataOffset, littleEndian) /
32181                     dataView.getInt32(dataOffset + 4, littleEndian);
32182             },
32183             size: 8
32184         }
32185     },
32186     
32187     footer : {
32188         STANDARD : [
32189             {
32190                 tag : 'div',
32191                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32192                 action : 'rotate-left',
32193                 cn : [
32194                     {
32195                         tag : 'button',
32196                         cls : 'btn btn-default',
32197                         html : '<i class="fa fa-undo"></i>'
32198                     }
32199                 ]
32200             },
32201             {
32202                 tag : 'div',
32203                 cls : 'btn-group roo-upload-cropbox-picture',
32204                 action : 'picture',
32205                 cn : [
32206                     {
32207                         tag : 'button',
32208                         cls : 'btn btn-default',
32209                         html : '<i class="fa fa-picture-o"></i>'
32210                     }
32211                 ]
32212             },
32213             {
32214                 tag : 'div',
32215                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32216                 action : 'rotate-right',
32217                 cn : [
32218                     {
32219                         tag : 'button',
32220                         cls : 'btn btn-default',
32221                         html : '<i class="fa fa-repeat"></i>'
32222                     }
32223                 ]
32224             }
32225         ],
32226         DOCUMENT : [
32227             {
32228                 tag : 'div',
32229                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32230                 action : 'rotate-left',
32231                 cn : [
32232                     {
32233                         tag : 'button',
32234                         cls : 'btn btn-default',
32235                         html : '<i class="fa fa-undo"></i>'
32236                     }
32237                 ]
32238             },
32239             {
32240                 tag : 'div',
32241                 cls : 'btn-group roo-upload-cropbox-download',
32242                 action : 'download',
32243                 cn : [
32244                     {
32245                         tag : 'button',
32246                         cls : 'btn btn-default',
32247                         html : '<i class="fa fa-download"></i>'
32248                     }
32249                 ]
32250             },
32251             {
32252                 tag : 'div',
32253                 cls : 'btn-group roo-upload-cropbox-crop',
32254                 action : 'crop',
32255                 cn : [
32256                     {
32257                         tag : 'button',
32258                         cls : 'btn btn-default',
32259                         html : '<i class="fa fa-crop"></i>'
32260                     }
32261                 ]
32262             },
32263             {
32264                 tag : 'div',
32265                 cls : 'btn-group roo-upload-cropbox-trash',
32266                 action : 'trash',
32267                 cn : [
32268                     {
32269                         tag : 'button',
32270                         cls : 'btn btn-default',
32271                         html : '<i class="fa fa-trash"></i>'
32272                     }
32273                 ]
32274             },
32275             {
32276                 tag : 'div',
32277                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32278                 action : 'rotate-right',
32279                 cn : [
32280                     {
32281                         tag : 'button',
32282                         cls : 'btn btn-default',
32283                         html : '<i class="fa fa-repeat"></i>'
32284                     }
32285                 ]
32286             }
32287         ],
32288         ROTATOR : [
32289             {
32290                 tag : 'div',
32291                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32292                 action : 'rotate-left',
32293                 cn : [
32294                     {
32295                         tag : 'button',
32296                         cls : 'btn btn-default',
32297                         html : '<i class="fa fa-undo"></i>'
32298                     }
32299                 ]
32300             },
32301             {
32302                 tag : 'div',
32303                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32304                 action : 'rotate-right',
32305                 cn : [
32306                     {
32307                         tag : 'button',
32308                         cls : 'btn btn-default',
32309                         html : '<i class="fa fa-repeat"></i>'
32310                     }
32311                 ]
32312             }
32313         ]
32314     }
32315 });
32316
32317 /*
32318 * Licence: LGPL
32319 */
32320
32321 /**
32322  * @class Roo.bootstrap.DocumentManager
32323  * @extends Roo.bootstrap.Component
32324  * Bootstrap DocumentManager class
32325  * @cfg {String} paramName default 'imageUpload'
32326  * @cfg {String} toolTipName default 'filename'
32327  * @cfg {String} method default POST
32328  * @cfg {String} url action url
32329  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32330  * @cfg {Boolean} multiple multiple upload default true
32331  * @cfg {Number} thumbSize default 300
32332  * @cfg {String} fieldLabel
32333  * @cfg {Number} labelWidth default 4
32334  * @cfg {String} labelAlign (left|top) default left
32335  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32336 * @cfg {Number} labellg set the width of label (1-12)
32337  * @cfg {Number} labelmd set the width of label (1-12)
32338  * @cfg {Number} labelsm set the width of label (1-12)
32339  * @cfg {Number} labelxs set the width of label (1-12)
32340  * 
32341  * @constructor
32342  * Create a new DocumentManager
32343  * @param {Object} config The config object
32344  */
32345
32346 Roo.bootstrap.DocumentManager = function(config){
32347     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32348     
32349     this.files = [];
32350     this.delegates = [];
32351     
32352     this.addEvents({
32353         /**
32354          * @event initial
32355          * Fire when initial the DocumentManager
32356          * @param {Roo.bootstrap.DocumentManager} this
32357          */
32358         "initial" : true,
32359         /**
32360          * @event inspect
32361          * inspect selected file
32362          * @param {Roo.bootstrap.DocumentManager} this
32363          * @param {File} file
32364          */
32365         "inspect" : true,
32366         /**
32367          * @event exception
32368          * Fire when xhr load exception
32369          * @param {Roo.bootstrap.DocumentManager} this
32370          * @param {XMLHttpRequest} xhr
32371          */
32372         "exception" : true,
32373         /**
32374          * @event afterupload
32375          * Fire when xhr load exception
32376          * @param {Roo.bootstrap.DocumentManager} this
32377          * @param {XMLHttpRequest} xhr
32378          */
32379         "afterupload" : true,
32380         /**
32381          * @event prepare
32382          * prepare the form data
32383          * @param {Roo.bootstrap.DocumentManager} this
32384          * @param {Object} formData
32385          */
32386         "prepare" : true,
32387         /**
32388          * @event remove
32389          * Fire when remove the file
32390          * @param {Roo.bootstrap.DocumentManager} this
32391          * @param {Object} file
32392          */
32393         "remove" : true,
32394         /**
32395          * @event refresh
32396          * Fire after refresh the file
32397          * @param {Roo.bootstrap.DocumentManager} this
32398          */
32399         "refresh" : true,
32400         /**
32401          * @event click
32402          * Fire after click the image
32403          * @param {Roo.bootstrap.DocumentManager} this
32404          * @param {Object} file
32405          */
32406         "click" : true,
32407         /**
32408          * @event edit
32409          * Fire when upload a image and editable set to true
32410          * @param {Roo.bootstrap.DocumentManager} this
32411          * @param {Object} file
32412          */
32413         "edit" : true,
32414         /**
32415          * @event beforeselectfile
32416          * Fire before select file
32417          * @param {Roo.bootstrap.DocumentManager} this
32418          */
32419         "beforeselectfile" : true,
32420         /**
32421          * @event process
32422          * Fire before process file
32423          * @param {Roo.bootstrap.DocumentManager} this
32424          * @param {Object} file
32425          */
32426         "process" : true,
32427         /**
32428          * @event previewrendered
32429          * Fire when preview rendered
32430          * @param {Roo.bootstrap.DocumentManager} this
32431          * @param {Object} file
32432          */
32433         "previewrendered" : true,
32434         /**
32435          */
32436         "previewResize" : true
32437         
32438     });
32439 };
32440
32441 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32442     
32443     boxes : 0,
32444     inputName : '',
32445     thumbSize : 300,
32446     multiple : true,
32447     files : false,
32448     method : 'POST',
32449     url : '',
32450     paramName : 'imageUpload',
32451     toolTipName : 'filename',
32452     fieldLabel : '',
32453     labelWidth : 4,
32454     labelAlign : 'left',
32455     editable : true,
32456     delegates : false,
32457     xhr : false, 
32458     
32459     labellg : 0,
32460     labelmd : 0,
32461     labelsm : 0,
32462     labelxs : 0,
32463     
32464     getAutoCreate : function()
32465     {   
32466         var managerWidget = {
32467             tag : 'div',
32468             cls : 'roo-document-manager',
32469             cn : [
32470                 {
32471                     tag : 'input',
32472                     cls : 'roo-document-manager-selector',
32473                     type : 'file'
32474                 },
32475                 {
32476                     tag : 'div',
32477                     cls : 'roo-document-manager-uploader',
32478                     cn : [
32479                         {
32480                             tag : 'div',
32481                             cls : 'roo-document-manager-upload-btn',
32482                             html : '<i class="fa fa-plus"></i>'
32483                         }
32484                     ]
32485                     
32486                 }
32487             ]
32488         };
32489         
32490         var content = [
32491             {
32492                 tag : 'div',
32493                 cls : 'column col-md-12',
32494                 cn : managerWidget
32495             }
32496         ];
32497         
32498         if(this.fieldLabel.length){
32499             
32500             content = [
32501                 {
32502                     tag : 'div',
32503                     cls : 'column col-md-12',
32504                     html : this.fieldLabel
32505                 },
32506                 {
32507                     tag : 'div',
32508                     cls : 'column col-md-12',
32509                     cn : managerWidget
32510                 }
32511             ];
32512
32513             if(this.labelAlign == 'left'){
32514                 content = [
32515                     {
32516                         tag : 'div',
32517                         cls : 'column',
32518                         html : this.fieldLabel
32519                     },
32520                     {
32521                         tag : 'div',
32522                         cls : 'column',
32523                         cn : managerWidget
32524                     }
32525                 ];
32526                 
32527                 if(this.labelWidth > 12){
32528                     content[0].style = "width: " + this.labelWidth + 'px';
32529                 }
32530
32531                 if(this.labelWidth < 13 && this.labelmd == 0){
32532                     this.labelmd = this.labelWidth;
32533                 }
32534
32535                 if(this.labellg > 0){
32536                     content[0].cls += ' col-lg-' + this.labellg;
32537                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32538                 }
32539
32540                 if(this.labelmd > 0){
32541                     content[0].cls += ' col-md-' + this.labelmd;
32542                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32543                 }
32544
32545                 if(this.labelsm > 0){
32546                     content[0].cls += ' col-sm-' + this.labelsm;
32547                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32548                 }
32549
32550                 if(this.labelxs > 0){
32551                     content[0].cls += ' col-xs-' + this.labelxs;
32552                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32553                 }
32554                 
32555             }
32556         }
32557         
32558         var cfg = {
32559             tag : 'div',
32560             cls : 'row clearfix',
32561             cn : content
32562         };
32563         
32564         return cfg;
32565         
32566     },
32567     
32568     initEvents : function()
32569     {
32570         this.managerEl = this.el.select('.roo-document-manager', true).first();
32571         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32572         
32573         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32574         this.selectorEl.hide();
32575         
32576         if(this.multiple){
32577             this.selectorEl.attr('multiple', 'multiple');
32578         }
32579         
32580         this.selectorEl.on('change', this.onFileSelected, this);
32581         
32582         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32583         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32584         
32585         this.uploader.on('click', this.onUploaderClick, this);
32586         
32587         this.renderProgressDialog();
32588         
32589         var _this = this;
32590         
32591         window.addEventListener("resize", function() { _this.refresh(); } );
32592         
32593         this.fireEvent('initial', this);
32594     },
32595     
32596     renderProgressDialog : function()
32597     {
32598         var _this = this;
32599         
32600         this.progressDialog = new Roo.bootstrap.Modal({
32601             cls : 'roo-document-manager-progress-dialog',
32602             allow_close : false,
32603             animate : false,
32604             title : '',
32605             buttons : [
32606                 {
32607                     name  :'cancel',
32608                     weight : 'danger',
32609                     html : 'Cancel'
32610                 }
32611             ], 
32612             listeners : { 
32613                 btnclick : function() {
32614                     _this.uploadCancel();
32615                     this.hide();
32616                 }
32617             }
32618         });
32619          
32620         this.progressDialog.render(Roo.get(document.body));
32621          
32622         this.progress = new Roo.bootstrap.Progress({
32623             cls : 'roo-document-manager-progress',
32624             active : true,
32625             striped : true
32626         });
32627         
32628         this.progress.render(this.progressDialog.getChildContainer());
32629         
32630         this.progressBar = new Roo.bootstrap.ProgressBar({
32631             cls : 'roo-document-manager-progress-bar',
32632             aria_valuenow : 0,
32633             aria_valuemin : 0,
32634             aria_valuemax : 12,
32635             panel : 'success'
32636         });
32637         
32638         this.progressBar.render(this.progress.getChildContainer());
32639     },
32640     
32641     onUploaderClick : function(e)
32642     {
32643         e.preventDefault();
32644      
32645         if(this.fireEvent('beforeselectfile', this) != false){
32646             this.selectorEl.dom.click();
32647         }
32648         
32649     },
32650     
32651     onFileSelected : function(e)
32652     {
32653         e.preventDefault();
32654         
32655         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32656             return;
32657         }
32658         
32659         Roo.each(this.selectorEl.dom.files, function(file){
32660             if(this.fireEvent('inspect', this, file) != false){
32661                 this.files.push(file);
32662             }
32663         }, this);
32664         
32665         this.queue();
32666         
32667     },
32668     
32669     queue : function()
32670     {
32671         this.selectorEl.dom.value = '';
32672         
32673         if(!this.files || !this.files.length){
32674             return;
32675         }
32676         
32677         if(this.boxes > 0 && this.files.length > this.boxes){
32678             this.files = this.files.slice(0, this.boxes);
32679         }
32680         
32681         this.uploader.show();
32682         
32683         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32684             this.uploader.hide();
32685         }
32686         
32687         var _this = this;
32688         
32689         var files = [];
32690         
32691         var docs = [];
32692         
32693         Roo.each(this.files, function(file){
32694             
32695             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32696                 var f = this.renderPreview(file);
32697                 files.push(f);
32698                 return;
32699             }
32700             
32701             if(file.type.indexOf('image') != -1){
32702                 this.delegates.push(
32703                     (function(){
32704                         _this.process(file);
32705                     }).createDelegate(this)
32706                 );
32707         
32708                 return;
32709             }
32710             
32711             docs.push(
32712                 (function(){
32713                     _this.process(file);
32714                 }).createDelegate(this)
32715             );
32716             
32717         }, this);
32718         
32719         this.files = files;
32720         
32721         this.delegates = this.delegates.concat(docs);
32722         
32723         if(!this.delegates.length){
32724             this.refresh();
32725             return;
32726         }
32727         
32728         this.progressBar.aria_valuemax = this.delegates.length;
32729         
32730         this.arrange();
32731         
32732         return;
32733     },
32734     
32735     arrange : function()
32736     {
32737         if(!this.delegates.length){
32738             this.progressDialog.hide();
32739             this.refresh();
32740             return;
32741         }
32742         
32743         var delegate = this.delegates.shift();
32744         
32745         this.progressDialog.show();
32746         
32747         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32748         
32749         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32750         
32751         delegate();
32752     },
32753     
32754     refresh : function()
32755     {
32756         this.uploader.show();
32757         
32758         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32759             this.uploader.hide();
32760         }
32761         
32762         Roo.isTouch ? this.closable(false) : this.closable(true);
32763         
32764         this.fireEvent('refresh', this);
32765     },
32766     
32767     onRemove : function(e, el, o)
32768     {
32769         e.preventDefault();
32770         
32771         this.fireEvent('remove', this, o);
32772         
32773     },
32774     
32775     remove : function(o)
32776     {
32777         var files = [];
32778         
32779         Roo.each(this.files, function(file){
32780             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32781                 files.push(file);
32782                 return;
32783             }
32784
32785             o.target.remove();
32786
32787         }, this);
32788         
32789         this.files = files;
32790         
32791         this.refresh();
32792     },
32793     
32794     clear : function()
32795     {
32796         Roo.each(this.files, function(file){
32797             if(!file.target){
32798                 return;
32799             }
32800             
32801             file.target.remove();
32802
32803         }, this);
32804         
32805         this.files = [];
32806         
32807         this.refresh();
32808     },
32809     
32810     onClick : function(e, el, o)
32811     {
32812         e.preventDefault();
32813         
32814         this.fireEvent('click', this, o);
32815         
32816     },
32817     
32818     closable : function(closable)
32819     {
32820         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32821             
32822             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32823             
32824             if(closable){
32825                 el.show();
32826                 return;
32827             }
32828             
32829             el.hide();
32830             
32831         }, this);
32832     },
32833     
32834     xhrOnLoad : function(xhr)
32835     {
32836         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32837             el.remove();
32838         }, this);
32839         
32840         if (xhr.readyState !== 4) {
32841             this.arrange();
32842             this.fireEvent('exception', this, xhr);
32843             return;
32844         }
32845
32846         var response = Roo.decode(xhr.responseText);
32847         
32848         if(!response.success){
32849             this.arrange();
32850             this.fireEvent('exception', this, xhr);
32851             return;
32852         }
32853         
32854         var file = this.renderPreview(response.data);
32855         
32856         this.files.push(file);
32857         
32858         this.arrange();
32859         
32860         this.fireEvent('afterupload', this, xhr);
32861         
32862     },
32863     
32864     xhrOnError : function(xhr)
32865     {
32866         Roo.log('xhr on error');
32867         
32868         var response = Roo.decode(xhr.responseText);
32869           
32870         Roo.log(response);
32871         
32872         this.arrange();
32873     },
32874     
32875     process : function(file)
32876     {
32877         if(this.fireEvent('process', this, file) !== false){
32878             if(this.editable && file.type.indexOf('image') != -1){
32879                 this.fireEvent('edit', this, file);
32880                 return;
32881             }
32882
32883             this.uploadStart(file, false);
32884
32885             return;
32886         }
32887         
32888     },
32889     
32890     uploadStart : function(file, crop)
32891     {
32892         this.xhr = new XMLHttpRequest();
32893         
32894         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32895             this.arrange();
32896             return;
32897         }
32898         
32899         file.xhr = this.xhr;
32900             
32901         this.managerEl.createChild({
32902             tag : 'div',
32903             cls : 'roo-document-manager-loading',
32904             cn : [
32905                 {
32906                     tag : 'div',
32907                     tooltip : file.name,
32908                     cls : 'roo-document-manager-thumb',
32909                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32910                 }
32911             ]
32912
32913         });
32914
32915         this.xhr.open(this.method, this.url, true);
32916         
32917         var headers = {
32918             "Accept": "application/json",
32919             "Cache-Control": "no-cache",
32920             "X-Requested-With": "XMLHttpRequest"
32921         };
32922         
32923         for (var headerName in headers) {
32924             var headerValue = headers[headerName];
32925             if (headerValue) {
32926                 this.xhr.setRequestHeader(headerName, headerValue);
32927             }
32928         }
32929         
32930         var _this = this;
32931         
32932         this.xhr.onload = function()
32933         {
32934             _this.xhrOnLoad(_this.xhr);
32935         }
32936         
32937         this.xhr.onerror = function()
32938         {
32939             _this.xhrOnError(_this.xhr);
32940         }
32941         
32942         var formData = new FormData();
32943
32944         formData.append('returnHTML', 'NO');
32945         
32946         if(crop){
32947             formData.append('crop', crop);
32948         }
32949         
32950         formData.append(this.paramName, file, file.name);
32951         
32952         var options = {
32953             file : file, 
32954             manually : false
32955         };
32956         
32957         if(this.fireEvent('prepare', this, formData, options) != false){
32958             
32959             if(options.manually){
32960                 return;
32961             }
32962             
32963             this.xhr.send(formData);
32964             return;
32965         };
32966         
32967         this.uploadCancel();
32968     },
32969     
32970     uploadCancel : function()
32971     {
32972         if (this.xhr) {
32973             this.xhr.abort();
32974         }
32975         
32976         this.delegates = [];
32977         
32978         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32979             el.remove();
32980         }, this);
32981         
32982         this.arrange();
32983     },
32984     
32985     renderPreview : function(file)
32986     {
32987         if(typeof(file.target) != 'undefined' && file.target){
32988             return file;
32989         }
32990         
32991         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32992         
32993         var previewEl = this.managerEl.createChild({
32994             tag : 'div',
32995             cls : 'roo-document-manager-preview',
32996             cn : [
32997                 {
32998                     tag : 'div',
32999                     tooltip : file[this.toolTipName],
33000                     cls : 'roo-document-manager-thumb',
33001                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33002                 },
33003                 {
33004                     tag : 'button',
33005                     cls : 'close',
33006                     html : '<i class="fa fa-times-circle"></i>'
33007                 }
33008             ]
33009         });
33010
33011         var close = previewEl.select('button.close', true).first();
33012
33013         close.on('click', this.onRemove, this, file);
33014
33015         file.target = previewEl;
33016
33017         var image = previewEl.select('img', true).first();
33018         
33019         var _this = this;
33020         
33021         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33022         
33023         image.on('click', this.onClick, this, file);
33024         
33025         this.fireEvent('previewrendered', this, file);
33026         
33027         return file;
33028         
33029     },
33030     
33031     onPreviewLoad : function(file, image)
33032     {
33033         if(typeof(file.target) == 'undefined' || !file.target){
33034             return;
33035         }
33036         
33037         var width = image.dom.naturalWidth || image.dom.width;
33038         var height = image.dom.naturalHeight || image.dom.height;
33039         
33040         if(!this.previewResize) {
33041             return;
33042         }
33043         
33044         if(width > height){
33045             file.target.addClass('wide');
33046             return;
33047         }
33048         
33049         file.target.addClass('tall');
33050         return;
33051         
33052     },
33053     
33054     uploadFromSource : function(file, crop)
33055     {
33056         this.xhr = new XMLHttpRequest();
33057         
33058         this.managerEl.createChild({
33059             tag : 'div',
33060             cls : 'roo-document-manager-loading',
33061             cn : [
33062                 {
33063                     tag : 'div',
33064                     tooltip : file.name,
33065                     cls : 'roo-document-manager-thumb',
33066                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33067                 }
33068             ]
33069
33070         });
33071
33072         this.xhr.open(this.method, this.url, true);
33073         
33074         var headers = {
33075             "Accept": "application/json",
33076             "Cache-Control": "no-cache",
33077             "X-Requested-With": "XMLHttpRequest"
33078         };
33079         
33080         for (var headerName in headers) {
33081             var headerValue = headers[headerName];
33082             if (headerValue) {
33083                 this.xhr.setRequestHeader(headerName, headerValue);
33084             }
33085         }
33086         
33087         var _this = this;
33088         
33089         this.xhr.onload = function()
33090         {
33091             _this.xhrOnLoad(_this.xhr);
33092         }
33093         
33094         this.xhr.onerror = function()
33095         {
33096             _this.xhrOnError(_this.xhr);
33097         }
33098         
33099         var formData = new FormData();
33100
33101         formData.append('returnHTML', 'NO');
33102         
33103         formData.append('crop', crop);
33104         
33105         if(typeof(file.filename) != 'undefined'){
33106             formData.append('filename', file.filename);
33107         }
33108         
33109         if(typeof(file.mimetype) != 'undefined'){
33110             formData.append('mimetype', file.mimetype);
33111         }
33112         
33113         Roo.log(formData);
33114         
33115         if(this.fireEvent('prepare', this, formData) != false){
33116             this.xhr.send(formData);
33117         };
33118     }
33119 });
33120
33121 /*
33122 * Licence: LGPL
33123 */
33124
33125 /**
33126  * @class Roo.bootstrap.DocumentViewer
33127  * @extends Roo.bootstrap.Component
33128  * Bootstrap DocumentViewer class
33129  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33130  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33131  * 
33132  * @constructor
33133  * Create a new DocumentViewer
33134  * @param {Object} config The config object
33135  */
33136
33137 Roo.bootstrap.DocumentViewer = function(config){
33138     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33139     
33140     this.addEvents({
33141         /**
33142          * @event initial
33143          * Fire after initEvent
33144          * @param {Roo.bootstrap.DocumentViewer} this
33145          */
33146         "initial" : true,
33147         /**
33148          * @event click
33149          * Fire after click
33150          * @param {Roo.bootstrap.DocumentViewer} this
33151          */
33152         "click" : true,
33153         /**
33154          * @event download
33155          * Fire after download button
33156          * @param {Roo.bootstrap.DocumentViewer} this
33157          */
33158         "download" : true,
33159         /**
33160          * @event trash
33161          * Fire after trash button
33162          * @param {Roo.bootstrap.DocumentViewer} this
33163          */
33164         "trash" : true
33165         
33166     });
33167 };
33168
33169 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33170     
33171     showDownload : true,
33172     
33173     showTrash : true,
33174     
33175     getAutoCreate : function()
33176     {
33177         var cfg = {
33178             tag : 'div',
33179             cls : 'roo-document-viewer',
33180             cn : [
33181                 {
33182                     tag : 'div',
33183                     cls : 'roo-document-viewer-body',
33184                     cn : [
33185                         {
33186                             tag : 'div',
33187                             cls : 'roo-document-viewer-thumb',
33188                             cn : [
33189                                 {
33190                                     tag : 'img',
33191                                     cls : 'roo-document-viewer-image'
33192                                 }
33193                             ]
33194                         }
33195                     ]
33196                 },
33197                 {
33198                     tag : 'div',
33199                     cls : 'roo-document-viewer-footer',
33200                     cn : {
33201                         tag : 'div',
33202                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33203                         cn : [
33204                             {
33205                                 tag : 'div',
33206                                 cls : 'btn-group roo-document-viewer-download',
33207                                 cn : [
33208                                     {
33209                                         tag : 'button',
33210                                         cls : 'btn btn-default',
33211                                         html : '<i class="fa fa-download"></i>'
33212                                     }
33213                                 ]
33214                             },
33215                             {
33216                                 tag : 'div',
33217                                 cls : 'btn-group roo-document-viewer-trash',
33218                                 cn : [
33219                                     {
33220                                         tag : 'button',
33221                                         cls : 'btn btn-default',
33222                                         html : '<i class="fa fa-trash"></i>'
33223                                     }
33224                                 ]
33225                             }
33226                         ]
33227                     }
33228                 }
33229             ]
33230         };
33231         
33232         return cfg;
33233     },
33234     
33235     initEvents : function()
33236     {
33237         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33238         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33239         
33240         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33241         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33242         
33243         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33244         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33245         
33246         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33247         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33248         
33249         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33250         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33251         
33252         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33253         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33254         
33255         this.bodyEl.on('click', this.onClick, this);
33256         this.downloadBtn.on('click', this.onDownload, this);
33257         this.trashBtn.on('click', this.onTrash, this);
33258         
33259         this.downloadBtn.hide();
33260         this.trashBtn.hide();
33261         
33262         if(this.showDownload){
33263             this.downloadBtn.show();
33264         }
33265         
33266         if(this.showTrash){
33267             this.trashBtn.show();
33268         }
33269         
33270         if(!this.showDownload && !this.showTrash) {
33271             this.footerEl.hide();
33272         }
33273         
33274     },
33275     
33276     initial : function()
33277     {
33278         this.fireEvent('initial', this);
33279         
33280     },
33281     
33282     onClick : function(e)
33283     {
33284         e.preventDefault();
33285         
33286         this.fireEvent('click', this);
33287     },
33288     
33289     onDownload : function(e)
33290     {
33291         e.preventDefault();
33292         
33293         this.fireEvent('download', this);
33294     },
33295     
33296     onTrash : function(e)
33297     {
33298         e.preventDefault();
33299         
33300         this.fireEvent('trash', this);
33301     }
33302     
33303 });
33304 /*
33305  * - LGPL
33306  *
33307  * nav progress bar
33308  * 
33309  */
33310
33311 /**
33312  * @class Roo.bootstrap.NavProgressBar
33313  * @extends Roo.bootstrap.Component
33314  * Bootstrap NavProgressBar class
33315  * 
33316  * @constructor
33317  * Create a new nav progress bar
33318  * @param {Object} config The config object
33319  */
33320
33321 Roo.bootstrap.NavProgressBar = function(config){
33322     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33323
33324     this.bullets = this.bullets || [];
33325    
33326 //    Roo.bootstrap.NavProgressBar.register(this);
33327      this.addEvents({
33328         /**
33329              * @event changed
33330              * Fires when the active item changes
33331              * @param {Roo.bootstrap.NavProgressBar} this
33332              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33333              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33334          */
33335         'changed': true
33336      });
33337     
33338 };
33339
33340 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33341     
33342     bullets : [],
33343     barItems : [],
33344     
33345     getAutoCreate : function()
33346     {
33347         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33348         
33349         cfg = {
33350             tag : 'div',
33351             cls : 'roo-navigation-bar-group',
33352             cn : [
33353                 {
33354                     tag : 'div',
33355                     cls : 'roo-navigation-top-bar'
33356                 },
33357                 {
33358                     tag : 'div',
33359                     cls : 'roo-navigation-bullets-bar',
33360                     cn : [
33361                         {
33362                             tag : 'ul',
33363                             cls : 'roo-navigation-bar'
33364                         }
33365                     ]
33366                 },
33367                 
33368                 {
33369                     tag : 'div',
33370                     cls : 'roo-navigation-bottom-bar'
33371                 }
33372             ]
33373             
33374         };
33375         
33376         return cfg;
33377         
33378     },
33379     
33380     initEvents: function() 
33381     {
33382         
33383     },
33384     
33385     onRender : function(ct, position) 
33386     {
33387         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33388         
33389         if(this.bullets.length){
33390             Roo.each(this.bullets, function(b){
33391                this.addItem(b);
33392             }, this);
33393         }
33394         
33395         this.format();
33396         
33397     },
33398     
33399     addItem : function(cfg)
33400     {
33401         var item = new Roo.bootstrap.NavProgressItem(cfg);
33402         
33403         item.parentId = this.id;
33404         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33405         
33406         if(cfg.html){
33407             var top = new Roo.bootstrap.Element({
33408                 tag : 'div',
33409                 cls : 'roo-navigation-bar-text'
33410             });
33411             
33412             var bottom = new Roo.bootstrap.Element({
33413                 tag : 'div',
33414                 cls : 'roo-navigation-bar-text'
33415             });
33416             
33417             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33418             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33419             
33420             var topText = new Roo.bootstrap.Element({
33421                 tag : 'span',
33422                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33423             });
33424             
33425             var bottomText = new Roo.bootstrap.Element({
33426                 tag : 'span',
33427                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33428             });
33429             
33430             topText.onRender(top.el, null);
33431             bottomText.onRender(bottom.el, null);
33432             
33433             item.topEl = top;
33434             item.bottomEl = bottom;
33435         }
33436         
33437         this.barItems.push(item);
33438         
33439         return item;
33440     },
33441     
33442     getActive : function()
33443     {
33444         var active = false;
33445         
33446         Roo.each(this.barItems, function(v){
33447             
33448             if (!v.isActive()) {
33449                 return;
33450             }
33451             
33452             active = v;
33453             return false;
33454             
33455         });
33456         
33457         return active;
33458     },
33459     
33460     setActiveItem : function(item)
33461     {
33462         var prev = false;
33463         
33464         Roo.each(this.barItems, function(v){
33465             if (v.rid == item.rid) {
33466                 return ;
33467             }
33468             
33469             if (v.isActive()) {
33470                 v.setActive(false);
33471                 prev = v;
33472             }
33473         });
33474
33475         item.setActive(true);
33476         
33477         this.fireEvent('changed', this, item, prev);
33478     },
33479     
33480     getBarItem: function(rid)
33481     {
33482         var ret = false;
33483         
33484         Roo.each(this.barItems, function(e) {
33485             if (e.rid != rid) {
33486                 return;
33487             }
33488             
33489             ret =  e;
33490             return false;
33491         });
33492         
33493         return ret;
33494     },
33495     
33496     indexOfItem : function(item)
33497     {
33498         var index = false;
33499         
33500         Roo.each(this.barItems, function(v, i){
33501             
33502             if (v.rid != item.rid) {
33503                 return;
33504             }
33505             
33506             index = i;
33507             return false
33508         });
33509         
33510         return index;
33511     },
33512     
33513     setActiveNext : function()
33514     {
33515         var i = this.indexOfItem(this.getActive());
33516         
33517         if (i > this.barItems.length) {
33518             return;
33519         }
33520         
33521         this.setActiveItem(this.barItems[i+1]);
33522     },
33523     
33524     setActivePrev : function()
33525     {
33526         var i = this.indexOfItem(this.getActive());
33527         
33528         if (i  < 1) {
33529             return;
33530         }
33531         
33532         this.setActiveItem(this.barItems[i-1]);
33533     },
33534     
33535     format : function()
33536     {
33537         if(!this.barItems.length){
33538             return;
33539         }
33540      
33541         var width = 100 / this.barItems.length;
33542         
33543         Roo.each(this.barItems, function(i){
33544             i.el.setStyle('width', width + '%');
33545             i.topEl.el.setStyle('width', width + '%');
33546             i.bottomEl.el.setStyle('width', width + '%');
33547         }, this);
33548         
33549     }
33550     
33551 });
33552 /*
33553  * - LGPL
33554  *
33555  * Nav Progress Item
33556  * 
33557  */
33558
33559 /**
33560  * @class Roo.bootstrap.NavProgressItem
33561  * @extends Roo.bootstrap.Component
33562  * Bootstrap NavProgressItem class
33563  * @cfg {String} rid the reference id
33564  * @cfg {Boolean} active (true|false) Is item active default false
33565  * @cfg {Boolean} disabled (true|false) Is item active default false
33566  * @cfg {String} html
33567  * @cfg {String} position (top|bottom) text position default bottom
33568  * @cfg {String} icon show icon instead of number
33569  * 
33570  * @constructor
33571  * Create a new NavProgressItem
33572  * @param {Object} config The config object
33573  */
33574 Roo.bootstrap.NavProgressItem = function(config){
33575     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33576     this.addEvents({
33577         // raw events
33578         /**
33579          * @event click
33580          * The raw click event for the entire grid.
33581          * @param {Roo.bootstrap.NavProgressItem} this
33582          * @param {Roo.EventObject} e
33583          */
33584         "click" : true
33585     });
33586    
33587 };
33588
33589 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33590     
33591     rid : '',
33592     active : false,
33593     disabled : false,
33594     html : '',
33595     position : 'bottom',
33596     icon : false,
33597     
33598     getAutoCreate : function()
33599     {
33600         var iconCls = 'roo-navigation-bar-item-icon';
33601         
33602         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33603         
33604         var cfg = {
33605             tag: 'li',
33606             cls: 'roo-navigation-bar-item',
33607             cn : [
33608                 {
33609                     tag : 'i',
33610                     cls : iconCls
33611                 }
33612             ]
33613         };
33614         
33615         if(this.active){
33616             cfg.cls += ' active';
33617         }
33618         if(this.disabled){
33619             cfg.cls += ' disabled';
33620         }
33621         
33622         return cfg;
33623     },
33624     
33625     disable : function()
33626     {
33627         this.setDisabled(true);
33628     },
33629     
33630     enable : function()
33631     {
33632         this.setDisabled(false);
33633     },
33634     
33635     initEvents: function() 
33636     {
33637         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33638         
33639         this.iconEl.on('click', this.onClick, this);
33640     },
33641     
33642     onClick : function(e)
33643     {
33644         e.preventDefault();
33645         
33646         if(this.disabled){
33647             return;
33648         }
33649         
33650         if(this.fireEvent('click', this, e) === false){
33651             return;
33652         };
33653         
33654         this.parent().setActiveItem(this);
33655     },
33656     
33657     isActive: function () 
33658     {
33659         return this.active;
33660     },
33661     
33662     setActive : function(state)
33663     {
33664         if(this.active == state){
33665             return;
33666         }
33667         
33668         this.active = state;
33669         
33670         if (state) {
33671             this.el.addClass('active');
33672             return;
33673         }
33674         
33675         this.el.removeClass('active');
33676         
33677         return;
33678     },
33679     
33680     setDisabled : function(state)
33681     {
33682         if(this.disabled == state){
33683             return;
33684         }
33685         
33686         this.disabled = state;
33687         
33688         if (state) {
33689             this.el.addClass('disabled');
33690             return;
33691         }
33692         
33693         this.el.removeClass('disabled');
33694     },
33695     
33696     tooltipEl : function()
33697     {
33698         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33699     }
33700 });
33701  
33702
33703  /*
33704  * - LGPL
33705  *
33706  * FieldLabel
33707  * 
33708  */
33709
33710 /**
33711  * @class Roo.bootstrap.FieldLabel
33712  * @extends Roo.bootstrap.Component
33713  * Bootstrap FieldLabel class
33714  * @cfg {String} html contents of the element
33715  * @cfg {String} tag tag of the element default label
33716  * @cfg {String} cls class of the element
33717  * @cfg {String} target label target 
33718  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33719  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33720  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33721  * @cfg {String} iconTooltip default "This field is required"
33722  * @cfg {String} indicatorpos (left|right) default left
33723  * 
33724  * @constructor
33725  * Create a new FieldLabel
33726  * @param {Object} config The config object
33727  */
33728
33729 Roo.bootstrap.FieldLabel = function(config){
33730     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33731     
33732     this.addEvents({
33733             /**
33734              * @event invalid
33735              * Fires after the field has been marked as invalid.
33736              * @param {Roo.form.FieldLabel} this
33737              * @param {String} msg The validation message
33738              */
33739             invalid : true,
33740             /**
33741              * @event valid
33742              * Fires after the field has been validated with no errors.
33743              * @param {Roo.form.FieldLabel} this
33744              */
33745             valid : true
33746         });
33747 };
33748
33749 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33750     
33751     tag: 'label',
33752     cls: '',
33753     html: '',
33754     target: '',
33755     allowBlank : true,
33756     invalidClass : 'has-warning',
33757     validClass : 'has-success',
33758     iconTooltip : 'This field is required',
33759     indicatorpos : 'left',
33760     
33761     getAutoCreate : function(){
33762         
33763         var cls = "";
33764         if (!this.allowBlank) {
33765             cls  = "visible";
33766         }
33767         
33768         var cfg = {
33769             tag : this.tag,
33770             cls : 'roo-bootstrap-field-label ' + this.cls,
33771             for : this.target,
33772             cn : [
33773                 {
33774                     tag : 'i',
33775                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33776                     tooltip : this.iconTooltip
33777                 },
33778                 {
33779                     tag : 'span',
33780                     html : this.html
33781                 }
33782             ] 
33783         };
33784         
33785         if(this.indicatorpos == 'right'){
33786             var cfg = {
33787                 tag : this.tag,
33788                 cls : 'roo-bootstrap-field-label ' + this.cls,
33789                 for : this.target,
33790                 cn : [
33791                     {
33792                         tag : 'span',
33793                         html : this.html
33794                     },
33795                     {
33796                         tag : 'i',
33797                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33798                         tooltip : this.iconTooltip
33799                     }
33800                 ] 
33801             };
33802         }
33803         
33804         return cfg;
33805     },
33806     
33807     initEvents: function() 
33808     {
33809         Roo.bootstrap.Element.superclass.initEvents.call(this);
33810         
33811         this.indicator = this.indicatorEl();
33812         
33813         if(this.indicator){
33814             this.indicator.removeClass('visible');
33815             this.indicator.addClass('invisible');
33816         }
33817         
33818         Roo.bootstrap.FieldLabel.register(this);
33819     },
33820     
33821     indicatorEl : function()
33822     {
33823         var indicator = this.el.select('i.roo-required-indicator',true).first();
33824         
33825         if(!indicator){
33826             return false;
33827         }
33828         
33829         return indicator;
33830         
33831     },
33832     
33833     /**
33834      * Mark this field as valid
33835      */
33836     markValid : function()
33837     {
33838         if(this.indicator){
33839             this.indicator.removeClass('visible');
33840             this.indicator.addClass('invisible');
33841         }
33842         if (Roo.bootstrap.version == 3) {
33843             this.el.removeClass(this.invalidClass);
33844             this.el.addClass(this.validClass);
33845         } else {
33846             this.el.removeClass('is-invalid');
33847             this.el.addClass('is-valid');
33848         }
33849         
33850         
33851         this.fireEvent('valid', this);
33852     },
33853     
33854     /**
33855      * Mark this field as invalid
33856      * @param {String} msg The validation message
33857      */
33858     markInvalid : function(msg)
33859     {
33860         if(this.indicator){
33861             this.indicator.removeClass('invisible');
33862             this.indicator.addClass('visible');
33863         }
33864           if (Roo.bootstrap.version == 3) {
33865             this.el.removeClass(this.validClass);
33866             this.el.addClass(this.invalidClass);
33867         } else {
33868             this.el.removeClass('is-valid');
33869             this.el.addClass('is-invalid');
33870         }
33871         
33872         
33873         this.fireEvent('invalid', this, msg);
33874     }
33875     
33876    
33877 });
33878
33879 Roo.apply(Roo.bootstrap.FieldLabel, {
33880     
33881     groups: {},
33882     
33883      /**
33884     * register a FieldLabel Group
33885     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33886     */
33887     register : function(label)
33888     {
33889         if(this.groups.hasOwnProperty(label.target)){
33890             return;
33891         }
33892      
33893         this.groups[label.target] = label;
33894         
33895     },
33896     /**
33897     * fetch a FieldLabel Group based on the target
33898     * @param {string} target
33899     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33900     */
33901     get: function(target) {
33902         if (typeof(this.groups[target]) == 'undefined') {
33903             return false;
33904         }
33905         
33906         return this.groups[target] ;
33907     }
33908 });
33909
33910  
33911
33912  /*
33913  * - LGPL
33914  *
33915  * page DateSplitField.
33916  * 
33917  */
33918
33919
33920 /**
33921  * @class Roo.bootstrap.DateSplitField
33922  * @extends Roo.bootstrap.Component
33923  * Bootstrap DateSplitField class
33924  * @cfg {string} fieldLabel - the label associated
33925  * @cfg {Number} labelWidth set the width of label (0-12)
33926  * @cfg {String} labelAlign (top|left)
33927  * @cfg {Boolean} dayAllowBlank (true|false) default false
33928  * @cfg {Boolean} monthAllowBlank (true|false) default false
33929  * @cfg {Boolean} yearAllowBlank (true|false) default false
33930  * @cfg {string} dayPlaceholder 
33931  * @cfg {string} monthPlaceholder
33932  * @cfg {string} yearPlaceholder
33933  * @cfg {string} dayFormat default 'd'
33934  * @cfg {string} monthFormat default 'm'
33935  * @cfg {string} yearFormat default 'Y'
33936  * @cfg {Number} labellg set the width of label (1-12)
33937  * @cfg {Number} labelmd set the width of label (1-12)
33938  * @cfg {Number} labelsm set the width of label (1-12)
33939  * @cfg {Number} labelxs set the width of label (1-12)
33940
33941  *     
33942  * @constructor
33943  * Create a new DateSplitField
33944  * @param {Object} config The config object
33945  */
33946
33947 Roo.bootstrap.DateSplitField = function(config){
33948     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33949     
33950     this.addEvents({
33951         // raw events
33952          /**
33953          * @event years
33954          * getting the data of years
33955          * @param {Roo.bootstrap.DateSplitField} this
33956          * @param {Object} years
33957          */
33958         "years" : true,
33959         /**
33960          * @event days
33961          * getting the data of days
33962          * @param {Roo.bootstrap.DateSplitField} this
33963          * @param {Object} days
33964          */
33965         "days" : true,
33966         /**
33967          * @event invalid
33968          * Fires after the field has been marked as invalid.
33969          * @param {Roo.form.Field} this
33970          * @param {String} msg The validation message
33971          */
33972         invalid : true,
33973        /**
33974          * @event valid
33975          * Fires after the field has been validated with no errors.
33976          * @param {Roo.form.Field} this
33977          */
33978         valid : true
33979     });
33980 };
33981
33982 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33983     
33984     fieldLabel : '',
33985     labelAlign : 'top',
33986     labelWidth : 3,
33987     dayAllowBlank : false,
33988     monthAllowBlank : false,
33989     yearAllowBlank : false,
33990     dayPlaceholder : '',
33991     monthPlaceholder : '',
33992     yearPlaceholder : '',
33993     dayFormat : 'd',
33994     monthFormat : 'm',
33995     yearFormat : 'Y',
33996     isFormField : true,
33997     labellg : 0,
33998     labelmd : 0,
33999     labelsm : 0,
34000     labelxs : 0,
34001     
34002     getAutoCreate : function()
34003     {
34004         var cfg = {
34005             tag : 'div',
34006             cls : 'row roo-date-split-field-group',
34007             cn : [
34008                 {
34009                     tag : 'input',
34010                     type : 'hidden',
34011                     cls : 'form-hidden-field roo-date-split-field-group-value',
34012                     name : this.name
34013                 }
34014             ]
34015         };
34016         
34017         var labelCls = 'col-md-12';
34018         var contentCls = 'col-md-4';
34019         
34020         if(this.fieldLabel){
34021             
34022             var label = {
34023                 tag : 'div',
34024                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34025                 cn : [
34026                     {
34027                         tag : 'label',
34028                         html : this.fieldLabel
34029                     }
34030                 ]
34031             };
34032             
34033             if(this.labelAlign == 'left'){
34034             
34035                 if(this.labelWidth > 12){
34036                     label.style = "width: " + this.labelWidth + 'px';
34037                 }
34038
34039                 if(this.labelWidth < 13 && this.labelmd == 0){
34040                     this.labelmd = this.labelWidth;
34041                 }
34042
34043                 if(this.labellg > 0){
34044                     labelCls = ' col-lg-' + this.labellg;
34045                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34046                 }
34047
34048                 if(this.labelmd > 0){
34049                     labelCls = ' col-md-' + this.labelmd;
34050                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34051                 }
34052
34053                 if(this.labelsm > 0){
34054                     labelCls = ' col-sm-' + this.labelsm;
34055                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34056                 }
34057
34058                 if(this.labelxs > 0){
34059                     labelCls = ' col-xs-' + this.labelxs;
34060                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34061                 }
34062             }
34063             
34064             label.cls += ' ' + labelCls;
34065             
34066             cfg.cn.push(label);
34067         }
34068         
34069         Roo.each(['day', 'month', 'year'], function(t){
34070             cfg.cn.push({
34071                 tag : 'div',
34072                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34073             });
34074         }, this);
34075         
34076         return cfg;
34077     },
34078     
34079     inputEl: function ()
34080     {
34081         return this.el.select('.roo-date-split-field-group-value', true).first();
34082     },
34083     
34084     onRender : function(ct, position) 
34085     {
34086         var _this = this;
34087         
34088         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34089         
34090         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34091         
34092         this.dayField = new Roo.bootstrap.ComboBox({
34093             allowBlank : this.dayAllowBlank,
34094             alwaysQuery : true,
34095             displayField : 'value',
34096             editable : false,
34097             fieldLabel : '',
34098             forceSelection : true,
34099             mode : 'local',
34100             placeholder : this.dayPlaceholder,
34101             selectOnFocus : true,
34102             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34103             triggerAction : 'all',
34104             typeAhead : true,
34105             valueField : 'value',
34106             store : new Roo.data.SimpleStore({
34107                 data : (function() {    
34108                     var days = [];
34109                     _this.fireEvent('days', _this, days);
34110                     return days;
34111                 })(),
34112                 fields : [ 'value' ]
34113             }),
34114             listeners : {
34115                 select : function (_self, record, index)
34116                 {
34117                     _this.setValue(_this.getValue());
34118                 }
34119             }
34120         });
34121
34122         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34123         
34124         this.monthField = new Roo.bootstrap.MonthField({
34125             after : '<i class=\"fa fa-calendar\"></i>',
34126             allowBlank : this.monthAllowBlank,
34127             placeholder : this.monthPlaceholder,
34128             readOnly : true,
34129             listeners : {
34130                 render : function (_self)
34131                 {
34132                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34133                         e.preventDefault();
34134                         _self.focus();
34135                     });
34136                 },
34137                 select : function (_self, oldvalue, newvalue)
34138                 {
34139                     _this.setValue(_this.getValue());
34140                 }
34141             }
34142         });
34143         
34144         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34145         
34146         this.yearField = new Roo.bootstrap.ComboBox({
34147             allowBlank : this.yearAllowBlank,
34148             alwaysQuery : true,
34149             displayField : 'value',
34150             editable : false,
34151             fieldLabel : '',
34152             forceSelection : true,
34153             mode : 'local',
34154             placeholder : this.yearPlaceholder,
34155             selectOnFocus : true,
34156             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34157             triggerAction : 'all',
34158             typeAhead : true,
34159             valueField : 'value',
34160             store : new Roo.data.SimpleStore({
34161                 data : (function() {
34162                     var years = [];
34163                     _this.fireEvent('years', _this, years);
34164                     return years;
34165                 })(),
34166                 fields : [ 'value' ]
34167             }),
34168             listeners : {
34169                 select : function (_self, record, index)
34170                 {
34171                     _this.setValue(_this.getValue());
34172                 }
34173             }
34174         });
34175
34176         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34177     },
34178     
34179     setValue : function(v, format)
34180     {
34181         this.inputEl.dom.value = v;
34182         
34183         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34184         
34185         var d = Date.parseDate(v, f);
34186         
34187         if(!d){
34188             this.validate();
34189             return;
34190         }
34191         
34192         this.setDay(d.format(this.dayFormat));
34193         this.setMonth(d.format(this.monthFormat));
34194         this.setYear(d.format(this.yearFormat));
34195         
34196         this.validate();
34197         
34198         return;
34199     },
34200     
34201     setDay : function(v)
34202     {
34203         this.dayField.setValue(v);
34204         this.inputEl.dom.value = this.getValue();
34205         this.validate();
34206         return;
34207     },
34208     
34209     setMonth : function(v)
34210     {
34211         this.monthField.setValue(v, true);
34212         this.inputEl.dom.value = this.getValue();
34213         this.validate();
34214         return;
34215     },
34216     
34217     setYear : function(v)
34218     {
34219         this.yearField.setValue(v);
34220         this.inputEl.dom.value = this.getValue();
34221         this.validate();
34222         return;
34223     },
34224     
34225     getDay : function()
34226     {
34227         return this.dayField.getValue();
34228     },
34229     
34230     getMonth : function()
34231     {
34232         return this.monthField.getValue();
34233     },
34234     
34235     getYear : function()
34236     {
34237         return this.yearField.getValue();
34238     },
34239     
34240     getValue : function()
34241     {
34242         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34243         
34244         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34245         
34246         return date;
34247     },
34248     
34249     reset : function()
34250     {
34251         this.setDay('');
34252         this.setMonth('');
34253         this.setYear('');
34254         this.inputEl.dom.value = '';
34255         this.validate();
34256         return;
34257     },
34258     
34259     validate : function()
34260     {
34261         var d = this.dayField.validate();
34262         var m = this.monthField.validate();
34263         var y = this.yearField.validate();
34264         
34265         var valid = true;
34266         
34267         if(
34268                 (!this.dayAllowBlank && !d) ||
34269                 (!this.monthAllowBlank && !m) ||
34270                 (!this.yearAllowBlank && !y)
34271         ){
34272             valid = false;
34273         }
34274         
34275         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34276             return valid;
34277         }
34278         
34279         if(valid){
34280             this.markValid();
34281             return valid;
34282         }
34283         
34284         this.markInvalid();
34285         
34286         return valid;
34287     },
34288     
34289     markValid : function()
34290     {
34291         
34292         var label = this.el.select('label', true).first();
34293         var icon = this.el.select('i.fa-star', true).first();
34294
34295         if(label && icon){
34296             icon.remove();
34297         }
34298         
34299         this.fireEvent('valid', this);
34300     },
34301     
34302      /**
34303      * Mark this field as invalid
34304      * @param {String} msg The validation message
34305      */
34306     markInvalid : function(msg)
34307     {
34308         
34309         var label = this.el.select('label', true).first();
34310         var icon = this.el.select('i.fa-star', true).first();
34311
34312         if(label && !icon){
34313             this.el.select('.roo-date-split-field-label', true).createChild({
34314                 tag : 'i',
34315                 cls : 'text-danger fa fa-lg fa-star',
34316                 tooltip : 'This field is required',
34317                 style : 'margin-right:5px;'
34318             }, label, true);
34319         }
34320         
34321         this.fireEvent('invalid', this, msg);
34322     },
34323     
34324     clearInvalid : function()
34325     {
34326         var label = this.el.select('label', true).first();
34327         var icon = this.el.select('i.fa-star', true).first();
34328
34329         if(label && icon){
34330             icon.remove();
34331         }
34332         
34333         this.fireEvent('valid', this);
34334     },
34335     
34336     getName: function()
34337     {
34338         return this.name;
34339     }
34340     
34341 });
34342
34343  /**
34344  *
34345  * This is based on 
34346  * http://masonry.desandro.com
34347  *
34348  * The idea is to render all the bricks based on vertical width...
34349  *
34350  * The original code extends 'outlayer' - we might need to use that....
34351  * 
34352  */
34353
34354
34355 /**
34356  * @class Roo.bootstrap.LayoutMasonry
34357  * @extends Roo.bootstrap.Component
34358  * Bootstrap Layout Masonry class
34359  * 
34360  * @constructor
34361  * Create a new Element
34362  * @param {Object} config The config object
34363  */
34364
34365 Roo.bootstrap.LayoutMasonry = function(config){
34366     
34367     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34368     
34369     this.bricks = [];
34370     
34371     Roo.bootstrap.LayoutMasonry.register(this);
34372     
34373     this.addEvents({
34374         // raw events
34375         /**
34376          * @event layout
34377          * Fire after layout the items
34378          * @param {Roo.bootstrap.LayoutMasonry} this
34379          * @param {Roo.EventObject} e
34380          */
34381         "layout" : true
34382     });
34383     
34384 };
34385
34386 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34387     
34388     /**
34389      * @cfg {Boolean} isLayoutInstant = no animation?
34390      */   
34391     isLayoutInstant : false, // needed?
34392    
34393     /**
34394      * @cfg {Number} boxWidth  width of the columns
34395      */   
34396     boxWidth : 450,
34397     
34398       /**
34399      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34400      */   
34401     boxHeight : 0,
34402     
34403     /**
34404      * @cfg {Number} padWidth padding below box..
34405      */   
34406     padWidth : 10, 
34407     
34408     /**
34409      * @cfg {Number} gutter gutter width..
34410      */   
34411     gutter : 10,
34412     
34413      /**
34414      * @cfg {Number} maxCols maximum number of columns
34415      */   
34416     
34417     maxCols: 0,
34418     
34419     /**
34420      * @cfg {Boolean} isAutoInitial defalut true
34421      */   
34422     isAutoInitial : true, 
34423     
34424     containerWidth: 0,
34425     
34426     /**
34427      * @cfg {Boolean} isHorizontal defalut false
34428      */   
34429     isHorizontal : false, 
34430
34431     currentSize : null,
34432     
34433     tag: 'div',
34434     
34435     cls: '',
34436     
34437     bricks: null, //CompositeElement
34438     
34439     cols : 1,
34440     
34441     _isLayoutInited : false,
34442     
34443 //    isAlternative : false, // only use for vertical layout...
34444     
34445     /**
34446      * @cfg {Number} alternativePadWidth padding below box..
34447      */   
34448     alternativePadWidth : 50,
34449     
34450     selectedBrick : [],
34451     
34452     getAutoCreate : function(){
34453         
34454         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34455         
34456         var cfg = {
34457             tag: this.tag,
34458             cls: 'blog-masonary-wrapper ' + this.cls,
34459             cn : {
34460                 cls : 'mas-boxes masonary'
34461             }
34462         };
34463         
34464         return cfg;
34465     },
34466     
34467     getChildContainer: function( )
34468     {
34469         if (this.boxesEl) {
34470             return this.boxesEl;
34471         }
34472         
34473         this.boxesEl = this.el.select('.mas-boxes').first();
34474         
34475         return this.boxesEl;
34476     },
34477     
34478     
34479     initEvents : function()
34480     {
34481         var _this = this;
34482         
34483         if(this.isAutoInitial){
34484             Roo.log('hook children rendered');
34485             this.on('childrenrendered', function() {
34486                 Roo.log('children rendered');
34487                 _this.initial();
34488             } ,this);
34489         }
34490     },
34491     
34492     initial : function()
34493     {
34494         this.selectedBrick = [];
34495         
34496         this.currentSize = this.el.getBox(true);
34497         
34498         Roo.EventManager.onWindowResize(this.resize, this); 
34499
34500         if(!this.isAutoInitial){
34501             this.layout();
34502             return;
34503         }
34504         
34505         this.layout();
34506         
34507         return;
34508         //this.layout.defer(500,this);
34509         
34510     },
34511     
34512     resize : function()
34513     {
34514         var cs = this.el.getBox(true);
34515         
34516         if (
34517                 this.currentSize.width == cs.width && 
34518                 this.currentSize.x == cs.x && 
34519                 this.currentSize.height == cs.height && 
34520                 this.currentSize.y == cs.y 
34521         ) {
34522             Roo.log("no change in with or X or Y");
34523             return;
34524         }
34525         
34526         this.currentSize = cs;
34527         
34528         this.layout();
34529         
34530     },
34531     
34532     layout : function()
34533     {   
34534         this._resetLayout();
34535         
34536         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34537         
34538         this.layoutItems( isInstant );
34539       
34540         this._isLayoutInited = true;
34541         
34542         this.fireEvent('layout', this);
34543         
34544     },
34545     
34546     _resetLayout : function()
34547     {
34548         if(this.isHorizontal){
34549             this.horizontalMeasureColumns();
34550             return;
34551         }
34552         
34553         this.verticalMeasureColumns();
34554         
34555     },
34556     
34557     verticalMeasureColumns : function()
34558     {
34559         this.getContainerWidth();
34560         
34561 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34562 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34563 //            return;
34564 //        }
34565         
34566         var boxWidth = this.boxWidth + this.padWidth;
34567         
34568         if(this.containerWidth < this.boxWidth){
34569             boxWidth = this.containerWidth
34570         }
34571         
34572         var containerWidth = this.containerWidth;
34573         
34574         var cols = Math.floor(containerWidth / boxWidth);
34575         
34576         this.cols = Math.max( cols, 1 );
34577         
34578         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34579         
34580         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34581         
34582         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34583         
34584         this.colWidth = boxWidth + avail - this.padWidth;
34585         
34586         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34587         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34588     },
34589     
34590     horizontalMeasureColumns : function()
34591     {
34592         this.getContainerWidth();
34593         
34594         var boxWidth = this.boxWidth;
34595         
34596         if(this.containerWidth < boxWidth){
34597             boxWidth = this.containerWidth;
34598         }
34599         
34600         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34601         
34602         this.el.setHeight(boxWidth);
34603         
34604     },
34605     
34606     getContainerWidth : function()
34607     {
34608         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34609     },
34610     
34611     layoutItems : function( isInstant )
34612     {
34613         Roo.log(this.bricks);
34614         
34615         var items = Roo.apply([], this.bricks);
34616         
34617         if(this.isHorizontal){
34618             this._horizontalLayoutItems( items , isInstant );
34619             return;
34620         }
34621         
34622 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34623 //            this._verticalAlternativeLayoutItems( items , isInstant );
34624 //            return;
34625 //        }
34626         
34627         this._verticalLayoutItems( items , isInstant );
34628         
34629     },
34630     
34631     _verticalLayoutItems : function ( items , isInstant)
34632     {
34633         if ( !items || !items.length ) {
34634             return;
34635         }
34636         
34637         var standard = [
34638             ['xs', 'xs', 'xs', 'tall'],
34639             ['xs', 'xs', 'tall'],
34640             ['xs', 'xs', 'sm'],
34641             ['xs', 'xs', 'xs'],
34642             ['xs', 'tall'],
34643             ['xs', 'sm'],
34644             ['xs', 'xs'],
34645             ['xs'],
34646             
34647             ['sm', 'xs', 'xs'],
34648             ['sm', 'xs'],
34649             ['sm'],
34650             
34651             ['tall', 'xs', 'xs', 'xs'],
34652             ['tall', 'xs', 'xs'],
34653             ['tall', 'xs'],
34654             ['tall']
34655             
34656         ];
34657         
34658         var queue = [];
34659         
34660         var boxes = [];
34661         
34662         var box = [];
34663         
34664         Roo.each(items, function(item, k){
34665             
34666             switch (item.size) {
34667                 // these layouts take up a full box,
34668                 case 'md' :
34669                 case 'md-left' :
34670                 case 'md-right' :
34671                 case 'wide' :
34672                     
34673                     if(box.length){
34674                         boxes.push(box);
34675                         box = [];
34676                     }
34677                     
34678                     boxes.push([item]);
34679                     
34680                     break;
34681                     
34682                 case 'xs' :
34683                 case 'sm' :
34684                 case 'tall' :
34685                     
34686                     box.push(item);
34687                     
34688                     break;
34689                 default :
34690                     break;
34691                     
34692             }
34693             
34694         }, this);
34695         
34696         if(box.length){
34697             boxes.push(box);
34698             box = [];
34699         }
34700         
34701         var filterPattern = function(box, length)
34702         {
34703             if(!box.length){
34704                 return;
34705             }
34706             
34707             var match = false;
34708             
34709             var pattern = box.slice(0, length);
34710             
34711             var format = [];
34712             
34713             Roo.each(pattern, function(i){
34714                 format.push(i.size);
34715             }, this);
34716             
34717             Roo.each(standard, function(s){
34718                 
34719                 if(String(s) != String(format)){
34720                     return;
34721                 }
34722                 
34723                 match = true;
34724                 return false;
34725                 
34726             }, this);
34727             
34728             if(!match && length == 1){
34729                 return;
34730             }
34731             
34732             if(!match){
34733                 filterPattern(box, length - 1);
34734                 return;
34735             }
34736                 
34737             queue.push(pattern);
34738
34739             box = box.slice(length, box.length);
34740
34741             filterPattern(box, 4);
34742
34743             return;
34744             
34745         }
34746         
34747         Roo.each(boxes, function(box, k){
34748             
34749             if(!box.length){
34750                 return;
34751             }
34752             
34753             if(box.length == 1){
34754                 queue.push(box);
34755                 return;
34756             }
34757             
34758             filterPattern(box, 4);
34759             
34760         }, this);
34761         
34762         this._processVerticalLayoutQueue( queue, isInstant );
34763         
34764     },
34765     
34766 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34767 //    {
34768 //        if ( !items || !items.length ) {
34769 //            return;
34770 //        }
34771 //
34772 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34773 //        
34774 //    },
34775     
34776     _horizontalLayoutItems : function ( items , isInstant)
34777     {
34778         if ( !items || !items.length || items.length < 3) {
34779             return;
34780         }
34781         
34782         items.reverse();
34783         
34784         var eItems = items.slice(0, 3);
34785         
34786         items = items.slice(3, items.length);
34787         
34788         var standard = [
34789             ['xs', 'xs', 'xs', 'wide'],
34790             ['xs', 'xs', 'wide'],
34791             ['xs', 'xs', 'sm'],
34792             ['xs', 'xs', 'xs'],
34793             ['xs', 'wide'],
34794             ['xs', 'sm'],
34795             ['xs', 'xs'],
34796             ['xs'],
34797             
34798             ['sm', 'xs', 'xs'],
34799             ['sm', 'xs'],
34800             ['sm'],
34801             
34802             ['wide', 'xs', 'xs', 'xs'],
34803             ['wide', 'xs', 'xs'],
34804             ['wide', 'xs'],
34805             ['wide'],
34806             
34807             ['wide-thin']
34808         ];
34809         
34810         var queue = [];
34811         
34812         var boxes = [];
34813         
34814         var box = [];
34815         
34816         Roo.each(items, function(item, k){
34817             
34818             switch (item.size) {
34819                 case 'md' :
34820                 case 'md-left' :
34821                 case 'md-right' :
34822                 case 'tall' :
34823                     
34824                     if(box.length){
34825                         boxes.push(box);
34826                         box = [];
34827                     }
34828                     
34829                     boxes.push([item]);
34830                     
34831                     break;
34832                     
34833                 case 'xs' :
34834                 case 'sm' :
34835                 case 'wide' :
34836                 case 'wide-thin' :
34837                     
34838                     box.push(item);
34839                     
34840                     break;
34841                 default :
34842                     break;
34843                     
34844             }
34845             
34846         }, this);
34847         
34848         if(box.length){
34849             boxes.push(box);
34850             box = [];
34851         }
34852         
34853         var filterPattern = function(box, length)
34854         {
34855             if(!box.length){
34856                 return;
34857             }
34858             
34859             var match = false;
34860             
34861             var pattern = box.slice(0, length);
34862             
34863             var format = [];
34864             
34865             Roo.each(pattern, function(i){
34866                 format.push(i.size);
34867             }, this);
34868             
34869             Roo.each(standard, function(s){
34870                 
34871                 if(String(s) != String(format)){
34872                     return;
34873                 }
34874                 
34875                 match = true;
34876                 return false;
34877                 
34878             }, this);
34879             
34880             if(!match && length == 1){
34881                 return;
34882             }
34883             
34884             if(!match){
34885                 filterPattern(box, length - 1);
34886                 return;
34887             }
34888                 
34889             queue.push(pattern);
34890
34891             box = box.slice(length, box.length);
34892
34893             filterPattern(box, 4);
34894
34895             return;
34896             
34897         }
34898         
34899         Roo.each(boxes, function(box, k){
34900             
34901             if(!box.length){
34902                 return;
34903             }
34904             
34905             if(box.length == 1){
34906                 queue.push(box);
34907                 return;
34908             }
34909             
34910             filterPattern(box, 4);
34911             
34912         }, this);
34913         
34914         
34915         var prune = [];
34916         
34917         var pos = this.el.getBox(true);
34918         
34919         var minX = pos.x;
34920         
34921         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34922         
34923         var hit_end = false;
34924         
34925         Roo.each(queue, function(box){
34926             
34927             if(hit_end){
34928                 
34929                 Roo.each(box, function(b){
34930                 
34931                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34932                     b.el.hide();
34933
34934                 }, this);
34935
34936                 return;
34937             }
34938             
34939             var mx = 0;
34940             
34941             Roo.each(box, function(b){
34942                 
34943                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34944                 b.el.show();
34945
34946                 mx = Math.max(mx, b.x);
34947                 
34948             }, this);
34949             
34950             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34951             
34952             if(maxX < minX){
34953                 
34954                 Roo.each(box, function(b){
34955                 
34956                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34957                     b.el.hide();
34958                     
34959                 }, this);
34960                 
34961                 hit_end = true;
34962                 
34963                 return;
34964             }
34965             
34966             prune.push(box);
34967             
34968         }, this);
34969         
34970         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34971     },
34972     
34973     /** Sets position of item in DOM
34974     * @param {Element} item
34975     * @param {Number} x - horizontal position
34976     * @param {Number} y - vertical position
34977     * @param {Boolean} isInstant - disables transitions
34978     */
34979     _processVerticalLayoutQueue : function( queue, isInstant )
34980     {
34981         var pos = this.el.getBox(true);
34982         var x = pos.x;
34983         var y = pos.y;
34984         var maxY = [];
34985         
34986         for (var i = 0; i < this.cols; i++){
34987             maxY[i] = pos.y;
34988         }
34989         
34990         Roo.each(queue, function(box, k){
34991             
34992             var col = k % this.cols;
34993             
34994             Roo.each(box, function(b,kk){
34995                 
34996                 b.el.position('absolute');
34997                 
34998                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34999                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35000                 
35001                 if(b.size == 'md-left' || b.size == 'md-right'){
35002                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35003                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35004                 }
35005                 
35006                 b.el.setWidth(width);
35007                 b.el.setHeight(height);
35008                 // iframe?
35009                 b.el.select('iframe',true).setSize(width,height);
35010                 
35011             }, this);
35012             
35013             for (var i = 0; i < this.cols; i++){
35014                 
35015                 if(maxY[i] < maxY[col]){
35016                     col = i;
35017                     continue;
35018                 }
35019                 
35020                 col = Math.min(col, i);
35021                 
35022             }
35023             
35024             x = pos.x + col * (this.colWidth + this.padWidth);
35025             
35026             y = maxY[col];
35027             
35028             var positions = [];
35029             
35030             switch (box.length){
35031                 case 1 :
35032                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35033                     break;
35034                 case 2 :
35035                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35036                     break;
35037                 case 3 :
35038                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35039                     break;
35040                 case 4 :
35041                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35042                     break;
35043                 default :
35044                     break;
35045             }
35046             
35047             Roo.each(box, function(b,kk){
35048                 
35049                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35050                 
35051                 var sz = b.el.getSize();
35052                 
35053                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35054                 
35055             }, this);
35056             
35057         }, this);
35058         
35059         var mY = 0;
35060         
35061         for (var i = 0; i < this.cols; i++){
35062             mY = Math.max(mY, maxY[i]);
35063         }
35064         
35065         this.el.setHeight(mY - pos.y);
35066         
35067     },
35068     
35069 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35070 //    {
35071 //        var pos = this.el.getBox(true);
35072 //        var x = pos.x;
35073 //        var y = pos.y;
35074 //        var maxX = pos.right;
35075 //        
35076 //        var maxHeight = 0;
35077 //        
35078 //        Roo.each(items, function(item, k){
35079 //            
35080 //            var c = k % 2;
35081 //            
35082 //            item.el.position('absolute');
35083 //                
35084 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35085 //
35086 //            item.el.setWidth(width);
35087 //
35088 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35089 //
35090 //            item.el.setHeight(height);
35091 //            
35092 //            if(c == 0){
35093 //                item.el.setXY([x, y], isInstant ? false : true);
35094 //            } else {
35095 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35096 //            }
35097 //            
35098 //            y = y + height + this.alternativePadWidth;
35099 //            
35100 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35101 //            
35102 //        }, this);
35103 //        
35104 //        this.el.setHeight(maxHeight);
35105 //        
35106 //    },
35107     
35108     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35109     {
35110         var pos = this.el.getBox(true);
35111         
35112         var minX = pos.x;
35113         var minY = pos.y;
35114         
35115         var maxX = pos.right;
35116         
35117         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35118         
35119         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35120         
35121         Roo.each(queue, function(box, k){
35122             
35123             Roo.each(box, function(b, kk){
35124                 
35125                 b.el.position('absolute');
35126                 
35127                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35128                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35129                 
35130                 if(b.size == 'md-left' || b.size == 'md-right'){
35131                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35132                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35133                 }
35134                 
35135                 b.el.setWidth(width);
35136                 b.el.setHeight(height);
35137                 
35138             }, this);
35139             
35140             if(!box.length){
35141                 return;
35142             }
35143             
35144             var positions = [];
35145             
35146             switch (box.length){
35147                 case 1 :
35148                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35149                     break;
35150                 case 2 :
35151                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35152                     break;
35153                 case 3 :
35154                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35155                     break;
35156                 case 4 :
35157                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35158                     break;
35159                 default :
35160                     break;
35161             }
35162             
35163             Roo.each(box, function(b,kk){
35164                 
35165                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35166                 
35167                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35168                 
35169             }, this);
35170             
35171         }, this);
35172         
35173     },
35174     
35175     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35176     {
35177         Roo.each(eItems, function(b,k){
35178             
35179             b.size = (k == 0) ? 'sm' : 'xs';
35180             b.x = (k == 0) ? 2 : 1;
35181             b.y = (k == 0) ? 2 : 1;
35182             
35183             b.el.position('absolute');
35184             
35185             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35186                 
35187             b.el.setWidth(width);
35188             
35189             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35190             
35191             b.el.setHeight(height);
35192             
35193         }, this);
35194
35195         var positions = [];
35196         
35197         positions.push({
35198             x : maxX - this.unitWidth * 2 - this.gutter,
35199             y : minY
35200         });
35201         
35202         positions.push({
35203             x : maxX - this.unitWidth,
35204             y : minY + (this.unitWidth + this.gutter) * 2
35205         });
35206         
35207         positions.push({
35208             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35209             y : minY
35210         });
35211         
35212         Roo.each(eItems, function(b,k){
35213             
35214             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35215
35216         }, this);
35217         
35218     },
35219     
35220     getVerticalOneBoxColPositions : function(x, y, box)
35221     {
35222         var pos = [];
35223         
35224         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35225         
35226         if(box[0].size == 'md-left'){
35227             rand = 0;
35228         }
35229         
35230         if(box[0].size == 'md-right'){
35231             rand = 1;
35232         }
35233         
35234         pos.push({
35235             x : x + (this.unitWidth + this.gutter) * rand,
35236             y : y
35237         });
35238         
35239         return pos;
35240     },
35241     
35242     getVerticalTwoBoxColPositions : function(x, y, box)
35243     {
35244         var pos = [];
35245         
35246         if(box[0].size == 'xs'){
35247             
35248             pos.push({
35249                 x : x,
35250                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35251             });
35252
35253             pos.push({
35254                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35255                 y : y
35256             });
35257             
35258             return pos;
35259             
35260         }
35261         
35262         pos.push({
35263             x : x,
35264             y : y
35265         });
35266
35267         pos.push({
35268             x : x + (this.unitWidth + this.gutter) * 2,
35269             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35270         });
35271         
35272         return pos;
35273         
35274     },
35275     
35276     getVerticalThreeBoxColPositions : function(x, y, box)
35277     {
35278         var pos = [];
35279         
35280         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35281             
35282             pos.push({
35283                 x : x,
35284                 y : y
35285             });
35286
35287             pos.push({
35288                 x : x + (this.unitWidth + this.gutter) * 1,
35289                 y : y
35290             });
35291             
35292             pos.push({
35293                 x : x + (this.unitWidth + this.gutter) * 2,
35294                 y : y
35295             });
35296             
35297             return pos;
35298             
35299         }
35300         
35301         if(box[0].size == 'xs' && box[1].size == 'xs'){
35302             
35303             pos.push({
35304                 x : x,
35305                 y : y
35306             });
35307
35308             pos.push({
35309                 x : x,
35310                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35311             });
35312             
35313             pos.push({
35314                 x : x + (this.unitWidth + this.gutter) * 1,
35315                 y : y
35316             });
35317             
35318             return pos;
35319             
35320         }
35321         
35322         pos.push({
35323             x : x,
35324             y : y
35325         });
35326
35327         pos.push({
35328             x : x + (this.unitWidth + this.gutter) * 2,
35329             y : y
35330         });
35331
35332         pos.push({
35333             x : x + (this.unitWidth + this.gutter) * 2,
35334             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35335         });
35336             
35337         return pos;
35338         
35339     },
35340     
35341     getVerticalFourBoxColPositions : function(x, y, box)
35342     {
35343         var pos = [];
35344         
35345         if(box[0].size == 'xs'){
35346             
35347             pos.push({
35348                 x : x,
35349                 y : y
35350             });
35351
35352             pos.push({
35353                 x : x,
35354                 y : y + (this.unitHeight + this.gutter) * 1
35355             });
35356             
35357             pos.push({
35358                 x : x,
35359                 y : y + (this.unitHeight + this.gutter) * 2
35360             });
35361             
35362             pos.push({
35363                 x : x + (this.unitWidth + this.gutter) * 1,
35364                 y : y
35365             });
35366             
35367             return pos;
35368             
35369         }
35370         
35371         pos.push({
35372             x : x,
35373             y : y
35374         });
35375
35376         pos.push({
35377             x : x + (this.unitWidth + this.gutter) * 2,
35378             y : y
35379         });
35380
35381         pos.push({
35382             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35383             y : y + (this.unitHeight + this.gutter) * 1
35384         });
35385
35386         pos.push({
35387             x : x + (this.unitWidth + this.gutter) * 2,
35388             y : y + (this.unitWidth + this.gutter) * 2
35389         });
35390
35391         return pos;
35392         
35393     },
35394     
35395     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35396     {
35397         var pos = [];
35398         
35399         if(box[0].size == 'md-left'){
35400             pos.push({
35401                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35402                 y : minY
35403             });
35404             
35405             return pos;
35406         }
35407         
35408         if(box[0].size == 'md-right'){
35409             pos.push({
35410                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35411                 y : minY + (this.unitWidth + this.gutter) * 1
35412             });
35413             
35414             return pos;
35415         }
35416         
35417         var rand = Math.floor(Math.random() * (4 - box[0].y));
35418         
35419         pos.push({
35420             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35421             y : minY + (this.unitWidth + this.gutter) * rand
35422         });
35423         
35424         return pos;
35425         
35426     },
35427     
35428     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35429     {
35430         var pos = [];
35431         
35432         if(box[0].size == 'xs'){
35433             
35434             pos.push({
35435                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35436                 y : minY
35437             });
35438
35439             pos.push({
35440                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35441                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35442             });
35443             
35444             return pos;
35445             
35446         }
35447         
35448         pos.push({
35449             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35450             y : minY
35451         });
35452
35453         pos.push({
35454             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35455             y : minY + (this.unitWidth + this.gutter) * 2
35456         });
35457         
35458         return pos;
35459         
35460     },
35461     
35462     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35463     {
35464         var pos = [];
35465         
35466         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35467             
35468             pos.push({
35469                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35470                 y : minY
35471             });
35472
35473             pos.push({
35474                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35475                 y : minY + (this.unitWidth + this.gutter) * 1
35476             });
35477             
35478             pos.push({
35479                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35480                 y : minY + (this.unitWidth + this.gutter) * 2
35481             });
35482             
35483             return pos;
35484             
35485         }
35486         
35487         if(box[0].size == 'xs' && box[1].size == 'xs'){
35488             
35489             pos.push({
35490                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35491                 y : minY
35492             });
35493
35494             pos.push({
35495                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35496                 y : minY
35497             });
35498             
35499             pos.push({
35500                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35501                 y : minY + (this.unitWidth + this.gutter) * 1
35502             });
35503             
35504             return pos;
35505             
35506         }
35507         
35508         pos.push({
35509             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35510             y : minY
35511         });
35512
35513         pos.push({
35514             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35515             y : minY + (this.unitWidth + this.gutter) * 2
35516         });
35517
35518         pos.push({
35519             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35520             y : minY + (this.unitWidth + this.gutter) * 2
35521         });
35522             
35523         return pos;
35524         
35525     },
35526     
35527     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35528     {
35529         var pos = [];
35530         
35531         if(box[0].size == 'xs'){
35532             
35533             pos.push({
35534                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35535                 y : minY
35536             });
35537
35538             pos.push({
35539                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35540                 y : minY
35541             });
35542             
35543             pos.push({
35544                 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),
35545                 y : minY
35546             });
35547             
35548             pos.push({
35549                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35550                 y : minY + (this.unitWidth + this.gutter) * 1
35551             });
35552             
35553             return pos;
35554             
35555         }
35556         
35557         pos.push({
35558             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35559             y : minY
35560         });
35561         
35562         pos.push({
35563             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35564             y : minY + (this.unitWidth + this.gutter) * 2
35565         });
35566         
35567         pos.push({
35568             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35569             y : minY + (this.unitWidth + this.gutter) * 2
35570         });
35571         
35572         pos.push({
35573             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),
35574             y : minY + (this.unitWidth + this.gutter) * 2
35575         });
35576
35577         return pos;
35578         
35579     },
35580     
35581     /**
35582     * remove a Masonry Brick
35583     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35584     */
35585     removeBrick : function(brick_id)
35586     {
35587         if (!brick_id) {
35588             return;
35589         }
35590         
35591         for (var i = 0; i<this.bricks.length; i++) {
35592             if (this.bricks[i].id == brick_id) {
35593                 this.bricks.splice(i,1);
35594                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35595                 this.initial();
35596             }
35597         }
35598     },
35599     
35600     /**
35601     * adds a Masonry Brick
35602     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35603     */
35604     addBrick : function(cfg)
35605     {
35606         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35607         //this.register(cn);
35608         cn.parentId = this.id;
35609         cn.render(this.el);
35610         return cn;
35611     },
35612     
35613     /**
35614     * register a Masonry Brick
35615     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35616     */
35617     
35618     register : function(brick)
35619     {
35620         this.bricks.push(brick);
35621         brick.masonryId = this.id;
35622     },
35623     
35624     /**
35625     * clear all the Masonry Brick
35626     */
35627     clearAll : function()
35628     {
35629         this.bricks = [];
35630         //this.getChildContainer().dom.innerHTML = "";
35631         this.el.dom.innerHTML = '';
35632     },
35633     
35634     getSelected : function()
35635     {
35636         if (!this.selectedBrick) {
35637             return false;
35638         }
35639         
35640         return this.selectedBrick;
35641     }
35642 });
35643
35644 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35645     
35646     groups: {},
35647      /**
35648     * register a Masonry Layout
35649     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35650     */
35651     
35652     register : function(layout)
35653     {
35654         this.groups[layout.id] = layout;
35655     },
35656     /**
35657     * fetch a  Masonry Layout based on the masonry layout ID
35658     * @param {string} the masonry layout to add
35659     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35660     */
35661     
35662     get: function(layout_id) {
35663         if (typeof(this.groups[layout_id]) == 'undefined') {
35664             return false;
35665         }
35666         return this.groups[layout_id] ;
35667     }
35668     
35669     
35670     
35671 });
35672
35673  
35674
35675  /**
35676  *
35677  * This is based on 
35678  * http://masonry.desandro.com
35679  *
35680  * The idea is to render all the bricks based on vertical width...
35681  *
35682  * The original code extends 'outlayer' - we might need to use that....
35683  * 
35684  */
35685
35686
35687 /**
35688  * @class Roo.bootstrap.LayoutMasonryAuto
35689  * @extends Roo.bootstrap.Component
35690  * Bootstrap Layout Masonry class
35691  * 
35692  * @constructor
35693  * Create a new Element
35694  * @param {Object} config The config object
35695  */
35696
35697 Roo.bootstrap.LayoutMasonryAuto = function(config){
35698     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35699 };
35700
35701 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35702     
35703       /**
35704      * @cfg {Boolean} isFitWidth  - resize the width..
35705      */   
35706     isFitWidth : false,  // options..
35707     /**
35708      * @cfg {Boolean} isOriginLeft = left align?
35709      */   
35710     isOriginLeft : true,
35711     /**
35712      * @cfg {Boolean} isOriginTop = top align?
35713      */   
35714     isOriginTop : false,
35715     /**
35716      * @cfg {Boolean} isLayoutInstant = no animation?
35717      */   
35718     isLayoutInstant : false, // needed?
35719     /**
35720      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35721      */   
35722     isResizingContainer : true,
35723     /**
35724      * @cfg {Number} columnWidth  width of the columns 
35725      */   
35726     
35727     columnWidth : 0,
35728     
35729     /**
35730      * @cfg {Number} maxCols maximum number of columns
35731      */   
35732     
35733     maxCols: 0,
35734     /**
35735      * @cfg {Number} padHeight padding below box..
35736      */   
35737     
35738     padHeight : 10, 
35739     
35740     /**
35741      * @cfg {Boolean} isAutoInitial defalut true
35742      */   
35743     
35744     isAutoInitial : true, 
35745     
35746     // private?
35747     gutter : 0,
35748     
35749     containerWidth: 0,
35750     initialColumnWidth : 0,
35751     currentSize : null,
35752     
35753     colYs : null, // array.
35754     maxY : 0,
35755     padWidth: 10,
35756     
35757     
35758     tag: 'div',
35759     cls: '',
35760     bricks: null, //CompositeElement
35761     cols : 0, // array?
35762     // element : null, // wrapped now this.el
35763     _isLayoutInited : null, 
35764     
35765     
35766     getAutoCreate : function(){
35767         
35768         var cfg = {
35769             tag: this.tag,
35770             cls: 'blog-masonary-wrapper ' + this.cls,
35771             cn : {
35772                 cls : 'mas-boxes masonary'
35773             }
35774         };
35775         
35776         return cfg;
35777     },
35778     
35779     getChildContainer: function( )
35780     {
35781         if (this.boxesEl) {
35782             return this.boxesEl;
35783         }
35784         
35785         this.boxesEl = this.el.select('.mas-boxes').first();
35786         
35787         return this.boxesEl;
35788     },
35789     
35790     
35791     initEvents : function()
35792     {
35793         var _this = this;
35794         
35795         if(this.isAutoInitial){
35796             Roo.log('hook children rendered');
35797             this.on('childrenrendered', function() {
35798                 Roo.log('children rendered');
35799                 _this.initial();
35800             } ,this);
35801         }
35802         
35803     },
35804     
35805     initial : function()
35806     {
35807         this.reloadItems();
35808
35809         this.currentSize = this.el.getBox(true);
35810
35811         /// was window resize... - let's see if this works..
35812         Roo.EventManager.onWindowResize(this.resize, this); 
35813
35814         if(!this.isAutoInitial){
35815             this.layout();
35816             return;
35817         }
35818         
35819         this.layout.defer(500,this);
35820     },
35821     
35822     reloadItems: function()
35823     {
35824         this.bricks = this.el.select('.masonry-brick', true);
35825         
35826         this.bricks.each(function(b) {
35827             //Roo.log(b.getSize());
35828             if (!b.attr('originalwidth')) {
35829                 b.attr('originalwidth',  b.getSize().width);
35830             }
35831             
35832         });
35833         
35834         Roo.log(this.bricks.elements.length);
35835     },
35836     
35837     resize : function()
35838     {
35839         Roo.log('resize');
35840         var cs = this.el.getBox(true);
35841         
35842         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35843             Roo.log("no change in with or X");
35844             return;
35845         }
35846         this.currentSize = cs;
35847         this.layout();
35848     },
35849     
35850     layout : function()
35851     {
35852          Roo.log('layout');
35853         this._resetLayout();
35854         //this._manageStamps();
35855       
35856         // don't animate first layout
35857         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35858         this.layoutItems( isInstant );
35859       
35860         // flag for initalized
35861         this._isLayoutInited = true;
35862     },
35863     
35864     layoutItems : function( isInstant )
35865     {
35866         //var items = this._getItemsForLayout( this.items );
35867         // original code supports filtering layout items.. we just ignore it..
35868         
35869         this._layoutItems( this.bricks , isInstant );
35870       
35871         this._postLayout();
35872     },
35873     _layoutItems : function ( items , isInstant)
35874     {
35875        //this.fireEvent( 'layout', this, items );
35876     
35877
35878         if ( !items || !items.elements.length ) {
35879           // no items, emit event with empty array
35880             return;
35881         }
35882
35883         var queue = [];
35884         items.each(function(item) {
35885             Roo.log("layout item");
35886             Roo.log(item);
35887             // get x/y object from method
35888             var position = this._getItemLayoutPosition( item );
35889             // enqueue
35890             position.item = item;
35891             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35892             queue.push( position );
35893         }, this);
35894       
35895         this._processLayoutQueue( queue );
35896     },
35897     /** Sets position of item in DOM
35898     * @param {Element} item
35899     * @param {Number} x - horizontal position
35900     * @param {Number} y - vertical position
35901     * @param {Boolean} isInstant - disables transitions
35902     */
35903     _processLayoutQueue : function( queue )
35904     {
35905         for ( var i=0, len = queue.length; i < len; i++ ) {
35906             var obj = queue[i];
35907             obj.item.position('absolute');
35908             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35909         }
35910     },
35911       
35912     
35913     /**
35914     * Any logic you want to do after each layout,
35915     * i.e. size the container
35916     */
35917     _postLayout : function()
35918     {
35919         this.resizeContainer();
35920     },
35921     
35922     resizeContainer : function()
35923     {
35924         if ( !this.isResizingContainer ) {
35925             return;
35926         }
35927         var size = this._getContainerSize();
35928         if ( size ) {
35929             this.el.setSize(size.width,size.height);
35930             this.boxesEl.setSize(size.width,size.height);
35931         }
35932     },
35933     
35934     
35935     
35936     _resetLayout : function()
35937     {
35938         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35939         this.colWidth = this.el.getWidth();
35940         //this.gutter = this.el.getWidth(); 
35941         
35942         this.measureColumns();
35943
35944         // reset column Y
35945         var i = this.cols;
35946         this.colYs = [];
35947         while (i--) {
35948             this.colYs.push( 0 );
35949         }
35950     
35951         this.maxY = 0;
35952     },
35953
35954     measureColumns : function()
35955     {
35956         this.getContainerWidth();
35957       // if columnWidth is 0, default to outerWidth of first item
35958         if ( !this.columnWidth ) {
35959             var firstItem = this.bricks.first();
35960             Roo.log(firstItem);
35961             this.columnWidth  = this.containerWidth;
35962             if (firstItem && firstItem.attr('originalwidth') ) {
35963                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35964             }
35965             // columnWidth fall back to item of first element
35966             Roo.log("set column width?");
35967                         this.initialColumnWidth = this.columnWidth  ;
35968
35969             // if first elem has no width, default to size of container
35970             
35971         }
35972         
35973         
35974         if (this.initialColumnWidth) {
35975             this.columnWidth = this.initialColumnWidth;
35976         }
35977         
35978         
35979             
35980         // column width is fixed at the top - however if container width get's smaller we should
35981         // reduce it...
35982         
35983         // this bit calcs how man columns..
35984             
35985         var columnWidth = this.columnWidth += this.gutter;
35986       
35987         // calculate columns
35988         var containerWidth = this.containerWidth + this.gutter;
35989         
35990         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35991         // fix rounding errors, typically with gutters
35992         var excess = columnWidth - containerWidth % columnWidth;
35993         
35994         
35995         // if overshoot is less than a pixel, round up, otherwise floor it
35996         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35997         cols = Math[ mathMethod ]( cols );
35998         this.cols = Math.max( cols, 1 );
35999         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36000         
36001          // padding positioning..
36002         var totalColWidth = this.cols * this.columnWidth;
36003         var padavail = this.containerWidth - totalColWidth;
36004         // so for 2 columns - we need 3 'pads'
36005         
36006         var padNeeded = (1+this.cols) * this.padWidth;
36007         
36008         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36009         
36010         this.columnWidth += padExtra
36011         //this.padWidth = Math.floor(padavail /  ( this.cols));
36012         
36013         // adjust colum width so that padding is fixed??
36014         
36015         // we have 3 columns ... total = width * 3
36016         // we have X left over... that should be used by 
36017         
36018         //if (this.expandC) {
36019             
36020         //}
36021         
36022         
36023         
36024     },
36025     
36026     getContainerWidth : function()
36027     {
36028        /* // container is parent if fit width
36029         var container = this.isFitWidth ? this.element.parentNode : this.element;
36030         // check that this.size and size are there
36031         // IE8 triggers resize on body size change, so they might not be
36032         
36033         var size = getSize( container );  //FIXME
36034         this.containerWidth = size && size.innerWidth; //FIXME
36035         */
36036          
36037         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36038         
36039     },
36040     
36041     _getItemLayoutPosition : function( item )  // what is item?
36042     {
36043         // we resize the item to our columnWidth..
36044       
36045         item.setWidth(this.columnWidth);
36046         item.autoBoxAdjust  = false;
36047         
36048         var sz = item.getSize();
36049  
36050         // how many columns does this brick span
36051         var remainder = this.containerWidth % this.columnWidth;
36052         
36053         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36054         // round if off by 1 pixel, otherwise use ceil
36055         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36056         colSpan = Math.min( colSpan, this.cols );
36057         
36058         // normally this should be '1' as we dont' currently allow multi width columns..
36059         
36060         var colGroup = this._getColGroup( colSpan );
36061         // get the minimum Y value from the columns
36062         var minimumY = Math.min.apply( Math, colGroup );
36063         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36064         
36065         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36066          
36067         // position the brick
36068         var position = {
36069             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36070             y: this.currentSize.y + minimumY + this.padHeight
36071         };
36072         
36073         Roo.log(position);
36074         // apply setHeight to necessary columns
36075         var setHeight = minimumY + sz.height + this.padHeight;
36076         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36077         
36078         var setSpan = this.cols + 1 - colGroup.length;
36079         for ( var i = 0; i < setSpan; i++ ) {
36080           this.colYs[ shortColIndex + i ] = setHeight ;
36081         }
36082       
36083         return position;
36084     },
36085     
36086     /**
36087      * @param {Number} colSpan - number of columns the element spans
36088      * @returns {Array} colGroup
36089      */
36090     _getColGroup : function( colSpan )
36091     {
36092         if ( colSpan < 2 ) {
36093           // if brick spans only one column, use all the column Ys
36094           return this.colYs;
36095         }
36096       
36097         var colGroup = [];
36098         // how many different places could this brick fit horizontally
36099         var groupCount = this.cols + 1 - colSpan;
36100         // for each group potential horizontal position
36101         for ( var i = 0; i < groupCount; i++ ) {
36102           // make an array of colY values for that one group
36103           var groupColYs = this.colYs.slice( i, i + colSpan );
36104           // and get the max value of the array
36105           colGroup[i] = Math.max.apply( Math, groupColYs );
36106         }
36107         return colGroup;
36108     },
36109     /*
36110     _manageStamp : function( stamp )
36111     {
36112         var stampSize =  stamp.getSize();
36113         var offset = stamp.getBox();
36114         // get the columns that this stamp affects
36115         var firstX = this.isOriginLeft ? offset.x : offset.right;
36116         var lastX = firstX + stampSize.width;
36117         var firstCol = Math.floor( firstX / this.columnWidth );
36118         firstCol = Math.max( 0, firstCol );
36119         
36120         var lastCol = Math.floor( lastX / this.columnWidth );
36121         // lastCol should not go over if multiple of columnWidth #425
36122         lastCol -= lastX % this.columnWidth ? 0 : 1;
36123         lastCol = Math.min( this.cols - 1, lastCol );
36124         
36125         // set colYs to bottom of the stamp
36126         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36127             stampSize.height;
36128             
36129         for ( var i = firstCol; i <= lastCol; i++ ) {
36130           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36131         }
36132     },
36133     */
36134     
36135     _getContainerSize : function()
36136     {
36137         this.maxY = Math.max.apply( Math, this.colYs );
36138         var size = {
36139             height: this.maxY
36140         };
36141       
36142         if ( this.isFitWidth ) {
36143             size.width = this._getContainerFitWidth();
36144         }
36145       
36146         return size;
36147     },
36148     
36149     _getContainerFitWidth : function()
36150     {
36151         var unusedCols = 0;
36152         // count unused columns
36153         var i = this.cols;
36154         while ( --i ) {
36155           if ( this.colYs[i] !== 0 ) {
36156             break;
36157           }
36158           unusedCols++;
36159         }
36160         // fit container to columns that have been used
36161         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36162     },
36163     
36164     needsResizeLayout : function()
36165     {
36166         var previousWidth = this.containerWidth;
36167         this.getContainerWidth();
36168         return previousWidth !== this.containerWidth;
36169     }
36170  
36171 });
36172
36173  
36174
36175  /*
36176  * - LGPL
36177  *
36178  * element
36179  * 
36180  */
36181
36182 /**
36183  * @class Roo.bootstrap.MasonryBrick
36184  * @extends Roo.bootstrap.Component
36185  * Bootstrap MasonryBrick class
36186  * 
36187  * @constructor
36188  * Create a new MasonryBrick
36189  * @param {Object} config The config object
36190  */
36191
36192 Roo.bootstrap.MasonryBrick = function(config){
36193     
36194     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36195     
36196     Roo.bootstrap.MasonryBrick.register(this);
36197     
36198     this.addEvents({
36199         // raw events
36200         /**
36201          * @event click
36202          * When a MasonryBrick is clcik
36203          * @param {Roo.bootstrap.MasonryBrick} this
36204          * @param {Roo.EventObject} e
36205          */
36206         "click" : true
36207     });
36208 };
36209
36210 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36211     
36212     /**
36213      * @cfg {String} title
36214      */   
36215     title : '',
36216     /**
36217      * @cfg {String} html
36218      */   
36219     html : '',
36220     /**
36221      * @cfg {String} bgimage
36222      */   
36223     bgimage : '',
36224     /**
36225      * @cfg {String} videourl
36226      */   
36227     videourl : '',
36228     /**
36229      * @cfg {String} cls
36230      */   
36231     cls : '',
36232     /**
36233      * @cfg {String} href
36234      */   
36235     href : '',
36236     /**
36237      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36238      */   
36239     size : 'xs',
36240     
36241     /**
36242      * @cfg {String} placetitle (center|bottom)
36243      */   
36244     placetitle : '',
36245     
36246     /**
36247      * @cfg {Boolean} isFitContainer defalut true
36248      */   
36249     isFitContainer : true, 
36250     
36251     /**
36252      * @cfg {Boolean} preventDefault defalut false
36253      */   
36254     preventDefault : false, 
36255     
36256     /**
36257      * @cfg {Boolean} inverse defalut false
36258      */   
36259     maskInverse : false, 
36260     
36261     getAutoCreate : function()
36262     {
36263         if(!this.isFitContainer){
36264             return this.getSplitAutoCreate();
36265         }
36266         
36267         var cls = 'masonry-brick masonry-brick-full';
36268         
36269         if(this.href.length){
36270             cls += ' masonry-brick-link';
36271         }
36272         
36273         if(this.bgimage.length){
36274             cls += ' masonry-brick-image';
36275         }
36276         
36277         if(this.maskInverse){
36278             cls += ' mask-inverse';
36279         }
36280         
36281         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36282             cls += ' enable-mask';
36283         }
36284         
36285         if(this.size){
36286             cls += ' masonry-' + this.size + '-brick';
36287         }
36288         
36289         if(this.placetitle.length){
36290             
36291             switch (this.placetitle) {
36292                 case 'center' :
36293                     cls += ' masonry-center-title';
36294                     break;
36295                 case 'bottom' :
36296                     cls += ' masonry-bottom-title';
36297                     break;
36298                 default:
36299                     break;
36300             }
36301             
36302         } else {
36303             if(!this.html.length && !this.bgimage.length){
36304                 cls += ' masonry-center-title';
36305             }
36306
36307             if(!this.html.length && this.bgimage.length){
36308                 cls += ' masonry-bottom-title';
36309             }
36310         }
36311         
36312         if(this.cls){
36313             cls += ' ' + this.cls;
36314         }
36315         
36316         var cfg = {
36317             tag: (this.href.length) ? 'a' : 'div',
36318             cls: cls,
36319             cn: [
36320                 {
36321                     tag: 'div',
36322                     cls: 'masonry-brick-mask'
36323                 },
36324                 {
36325                     tag: 'div',
36326                     cls: 'masonry-brick-paragraph',
36327                     cn: []
36328                 }
36329             ]
36330         };
36331         
36332         if(this.href.length){
36333             cfg.href = this.href;
36334         }
36335         
36336         var cn = cfg.cn[1].cn;
36337         
36338         if(this.title.length){
36339             cn.push({
36340                 tag: 'h4',
36341                 cls: 'masonry-brick-title',
36342                 html: this.title
36343             });
36344         }
36345         
36346         if(this.html.length){
36347             cn.push({
36348                 tag: 'p',
36349                 cls: 'masonry-brick-text',
36350                 html: this.html
36351             });
36352         }
36353         
36354         if (!this.title.length && !this.html.length) {
36355             cfg.cn[1].cls += ' hide';
36356         }
36357         
36358         if(this.bgimage.length){
36359             cfg.cn.push({
36360                 tag: 'img',
36361                 cls: 'masonry-brick-image-view',
36362                 src: this.bgimage
36363             });
36364         }
36365         
36366         if(this.videourl.length){
36367             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36368             // youtube support only?
36369             cfg.cn.push({
36370                 tag: 'iframe',
36371                 cls: 'masonry-brick-image-view',
36372                 src: vurl,
36373                 frameborder : 0,
36374                 allowfullscreen : true
36375             });
36376         }
36377         
36378         return cfg;
36379         
36380     },
36381     
36382     getSplitAutoCreate : function()
36383     {
36384         var cls = 'masonry-brick masonry-brick-split';
36385         
36386         if(this.href.length){
36387             cls += ' masonry-brick-link';
36388         }
36389         
36390         if(this.bgimage.length){
36391             cls += ' masonry-brick-image';
36392         }
36393         
36394         if(this.size){
36395             cls += ' masonry-' + this.size + '-brick';
36396         }
36397         
36398         switch (this.placetitle) {
36399             case 'center' :
36400                 cls += ' masonry-center-title';
36401                 break;
36402             case 'bottom' :
36403                 cls += ' masonry-bottom-title';
36404                 break;
36405             default:
36406                 if(!this.bgimage.length){
36407                     cls += ' masonry-center-title';
36408                 }
36409
36410                 if(this.bgimage.length){
36411                     cls += ' masonry-bottom-title';
36412                 }
36413                 break;
36414         }
36415         
36416         if(this.cls){
36417             cls += ' ' + this.cls;
36418         }
36419         
36420         var cfg = {
36421             tag: (this.href.length) ? 'a' : 'div',
36422             cls: cls,
36423             cn: [
36424                 {
36425                     tag: 'div',
36426                     cls: 'masonry-brick-split-head',
36427                     cn: [
36428                         {
36429                             tag: 'div',
36430                             cls: 'masonry-brick-paragraph',
36431                             cn: []
36432                         }
36433                     ]
36434                 },
36435                 {
36436                     tag: 'div',
36437                     cls: 'masonry-brick-split-body',
36438                     cn: []
36439                 }
36440             ]
36441         };
36442         
36443         if(this.href.length){
36444             cfg.href = this.href;
36445         }
36446         
36447         if(this.title.length){
36448             cfg.cn[0].cn[0].cn.push({
36449                 tag: 'h4',
36450                 cls: 'masonry-brick-title',
36451                 html: this.title
36452             });
36453         }
36454         
36455         if(this.html.length){
36456             cfg.cn[1].cn.push({
36457                 tag: 'p',
36458                 cls: 'masonry-brick-text',
36459                 html: this.html
36460             });
36461         }
36462
36463         if(this.bgimage.length){
36464             cfg.cn[0].cn.push({
36465                 tag: 'img',
36466                 cls: 'masonry-brick-image-view',
36467                 src: this.bgimage
36468             });
36469         }
36470         
36471         if(this.videourl.length){
36472             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36473             // youtube support only?
36474             cfg.cn[0].cn.cn.push({
36475                 tag: 'iframe',
36476                 cls: 'masonry-brick-image-view',
36477                 src: vurl,
36478                 frameborder : 0,
36479                 allowfullscreen : true
36480             });
36481         }
36482         
36483         return cfg;
36484     },
36485     
36486     initEvents: function() 
36487     {
36488         switch (this.size) {
36489             case 'xs' :
36490                 this.x = 1;
36491                 this.y = 1;
36492                 break;
36493             case 'sm' :
36494                 this.x = 2;
36495                 this.y = 2;
36496                 break;
36497             case 'md' :
36498             case 'md-left' :
36499             case 'md-right' :
36500                 this.x = 3;
36501                 this.y = 3;
36502                 break;
36503             case 'tall' :
36504                 this.x = 2;
36505                 this.y = 3;
36506                 break;
36507             case 'wide' :
36508                 this.x = 3;
36509                 this.y = 2;
36510                 break;
36511             case 'wide-thin' :
36512                 this.x = 3;
36513                 this.y = 1;
36514                 break;
36515                         
36516             default :
36517                 break;
36518         }
36519         
36520         if(Roo.isTouch){
36521             this.el.on('touchstart', this.onTouchStart, this);
36522             this.el.on('touchmove', this.onTouchMove, this);
36523             this.el.on('touchend', this.onTouchEnd, this);
36524             this.el.on('contextmenu', this.onContextMenu, this);
36525         } else {
36526             this.el.on('mouseenter'  ,this.enter, this);
36527             this.el.on('mouseleave', this.leave, this);
36528             this.el.on('click', this.onClick, this);
36529         }
36530         
36531         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36532             this.parent().bricks.push(this);   
36533         }
36534         
36535     },
36536     
36537     onClick: function(e, el)
36538     {
36539         var time = this.endTimer - this.startTimer;
36540         // Roo.log(e.preventDefault());
36541         if(Roo.isTouch){
36542             if(time > 1000){
36543                 e.preventDefault();
36544                 return;
36545             }
36546         }
36547         
36548         if(!this.preventDefault){
36549             return;
36550         }
36551         
36552         e.preventDefault();
36553         
36554         if (this.activeClass != '') {
36555             this.selectBrick();
36556         }
36557         
36558         this.fireEvent('click', this, e);
36559     },
36560     
36561     enter: function(e, el)
36562     {
36563         e.preventDefault();
36564         
36565         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36566             return;
36567         }
36568         
36569         if(this.bgimage.length && this.html.length){
36570             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36571         }
36572     },
36573     
36574     leave: function(e, el)
36575     {
36576         e.preventDefault();
36577         
36578         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36579             return;
36580         }
36581         
36582         if(this.bgimage.length && this.html.length){
36583             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36584         }
36585     },
36586     
36587     onTouchStart: function(e, el)
36588     {
36589 //        e.preventDefault();
36590         
36591         this.touchmoved = false;
36592         
36593         if(!this.isFitContainer){
36594             return;
36595         }
36596         
36597         if(!this.bgimage.length || !this.html.length){
36598             return;
36599         }
36600         
36601         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36602         
36603         this.timer = new Date().getTime();
36604         
36605     },
36606     
36607     onTouchMove: function(e, el)
36608     {
36609         this.touchmoved = true;
36610     },
36611     
36612     onContextMenu : function(e,el)
36613     {
36614         e.preventDefault();
36615         e.stopPropagation();
36616         return false;
36617     },
36618     
36619     onTouchEnd: function(e, el)
36620     {
36621 //        e.preventDefault();
36622         
36623         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36624         
36625             this.leave(e,el);
36626             
36627             return;
36628         }
36629         
36630         if(!this.bgimage.length || !this.html.length){
36631             
36632             if(this.href.length){
36633                 window.location.href = this.href;
36634             }
36635             
36636             return;
36637         }
36638         
36639         if(!this.isFitContainer){
36640             return;
36641         }
36642         
36643         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36644         
36645         window.location.href = this.href;
36646     },
36647     
36648     //selection on single brick only
36649     selectBrick : function() {
36650         
36651         if (!this.parentId) {
36652             return;
36653         }
36654         
36655         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36656         var index = m.selectedBrick.indexOf(this.id);
36657         
36658         if ( index > -1) {
36659             m.selectedBrick.splice(index,1);
36660             this.el.removeClass(this.activeClass);
36661             return;
36662         }
36663         
36664         for(var i = 0; i < m.selectedBrick.length; i++) {
36665             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36666             b.el.removeClass(b.activeClass);
36667         }
36668         
36669         m.selectedBrick = [];
36670         
36671         m.selectedBrick.push(this.id);
36672         this.el.addClass(this.activeClass);
36673         return;
36674     },
36675     
36676     isSelected : function(){
36677         return this.el.hasClass(this.activeClass);
36678         
36679     }
36680 });
36681
36682 Roo.apply(Roo.bootstrap.MasonryBrick, {
36683     
36684     //groups: {},
36685     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36686      /**
36687     * register a Masonry Brick
36688     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36689     */
36690     
36691     register : function(brick)
36692     {
36693         //this.groups[brick.id] = brick;
36694         this.groups.add(brick.id, brick);
36695     },
36696     /**
36697     * fetch a  masonry brick based on the masonry brick ID
36698     * @param {string} the masonry brick to add
36699     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36700     */
36701     
36702     get: function(brick_id) 
36703     {
36704         // if (typeof(this.groups[brick_id]) == 'undefined') {
36705         //     return false;
36706         // }
36707         // return this.groups[brick_id] ;
36708         
36709         if(this.groups.key(brick_id)) {
36710             return this.groups.key(brick_id);
36711         }
36712         
36713         return false;
36714     }
36715     
36716     
36717     
36718 });
36719
36720  /*
36721  * - LGPL
36722  *
36723  * element
36724  * 
36725  */
36726
36727 /**
36728  * @class Roo.bootstrap.Brick
36729  * @extends Roo.bootstrap.Component
36730  * Bootstrap Brick class
36731  * 
36732  * @constructor
36733  * Create a new Brick
36734  * @param {Object} config The config object
36735  */
36736
36737 Roo.bootstrap.Brick = function(config){
36738     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36739     
36740     this.addEvents({
36741         // raw events
36742         /**
36743          * @event click
36744          * When a Brick is click
36745          * @param {Roo.bootstrap.Brick} this
36746          * @param {Roo.EventObject} e
36747          */
36748         "click" : true
36749     });
36750 };
36751
36752 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36753     
36754     /**
36755      * @cfg {String} title
36756      */   
36757     title : '',
36758     /**
36759      * @cfg {String} html
36760      */   
36761     html : '',
36762     /**
36763      * @cfg {String} bgimage
36764      */   
36765     bgimage : '',
36766     /**
36767      * @cfg {String} cls
36768      */   
36769     cls : '',
36770     /**
36771      * @cfg {String} href
36772      */   
36773     href : '',
36774     /**
36775      * @cfg {String} video
36776      */   
36777     video : '',
36778     /**
36779      * @cfg {Boolean} square
36780      */   
36781     square : true,
36782     
36783     getAutoCreate : function()
36784     {
36785         var cls = 'roo-brick';
36786         
36787         if(this.href.length){
36788             cls += ' roo-brick-link';
36789         }
36790         
36791         if(this.bgimage.length){
36792             cls += ' roo-brick-image';
36793         }
36794         
36795         if(!this.html.length && !this.bgimage.length){
36796             cls += ' roo-brick-center-title';
36797         }
36798         
36799         if(!this.html.length && this.bgimage.length){
36800             cls += ' roo-brick-bottom-title';
36801         }
36802         
36803         if(this.cls){
36804             cls += ' ' + this.cls;
36805         }
36806         
36807         var cfg = {
36808             tag: (this.href.length) ? 'a' : 'div',
36809             cls: cls,
36810             cn: [
36811                 {
36812                     tag: 'div',
36813                     cls: 'roo-brick-paragraph',
36814                     cn: []
36815                 }
36816             ]
36817         };
36818         
36819         if(this.href.length){
36820             cfg.href = this.href;
36821         }
36822         
36823         var cn = cfg.cn[0].cn;
36824         
36825         if(this.title.length){
36826             cn.push({
36827                 tag: 'h4',
36828                 cls: 'roo-brick-title',
36829                 html: this.title
36830             });
36831         }
36832         
36833         if(this.html.length){
36834             cn.push({
36835                 tag: 'p',
36836                 cls: 'roo-brick-text',
36837                 html: this.html
36838             });
36839         } else {
36840             cn.cls += ' hide';
36841         }
36842         
36843         if(this.bgimage.length){
36844             cfg.cn.push({
36845                 tag: 'img',
36846                 cls: 'roo-brick-image-view',
36847                 src: this.bgimage
36848             });
36849         }
36850         
36851         return cfg;
36852     },
36853     
36854     initEvents: function() 
36855     {
36856         if(this.title.length || this.html.length){
36857             this.el.on('mouseenter'  ,this.enter, this);
36858             this.el.on('mouseleave', this.leave, this);
36859         }
36860         
36861         Roo.EventManager.onWindowResize(this.resize, this); 
36862         
36863         if(this.bgimage.length){
36864             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36865             this.imageEl.on('load', this.onImageLoad, this);
36866             return;
36867         }
36868         
36869         this.resize();
36870     },
36871     
36872     onImageLoad : function()
36873     {
36874         this.resize();
36875     },
36876     
36877     resize : function()
36878     {
36879         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36880         
36881         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36882         
36883         if(this.bgimage.length){
36884             var image = this.el.select('.roo-brick-image-view', true).first();
36885             
36886             image.setWidth(paragraph.getWidth());
36887             
36888             if(this.square){
36889                 image.setHeight(paragraph.getWidth());
36890             }
36891             
36892             this.el.setHeight(image.getHeight());
36893             paragraph.setHeight(image.getHeight());
36894             
36895         }
36896         
36897     },
36898     
36899     enter: function(e, el)
36900     {
36901         e.preventDefault();
36902         
36903         if(this.bgimage.length){
36904             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36905             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36906         }
36907     },
36908     
36909     leave: function(e, el)
36910     {
36911         e.preventDefault();
36912         
36913         if(this.bgimage.length){
36914             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36915             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36916         }
36917     }
36918     
36919 });
36920
36921  
36922
36923  /*
36924  * - LGPL
36925  *
36926  * Number field 
36927  */
36928
36929 /**
36930  * @class Roo.bootstrap.NumberField
36931  * @extends Roo.bootstrap.Input
36932  * Bootstrap NumberField class
36933  * 
36934  * 
36935  * 
36936  * 
36937  * @constructor
36938  * Create a new NumberField
36939  * @param {Object} config The config object
36940  */
36941
36942 Roo.bootstrap.NumberField = function(config){
36943     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36944 };
36945
36946 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36947     
36948     /**
36949      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36950      */
36951     allowDecimals : true,
36952     /**
36953      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36954      */
36955     decimalSeparator : ".",
36956     /**
36957      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36958      */
36959     decimalPrecision : 2,
36960     /**
36961      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36962      */
36963     allowNegative : true,
36964     
36965     /**
36966      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36967      */
36968     allowZero: true,
36969     /**
36970      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36971      */
36972     minValue : Number.NEGATIVE_INFINITY,
36973     /**
36974      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36975      */
36976     maxValue : Number.MAX_VALUE,
36977     /**
36978      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36979      */
36980     minText : "The minimum value for this field is {0}",
36981     /**
36982      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36983      */
36984     maxText : "The maximum value for this field is {0}",
36985     /**
36986      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36987      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36988      */
36989     nanText : "{0} is not a valid number",
36990     /**
36991      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36992      */
36993     thousandsDelimiter : false,
36994     /**
36995      * @cfg {String} valueAlign alignment of value
36996      */
36997     valueAlign : "left",
36998
36999     getAutoCreate : function()
37000     {
37001         var hiddenInput = {
37002             tag: 'input',
37003             type: 'hidden',
37004             id: Roo.id(),
37005             cls: 'hidden-number-input'
37006         };
37007         
37008         if (this.name) {
37009             hiddenInput.name = this.name;
37010         }
37011         
37012         this.name = '';
37013         
37014         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37015         
37016         this.name = hiddenInput.name;
37017         
37018         if(cfg.cn.length > 0) {
37019             cfg.cn.push(hiddenInput);
37020         }
37021         
37022         return cfg;
37023     },
37024
37025     // private
37026     initEvents : function()
37027     {   
37028         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37029         
37030         var allowed = "0123456789";
37031         
37032         if(this.allowDecimals){
37033             allowed += this.decimalSeparator;
37034         }
37035         
37036         if(this.allowNegative){
37037             allowed += "-";
37038         }
37039         
37040         if(this.thousandsDelimiter) {
37041             allowed += ",";
37042         }
37043         
37044         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37045         
37046         var keyPress = function(e){
37047             
37048             var k = e.getKey();
37049             
37050             var c = e.getCharCode();
37051             
37052             if(
37053                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37054                     allowed.indexOf(String.fromCharCode(c)) === -1
37055             ){
37056                 e.stopEvent();
37057                 return;
37058             }
37059             
37060             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37061                 return;
37062             }
37063             
37064             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37065                 e.stopEvent();
37066             }
37067         };
37068         
37069         this.el.on("keypress", keyPress, this);
37070     },
37071     
37072     validateValue : function(value)
37073     {
37074         
37075         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37076             return false;
37077         }
37078         
37079         var num = this.parseValue(value);
37080         
37081         if(isNaN(num)){
37082             this.markInvalid(String.format(this.nanText, value));
37083             return false;
37084         }
37085         
37086         if(num < this.minValue){
37087             this.markInvalid(String.format(this.minText, this.minValue));
37088             return false;
37089         }
37090         
37091         if(num > this.maxValue){
37092             this.markInvalid(String.format(this.maxText, this.maxValue));
37093             return false;
37094         }
37095         
37096         return true;
37097     },
37098
37099     getValue : function()
37100     {
37101         var v = this.hiddenEl().getValue();
37102         
37103         return this.fixPrecision(this.parseValue(v));
37104     },
37105
37106     parseValue : function(value)
37107     {
37108         if(this.thousandsDelimiter) {
37109             value += "";
37110             r = new RegExp(",", "g");
37111             value = value.replace(r, "");
37112         }
37113         
37114         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37115         return isNaN(value) ? '' : value;
37116     },
37117
37118     fixPrecision : function(value)
37119     {
37120         if(this.thousandsDelimiter) {
37121             value += "";
37122             r = new RegExp(",", "g");
37123             value = value.replace(r, "");
37124         }
37125         
37126         var nan = isNaN(value);
37127         
37128         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37129             return nan ? '' : value;
37130         }
37131         return parseFloat(value).toFixed(this.decimalPrecision);
37132     },
37133
37134     setValue : function(v)
37135     {
37136         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37137         
37138         this.value = v;
37139         
37140         if(this.rendered){
37141             
37142             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37143             
37144             this.inputEl().dom.value = (v == '') ? '' :
37145                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37146             
37147             if(!this.allowZero && v === '0') {
37148                 this.hiddenEl().dom.value = '';
37149                 this.inputEl().dom.value = '';
37150             }
37151             
37152             this.validate();
37153         }
37154     },
37155
37156     decimalPrecisionFcn : function(v)
37157     {
37158         return Math.floor(v);
37159     },
37160
37161     beforeBlur : function()
37162     {
37163         var v = this.parseValue(this.getRawValue());
37164         
37165         if(v || v === 0 || v === ''){
37166             this.setValue(v);
37167         }
37168     },
37169     
37170     hiddenEl : function()
37171     {
37172         return this.el.select('input.hidden-number-input',true).first();
37173     }
37174     
37175 });
37176
37177  
37178
37179 /*
37180 * Licence: LGPL
37181 */
37182
37183 /**
37184  * @class Roo.bootstrap.DocumentSlider
37185  * @extends Roo.bootstrap.Component
37186  * Bootstrap DocumentSlider class
37187  * 
37188  * @constructor
37189  * Create a new DocumentViewer
37190  * @param {Object} config The config object
37191  */
37192
37193 Roo.bootstrap.DocumentSlider = function(config){
37194     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37195     
37196     this.files = [];
37197     
37198     this.addEvents({
37199         /**
37200          * @event initial
37201          * Fire after initEvent
37202          * @param {Roo.bootstrap.DocumentSlider} this
37203          */
37204         "initial" : true,
37205         /**
37206          * @event update
37207          * Fire after update
37208          * @param {Roo.bootstrap.DocumentSlider} this
37209          */
37210         "update" : true,
37211         /**
37212          * @event click
37213          * Fire after click
37214          * @param {Roo.bootstrap.DocumentSlider} this
37215          */
37216         "click" : true
37217     });
37218 };
37219
37220 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37221     
37222     files : false,
37223     
37224     indicator : 0,
37225     
37226     getAutoCreate : function()
37227     {
37228         var cfg = {
37229             tag : 'div',
37230             cls : 'roo-document-slider',
37231             cn : [
37232                 {
37233                     tag : 'div',
37234                     cls : 'roo-document-slider-header',
37235                     cn : [
37236                         {
37237                             tag : 'div',
37238                             cls : 'roo-document-slider-header-title'
37239                         }
37240                     ]
37241                 },
37242                 {
37243                     tag : 'div',
37244                     cls : 'roo-document-slider-body',
37245                     cn : [
37246                         {
37247                             tag : 'div',
37248                             cls : 'roo-document-slider-prev',
37249                             cn : [
37250                                 {
37251                                     tag : 'i',
37252                                     cls : 'fa fa-chevron-left'
37253                                 }
37254                             ]
37255                         },
37256                         {
37257                             tag : 'div',
37258                             cls : 'roo-document-slider-thumb',
37259                             cn : [
37260                                 {
37261                                     tag : 'img',
37262                                     cls : 'roo-document-slider-image'
37263                                 }
37264                             ]
37265                         },
37266                         {
37267                             tag : 'div',
37268                             cls : 'roo-document-slider-next',
37269                             cn : [
37270                                 {
37271                                     tag : 'i',
37272                                     cls : 'fa fa-chevron-right'
37273                                 }
37274                             ]
37275                         }
37276                     ]
37277                 }
37278             ]
37279         };
37280         
37281         return cfg;
37282     },
37283     
37284     initEvents : function()
37285     {
37286         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37287         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37288         
37289         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37290         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37291         
37292         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37293         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37294         
37295         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37296         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37297         
37298         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37299         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37300         
37301         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37302         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37303         
37304         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37305         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37306         
37307         this.thumbEl.on('click', this.onClick, this);
37308         
37309         this.prevIndicator.on('click', this.prev, this);
37310         
37311         this.nextIndicator.on('click', this.next, this);
37312         
37313     },
37314     
37315     initial : function()
37316     {
37317         if(this.files.length){
37318             this.indicator = 1;
37319             this.update()
37320         }
37321         
37322         this.fireEvent('initial', this);
37323     },
37324     
37325     update : function()
37326     {
37327         this.imageEl.attr('src', this.files[this.indicator - 1]);
37328         
37329         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37330         
37331         this.prevIndicator.show();
37332         
37333         if(this.indicator == 1){
37334             this.prevIndicator.hide();
37335         }
37336         
37337         this.nextIndicator.show();
37338         
37339         if(this.indicator == this.files.length){
37340             this.nextIndicator.hide();
37341         }
37342         
37343         this.thumbEl.scrollTo('top');
37344         
37345         this.fireEvent('update', this);
37346     },
37347     
37348     onClick : function(e)
37349     {
37350         e.preventDefault();
37351         
37352         this.fireEvent('click', this);
37353     },
37354     
37355     prev : function(e)
37356     {
37357         e.preventDefault();
37358         
37359         this.indicator = Math.max(1, this.indicator - 1);
37360         
37361         this.update();
37362     },
37363     
37364     next : function(e)
37365     {
37366         e.preventDefault();
37367         
37368         this.indicator = Math.min(this.files.length, this.indicator + 1);
37369         
37370         this.update();
37371     }
37372 });
37373 /*
37374  * - LGPL
37375  *
37376  * RadioSet
37377  *
37378  *
37379  */
37380
37381 /**
37382  * @class Roo.bootstrap.RadioSet
37383  * @extends Roo.bootstrap.Input
37384  * Bootstrap RadioSet class
37385  * @cfg {String} indicatorpos (left|right) default left
37386  * @cfg {Boolean} inline (true|false) inline the element (default true)
37387  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37388  * @constructor
37389  * Create a new RadioSet
37390  * @param {Object} config The config object
37391  */
37392
37393 Roo.bootstrap.RadioSet = function(config){
37394     
37395     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37396     
37397     this.radioes = [];
37398     
37399     Roo.bootstrap.RadioSet.register(this);
37400     
37401     this.addEvents({
37402         /**
37403         * @event check
37404         * Fires when the element is checked or unchecked.
37405         * @param {Roo.bootstrap.RadioSet} this This radio
37406         * @param {Roo.bootstrap.Radio} item The checked item
37407         */
37408        check : true,
37409        /**
37410         * @event click
37411         * Fires when the element is click.
37412         * @param {Roo.bootstrap.RadioSet} this This radio set
37413         * @param {Roo.bootstrap.Radio} item The checked item
37414         * @param {Roo.EventObject} e The event object
37415         */
37416        click : true
37417     });
37418     
37419 };
37420
37421 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37422
37423     radioes : false,
37424     
37425     inline : true,
37426     
37427     weight : '',
37428     
37429     indicatorpos : 'left',
37430     
37431     getAutoCreate : function()
37432     {
37433         var label = {
37434             tag : 'label',
37435             cls : 'roo-radio-set-label',
37436             cn : [
37437                 {
37438                     tag : 'span',
37439                     html : this.fieldLabel
37440                 }
37441             ]
37442         };
37443         if (Roo.bootstrap.version == 3) {
37444             
37445             
37446             if(this.indicatorpos == 'left'){
37447                 label.cn.unshift({
37448                     tag : 'i',
37449                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37450                     tooltip : 'This field is required'
37451                 });
37452             } else {
37453                 label.cn.push({
37454                     tag : 'i',
37455                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37456                     tooltip : 'This field is required'
37457                 });
37458             }
37459         }
37460         var items = {
37461             tag : 'div',
37462             cls : 'roo-radio-set-items'
37463         };
37464         
37465         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37466         
37467         if (align === 'left' && this.fieldLabel.length) {
37468             
37469             items = {
37470                 cls : "roo-radio-set-right", 
37471                 cn: [
37472                     items
37473                 ]
37474             };
37475             
37476             if(this.labelWidth > 12){
37477                 label.style = "width: " + this.labelWidth + 'px';
37478             }
37479             
37480             if(this.labelWidth < 13 && this.labelmd == 0){
37481                 this.labelmd = this.labelWidth;
37482             }
37483             
37484             if(this.labellg > 0){
37485                 label.cls += ' col-lg-' + this.labellg;
37486                 items.cls += ' col-lg-' + (12 - this.labellg);
37487             }
37488             
37489             if(this.labelmd > 0){
37490                 label.cls += ' col-md-' + this.labelmd;
37491                 items.cls += ' col-md-' + (12 - this.labelmd);
37492             }
37493             
37494             if(this.labelsm > 0){
37495                 label.cls += ' col-sm-' + this.labelsm;
37496                 items.cls += ' col-sm-' + (12 - this.labelsm);
37497             }
37498             
37499             if(this.labelxs > 0){
37500                 label.cls += ' col-xs-' + this.labelxs;
37501                 items.cls += ' col-xs-' + (12 - this.labelxs);
37502             }
37503         }
37504         
37505         var cfg = {
37506             tag : 'div',
37507             cls : 'roo-radio-set',
37508             cn : [
37509                 {
37510                     tag : 'input',
37511                     cls : 'roo-radio-set-input',
37512                     type : 'hidden',
37513                     name : this.name,
37514                     value : this.value ? this.value :  ''
37515                 },
37516                 label,
37517                 items
37518             ]
37519         };
37520         
37521         if(this.weight.length){
37522             cfg.cls += ' roo-radio-' + this.weight;
37523         }
37524         
37525         if(this.inline) {
37526             cfg.cls += ' roo-radio-set-inline';
37527         }
37528         
37529         var settings=this;
37530         ['xs','sm','md','lg'].map(function(size){
37531             if (settings[size]) {
37532                 cfg.cls += ' col-' + size + '-' + settings[size];
37533             }
37534         });
37535         
37536         return cfg;
37537         
37538     },
37539
37540     initEvents : function()
37541     {
37542         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37543         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37544         
37545         if(!this.fieldLabel.length){
37546             this.labelEl.hide();
37547         }
37548         
37549         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37550         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37551         
37552         this.indicator = this.indicatorEl();
37553         
37554         if(this.indicator){
37555             this.indicator.addClass('invisible');
37556         }
37557         
37558         this.originalValue = this.getValue();
37559         
37560     },
37561     
37562     inputEl: function ()
37563     {
37564         return this.el.select('.roo-radio-set-input', true).first();
37565     },
37566     
37567     getChildContainer : function()
37568     {
37569         return this.itemsEl;
37570     },
37571     
37572     register : function(item)
37573     {
37574         this.radioes.push(item);
37575         
37576     },
37577     
37578     validate : function()
37579     {   
37580         if(this.getVisibilityEl().hasClass('hidden')){
37581             return true;
37582         }
37583         
37584         var valid = false;
37585         
37586         Roo.each(this.radioes, function(i){
37587             if(!i.checked){
37588                 return;
37589             }
37590             
37591             valid = true;
37592             return false;
37593         });
37594         
37595         if(this.allowBlank) {
37596             return true;
37597         }
37598         
37599         if(this.disabled || valid){
37600             this.markValid();
37601             return true;
37602         }
37603         
37604         this.markInvalid();
37605         return false;
37606         
37607     },
37608     
37609     markValid : function()
37610     {
37611         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37612             this.indicatorEl().removeClass('visible');
37613             this.indicatorEl().addClass('invisible');
37614         }
37615         
37616         
37617         if (Roo.bootstrap.version == 3) {
37618             this.el.removeClass([this.invalidClass, this.validClass]);
37619             this.el.addClass(this.validClass);
37620         } else {
37621             this.el.removeClass(['is-invalid','is-valid']);
37622             this.el.addClass(['is-valid']);
37623         }
37624         this.fireEvent('valid', this);
37625     },
37626     
37627     markInvalid : function(msg)
37628     {
37629         if(this.allowBlank || this.disabled){
37630             return;
37631         }
37632         
37633         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37634             this.indicatorEl().removeClass('invisible');
37635             this.indicatorEl().addClass('visible');
37636         }
37637         if (Roo.bootstrap.version == 3) {
37638             this.el.removeClass([this.invalidClass, this.validClass]);
37639             this.el.addClass(this.invalidClass);
37640         } else {
37641             this.el.removeClass(['is-invalid','is-valid']);
37642             this.el.addClass(['is-invalid']);
37643         }
37644         
37645         this.fireEvent('invalid', this, msg);
37646         
37647     },
37648     
37649     setValue : function(v, suppressEvent)
37650     {   
37651         if(this.value === v){
37652             return;
37653         }
37654         
37655         this.value = v;
37656         
37657         if(this.rendered){
37658             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37659         }
37660         
37661         Roo.each(this.radioes, function(i){
37662             i.checked = false;
37663             i.el.removeClass('checked');
37664         });
37665         
37666         Roo.each(this.radioes, function(i){
37667             
37668             if(i.value === v || i.value.toString() === v.toString()){
37669                 i.checked = true;
37670                 i.el.addClass('checked');
37671                 
37672                 if(suppressEvent !== true){
37673                     this.fireEvent('check', this, i);
37674                 }
37675                 
37676                 return false;
37677             }
37678             
37679         }, this);
37680         
37681         this.validate();
37682     },
37683     
37684     clearInvalid : function(){
37685         
37686         if(!this.el || this.preventMark){
37687             return;
37688         }
37689         
37690         this.el.removeClass([this.invalidClass]);
37691         
37692         this.fireEvent('valid', this);
37693     }
37694     
37695 });
37696
37697 Roo.apply(Roo.bootstrap.RadioSet, {
37698     
37699     groups: {},
37700     
37701     register : function(set)
37702     {
37703         this.groups[set.name] = set;
37704     },
37705     
37706     get: function(name) 
37707     {
37708         if (typeof(this.groups[name]) == 'undefined') {
37709             return false;
37710         }
37711         
37712         return this.groups[name] ;
37713     }
37714     
37715 });
37716 /*
37717  * Based on:
37718  * Ext JS Library 1.1.1
37719  * Copyright(c) 2006-2007, Ext JS, LLC.
37720  *
37721  * Originally Released Under LGPL - original licence link has changed is not relivant.
37722  *
37723  * Fork - LGPL
37724  * <script type="text/javascript">
37725  */
37726
37727
37728 /**
37729  * @class Roo.bootstrap.SplitBar
37730  * @extends Roo.util.Observable
37731  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37732  * <br><br>
37733  * Usage:
37734  * <pre><code>
37735 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37736                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37737 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37738 split.minSize = 100;
37739 split.maxSize = 600;
37740 split.animate = true;
37741 split.on('moved', splitterMoved);
37742 </code></pre>
37743  * @constructor
37744  * Create a new SplitBar
37745  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37746  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37747  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37748  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37749                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37750                         position of the SplitBar).
37751  */
37752 Roo.bootstrap.SplitBar = function(cfg){
37753     
37754     /** @private */
37755     
37756     //{
37757     //  dragElement : elm
37758     //  resizingElement: el,
37759         // optional..
37760     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37761     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37762         // existingProxy ???
37763     //}
37764     
37765     this.el = Roo.get(cfg.dragElement, true);
37766     this.el.dom.unselectable = "on";
37767     /** @private */
37768     this.resizingEl = Roo.get(cfg.resizingElement, true);
37769
37770     /**
37771      * @private
37772      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37773      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37774      * @type Number
37775      */
37776     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37777     
37778     /**
37779      * The minimum size of the resizing element. (Defaults to 0)
37780      * @type Number
37781      */
37782     this.minSize = 0;
37783     
37784     /**
37785      * The maximum size of the resizing element. (Defaults to 2000)
37786      * @type Number
37787      */
37788     this.maxSize = 2000;
37789     
37790     /**
37791      * Whether to animate the transition to the new size
37792      * @type Boolean
37793      */
37794     this.animate = false;
37795     
37796     /**
37797      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37798      * @type Boolean
37799      */
37800     this.useShim = false;
37801     
37802     /** @private */
37803     this.shim = null;
37804     
37805     if(!cfg.existingProxy){
37806         /** @private */
37807         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37808     }else{
37809         this.proxy = Roo.get(cfg.existingProxy).dom;
37810     }
37811     /** @private */
37812     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37813     
37814     /** @private */
37815     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37816     
37817     /** @private */
37818     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37819     
37820     /** @private */
37821     this.dragSpecs = {};
37822     
37823     /**
37824      * @private The adapter to use to positon and resize elements
37825      */
37826     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37827     this.adapter.init(this);
37828     
37829     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37830         /** @private */
37831         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37832         this.el.addClass("roo-splitbar-h");
37833     }else{
37834         /** @private */
37835         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37836         this.el.addClass("roo-splitbar-v");
37837     }
37838     
37839     this.addEvents({
37840         /**
37841          * @event resize
37842          * Fires when the splitter is moved (alias for {@link #event-moved})
37843          * @param {Roo.bootstrap.SplitBar} this
37844          * @param {Number} newSize the new width or height
37845          */
37846         "resize" : true,
37847         /**
37848          * @event moved
37849          * Fires when the splitter is moved
37850          * @param {Roo.bootstrap.SplitBar} this
37851          * @param {Number} newSize the new width or height
37852          */
37853         "moved" : true,
37854         /**
37855          * @event beforeresize
37856          * Fires before the splitter is dragged
37857          * @param {Roo.bootstrap.SplitBar} this
37858          */
37859         "beforeresize" : true,
37860
37861         "beforeapply" : true
37862     });
37863
37864     Roo.util.Observable.call(this);
37865 };
37866
37867 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37868     onStartProxyDrag : function(x, y){
37869         this.fireEvent("beforeresize", this);
37870         if(!this.overlay){
37871             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37872             o.unselectable();
37873             o.enableDisplayMode("block");
37874             // all splitbars share the same overlay
37875             Roo.bootstrap.SplitBar.prototype.overlay = o;
37876         }
37877         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37878         this.overlay.show();
37879         Roo.get(this.proxy).setDisplayed("block");
37880         var size = this.adapter.getElementSize(this);
37881         this.activeMinSize = this.getMinimumSize();;
37882         this.activeMaxSize = this.getMaximumSize();;
37883         var c1 = size - this.activeMinSize;
37884         var c2 = Math.max(this.activeMaxSize - size, 0);
37885         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37886             this.dd.resetConstraints();
37887             this.dd.setXConstraint(
37888                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37889                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37890             );
37891             this.dd.setYConstraint(0, 0);
37892         }else{
37893             this.dd.resetConstraints();
37894             this.dd.setXConstraint(0, 0);
37895             this.dd.setYConstraint(
37896                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37897                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37898             );
37899          }
37900         this.dragSpecs.startSize = size;
37901         this.dragSpecs.startPoint = [x, y];
37902         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37903     },
37904     
37905     /** 
37906      * @private Called after the drag operation by the DDProxy
37907      */
37908     onEndProxyDrag : function(e){
37909         Roo.get(this.proxy).setDisplayed(false);
37910         var endPoint = Roo.lib.Event.getXY(e);
37911         if(this.overlay){
37912             this.overlay.hide();
37913         }
37914         var newSize;
37915         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37916             newSize = this.dragSpecs.startSize + 
37917                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37918                     endPoint[0] - this.dragSpecs.startPoint[0] :
37919                     this.dragSpecs.startPoint[0] - endPoint[0]
37920                 );
37921         }else{
37922             newSize = this.dragSpecs.startSize + 
37923                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37924                     endPoint[1] - this.dragSpecs.startPoint[1] :
37925                     this.dragSpecs.startPoint[1] - endPoint[1]
37926                 );
37927         }
37928         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37929         if(newSize != this.dragSpecs.startSize){
37930             if(this.fireEvent('beforeapply', this, newSize) !== false){
37931                 this.adapter.setElementSize(this, newSize);
37932                 this.fireEvent("moved", this, newSize);
37933                 this.fireEvent("resize", this, newSize);
37934             }
37935         }
37936     },
37937     
37938     /**
37939      * Get the adapter this SplitBar uses
37940      * @return The adapter object
37941      */
37942     getAdapter : function(){
37943         return this.adapter;
37944     },
37945     
37946     /**
37947      * Set the adapter this SplitBar uses
37948      * @param {Object} adapter A SplitBar adapter object
37949      */
37950     setAdapter : function(adapter){
37951         this.adapter = adapter;
37952         this.adapter.init(this);
37953     },
37954     
37955     /**
37956      * Gets the minimum size for the resizing element
37957      * @return {Number} The minimum size
37958      */
37959     getMinimumSize : function(){
37960         return this.minSize;
37961     },
37962     
37963     /**
37964      * Sets the minimum size for the resizing element
37965      * @param {Number} minSize The minimum size
37966      */
37967     setMinimumSize : function(minSize){
37968         this.minSize = minSize;
37969     },
37970     
37971     /**
37972      * Gets the maximum size for the resizing element
37973      * @return {Number} The maximum size
37974      */
37975     getMaximumSize : function(){
37976         return this.maxSize;
37977     },
37978     
37979     /**
37980      * Sets the maximum size for the resizing element
37981      * @param {Number} maxSize The maximum size
37982      */
37983     setMaximumSize : function(maxSize){
37984         this.maxSize = maxSize;
37985     },
37986     
37987     /**
37988      * Sets the initialize size for the resizing element
37989      * @param {Number} size The initial size
37990      */
37991     setCurrentSize : function(size){
37992         var oldAnimate = this.animate;
37993         this.animate = false;
37994         this.adapter.setElementSize(this, size);
37995         this.animate = oldAnimate;
37996     },
37997     
37998     /**
37999      * Destroy this splitbar. 
38000      * @param {Boolean} removeEl True to remove the element
38001      */
38002     destroy : function(removeEl){
38003         if(this.shim){
38004             this.shim.remove();
38005         }
38006         this.dd.unreg();
38007         this.proxy.parentNode.removeChild(this.proxy);
38008         if(removeEl){
38009             this.el.remove();
38010         }
38011     }
38012 });
38013
38014 /**
38015  * @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.
38016  */
38017 Roo.bootstrap.SplitBar.createProxy = function(dir){
38018     var proxy = new Roo.Element(document.createElement("div"));
38019     proxy.unselectable();
38020     var cls = 'roo-splitbar-proxy';
38021     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38022     document.body.appendChild(proxy.dom);
38023     return proxy.dom;
38024 };
38025
38026 /** 
38027  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38028  * Default Adapter. It assumes the splitter and resizing element are not positioned
38029  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38030  */
38031 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38032 };
38033
38034 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38035     // do nothing for now
38036     init : function(s){
38037     
38038     },
38039     /**
38040      * Called before drag operations to get the current size of the resizing element. 
38041      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38042      */
38043      getElementSize : function(s){
38044         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38045             return s.resizingEl.getWidth();
38046         }else{
38047             return s.resizingEl.getHeight();
38048         }
38049     },
38050     
38051     /**
38052      * Called after drag operations to set the size of the resizing element.
38053      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38054      * @param {Number} newSize The new size to set
38055      * @param {Function} onComplete A function to be invoked when resizing is complete
38056      */
38057     setElementSize : function(s, newSize, onComplete){
38058         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38059             if(!s.animate){
38060                 s.resizingEl.setWidth(newSize);
38061                 if(onComplete){
38062                     onComplete(s, newSize);
38063                 }
38064             }else{
38065                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38066             }
38067         }else{
38068             
38069             if(!s.animate){
38070                 s.resizingEl.setHeight(newSize);
38071                 if(onComplete){
38072                     onComplete(s, newSize);
38073                 }
38074             }else{
38075                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38076             }
38077         }
38078     }
38079 };
38080
38081 /** 
38082  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38083  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38084  * Adapter that  moves the splitter element to align with the resized sizing element. 
38085  * Used with an absolute positioned SplitBar.
38086  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38087  * document.body, make sure you assign an id to the body element.
38088  */
38089 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38090     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38091     this.container = Roo.get(container);
38092 };
38093
38094 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38095     init : function(s){
38096         this.basic.init(s);
38097     },
38098     
38099     getElementSize : function(s){
38100         return this.basic.getElementSize(s);
38101     },
38102     
38103     setElementSize : function(s, newSize, onComplete){
38104         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38105     },
38106     
38107     moveSplitter : function(s){
38108         var yes = Roo.bootstrap.SplitBar;
38109         switch(s.placement){
38110             case yes.LEFT:
38111                 s.el.setX(s.resizingEl.getRight());
38112                 break;
38113             case yes.RIGHT:
38114                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38115                 break;
38116             case yes.TOP:
38117                 s.el.setY(s.resizingEl.getBottom());
38118                 break;
38119             case yes.BOTTOM:
38120                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38121                 break;
38122         }
38123     }
38124 };
38125
38126 /**
38127  * Orientation constant - Create a vertical SplitBar
38128  * @static
38129  * @type Number
38130  */
38131 Roo.bootstrap.SplitBar.VERTICAL = 1;
38132
38133 /**
38134  * Orientation constant - Create a horizontal SplitBar
38135  * @static
38136  * @type Number
38137  */
38138 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38139
38140 /**
38141  * Placement constant - The resizing element is to the left of the splitter element
38142  * @static
38143  * @type Number
38144  */
38145 Roo.bootstrap.SplitBar.LEFT = 1;
38146
38147 /**
38148  * Placement constant - The resizing element is to the right of the splitter element
38149  * @static
38150  * @type Number
38151  */
38152 Roo.bootstrap.SplitBar.RIGHT = 2;
38153
38154 /**
38155  * Placement constant - The resizing element is positioned above the splitter element
38156  * @static
38157  * @type Number
38158  */
38159 Roo.bootstrap.SplitBar.TOP = 3;
38160
38161 /**
38162  * Placement constant - The resizing element is positioned under splitter element
38163  * @static
38164  * @type Number
38165  */
38166 Roo.bootstrap.SplitBar.BOTTOM = 4;
38167 Roo.namespace("Roo.bootstrap.layout");/*
38168  * Based on:
38169  * Ext JS Library 1.1.1
38170  * Copyright(c) 2006-2007, Ext JS, LLC.
38171  *
38172  * Originally Released Under LGPL - original licence link has changed is not relivant.
38173  *
38174  * Fork - LGPL
38175  * <script type="text/javascript">
38176  */
38177
38178 /**
38179  * @class Roo.bootstrap.layout.Manager
38180  * @extends Roo.bootstrap.Component
38181  * Base class for layout managers.
38182  */
38183 Roo.bootstrap.layout.Manager = function(config)
38184 {
38185     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38186
38187
38188
38189
38190
38191     /** false to disable window resize monitoring @type Boolean */
38192     this.monitorWindowResize = true;
38193     this.regions = {};
38194     this.addEvents({
38195         /**
38196          * @event layout
38197          * Fires when a layout is performed.
38198          * @param {Roo.LayoutManager} this
38199          */
38200         "layout" : true,
38201         /**
38202          * @event regionresized
38203          * Fires when the user resizes a region.
38204          * @param {Roo.LayoutRegion} region The resized region
38205          * @param {Number} newSize The new size (width for east/west, height for north/south)
38206          */
38207         "regionresized" : true,
38208         /**
38209          * @event regioncollapsed
38210          * Fires when a region is collapsed.
38211          * @param {Roo.LayoutRegion} region The collapsed region
38212          */
38213         "regioncollapsed" : true,
38214         /**
38215          * @event regionexpanded
38216          * Fires when a region is expanded.
38217          * @param {Roo.LayoutRegion} region The expanded region
38218          */
38219         "regionexpanded" : true
38220     });
38221     this.updating = false;
38222
38223     if (config.el) {
38224         this.el = Roo.get(config.el);
38225         this.initEvents();
38226     }
38227
38228 };
38229
38230 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38231
38232
38233     regions : null,
38234
38235     monitorWindowResize : true,
38236
38237
38238     updating : false,
38239
38240
38241     onRender : function(ct, position)
38242     {
38243         if(!this.el){
38244             this.el = Roo.get(ct);
38245             this.initEvents();
38246         }
38247         //this.fireEvent('render',this);
38248     },
38249
38250
38251     initEvents: function()
38252     {
38253
38254
38255         // ie scrollbar fix
38256         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38257             document.body.scroll = "no";
38258         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38259             this.el.position('relative');
38260         }
38261         this.id = this.el.id;
38262         this.el.addClass("roo-layout-container");
38263         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38264         if(this.el.dom != document.body ) {
38265             this.el.on('resize', this.layout,this);
38266             this.el.on('show', this.layout,this);
38267         }
38268
38269     },
38270
38271     /**
38272      * Returns true if this layout is currently being updated
38273      * @return {Boolean}
38274      */
38275     isUpdating : function(){
38276         return this.updating;
38277     },
38278
38279     /**
38280      * Suspend the LayoutManager from doing auto-layouts while
38281      * making multiple add or remove calls
38282      */
38283     beginUpdate : function(){
38284         this.updating = true;
38285     },
38286
38287     /**
38288      * Restore auto-layouts and optionally disable the manager from performing a layout
38289      * @param {Boolean} noLayout true to disable a layout update
38290      */
38291     endUpdate : function(noLayout){
38292         this.updating = false;
38293         if(!noLayout){
38294             this.layout();
38295         }
38296     },
38297
38298     layout: function(){
38299         // abstract...
38300     },
38301
38302     onRegionResized : function(region, newSize){
38303         this.fireEvent("regionresized", region, newSize);
38304         this.layout();
38305     },
38306
38307     onRegionCollapsed : function(region){
38308         this.fireEvent("regioncollapsed", region);
38309     },
38310
38311     onRegionExpanded : function(region){
38312         this.fireEvent("regionexpanded", region);
38313     },
38314
38315     /**
38316      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38317      * performs box-model adjustments.
38318      * @return {Object} The size as an object {width: (the width), height: (the height)}
38319      */
38320     getViewSize : function()
38321     {
38322         var size;
38323         if(this.el.dom != document.body){
38324             size = this.el.getSize();
38325         }else{
38326             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38327         }
38328         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38329         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38330         return size;
38331     },
38332
38333     /**
38334      * Returns the Element this layout is bound to.
38335      * @return {Roo.Element}
38336      */
38337     getEl : function(){
38338         return this.el;
38339     },
38340
38341     /**
38342      * Returns the specified region.
38343      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38344      * @return {Roo.LayoutRegion}
38345      */
38346     getRegion : function(target){
38347         return this.regions[target.toLowerCase()];
38348     },
38349
38350     onWindowResize : function(){
38351         if(this.monitorWindowResize){
38352             this.layout();
38353         }
38354     }
38355 });
38356 /*
38357  * Based on:
38358  * Ext JS Library 1.1.1
38359  * Copyright(c) 2006-2007, Ext JS, LLC.
38360  *
38361  * Originally Released Under LGPL - original licence link has changed is not relivant.
38362  *
38363  * Fork - LGPL
38364  * <script type="text/javascript">
38365  */
38366 /**
38367  * @class Roo.bootstrap.layout.Border
38368  * @extends Roo.bootstrap.layout.Manager
38369  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38370  * please see: examples/bootstrap/nested.html<br><br>
38371  
38372 <b>The container the layout is rendered into can be either the body element or any other element.
38373 If it is not the body element, the container needs to either be an absolute positioned element,
38374 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38375 the container size if it is not the body element.</b>
38376
38377 * @constructor
38378 * Create a new Border
38379 * @param {Object} config Configuration options
38380  */
38381 Roo.bootstrap.layout.Border = function(config){
38382     config = config || {};
38383     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38384     
38385     
38386     
38387     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38388         if(config[region]){
38389             config[region].region = region;
38390             this.addRegion(config[region]);
38391         }
38392     },this);
38393     
38394 };
38395
38396 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38397
38398 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38399     
38400     parent : false, // this might point to a 'nest' or a ???
38401     
38402     /**
38403      * Creates and adds a new region if it doesn't already exist.
38404      * @param {String} target The target region key (north, south, east, west or center).
38405      * @param {Object} config The regions config object
38406      * @return {BorderLayoutRegion} The new region
38407      */
38408     addRegion : function(config)
38409     {
38410         if(!this.regions[config.region]){
38411             var r = this.factory(config);
38412             this.bindRegion(r);
38413         }
38414         return this.regions[config.region];
38415     },
38416
38417     // private (kinda)
38418     bindRegion : function(r){
38419         this.regions[r.config.region] = r;
38420         
38421         r.on("visibilitychange",    this.layout, this);
38422         r.on("paneladded",          this.layout, this);
38423         r.on("panelremoved",        this.layout, this);
38424         r.on("invalidated",         this.layout, this);
38425         r.on("resized",             this.onRegionResized, this);
38426         r.on("collapsed",           this.onRegionCollapsed, this);
38427         r.on("expanded",            this.onRegionExpanded, this);
38428     },
38429
38430     /**
38431      * Performs a layout update.
38432      */
38433     layout : function()
38434     {
38435         if(this.updating) {
38436             return;
38437         }
38438         
38439         // render all the rebions if they have not been done alreayd?
38440         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38441             if(this.regions[region] && !this.regions[region].bodyEl){
38442                 this.regions[region].onRender(this.el)
38443             }
38444         },this);
38445         
38446         var size = this.getViewSize();
38447         var w = size.width;
38448         var h = size.height;
38449         var centerW = w;
38450         var centerH = h;
38451         var centerY = 0;
38452         var centerX = 0;
38453         //var x = 0, y = 0;
38454
38455         var rs = this.regions;
38456         var north = rs["north"];
38457         var south = rs["south"]; 
38458         var west = rs["west"];
38459         var east = rs["east"];
38460         var center = rs["center"];
38461         //if(this.hideOnLayout){ // not supported anymore
38462             //c.el.setStyle("display", "none");
38463         //}
38464         if(north && north.isVisible()){
38465             var b = north.getBox();
38466             var m = north.getMargins();
38467             b.width = w - (m.left+m.right);
38468             b.x = m.left;
38469             b.y = m.top;
38470             centerY = b.height + b.y + m.bottom;
38471             centerH -= centerY;
38472             north.updateBox(this.safeBox(b));
38473         }
38474         if(south && south.isVisible()){
38475             var b = south.getBox();
38476             var m = south.getMargins();
38477             b.width = w - (m.left+m.right);
38478             b.x = m.left;
38479             var totalHeight = (b.height + m.top + m.bottom);
38480             b.y = h - totalHeight + m.top;
38481             centerH -= totalHeight;
38482             south.updateBox(this.safeBox(b));
38483         }
38484         if(west && west.isVisible()){
38485             var b = west.getBox();
38486             var m = west.getMargins();
38487             b.height = centerH - (m.top+m.bottom);
38488             b.x = m.left;
38489             b.y = centerY + m.top;
38490             var totalWidth = (b.width + m.left + m.right);
38491             centerX += totalWidth;
38492             centerW -= totalWidth;
38493             west.updateBox(this.safeBox(b));
38494         }
38495         if(east && east.isVisible()){
38496             var b = east.getBox();
38497             var m = east.getMargins();
38498             b.height = centerH - (m.top+m.bottom);
38499             var totalWidth = (b.width + m.left + m.right);
38500             b.x = w - totalWidth + m.left;
38501             b.y = centerY + m.top;
38502             centerW -= totalWidth;
38503             east.updateBox(this.safeBox(b));
38504         }
38505         if(center){
38506             var m = center.getMargins();
38507             var centerBox = {
38508                 x: centerX + m.left,
38509                 y: centerY + m.top,
38510                 width: centerW - (m.left+m.right),
38511                 height: centerH - (m.top+m.bottom)
38512             };
38513             //if(this.hideOnLayout){
38514                 //center.el.setStyle("display", "block");
38515             //}
38516             center.updateBox(this.safeBox(centerBox));
38517         }
38518         this.el.repaint();
38519         this.fireEvent("layout", this);
38520     },
38521
38522     // private
38523     safeBox : function(box){
38524         box.width = Math.max(0, box.width);
38525         box.height = Math.max(0, box.height);
38526         return box;
38527     },
38528
38529     /**
38530      * Adds a ContentPanel (or subclass) to this layout.
38531      * @param {String} target The target region key (north, south, east, west or center).
38532      * @param {Roo.ContentPanel} panel The panel to add
38533      * @return {Roo.ContentPanel} The added panel
38534      */
38535     add : function(target, panel){
38536          
38537         target = target.toLowerCase();
38538         return this.regions[target].add(panel);
38539     },
38540
38541     /**
38542      * Remove a ContentPanel (or subclass) to this layout.
38543      * @param {String} target The target region key (north, south, east, west or center).
38544      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38545      * @return {Roo.ContentPanel} The removed panel
38546      */
38547     remove : function(target, panel){
38548         target = target.toLowerCase();
38549         return this.regions[target].remove(panel);
38550     },
38551
38552     /**
38553      * Searches all regions for a panel with the specified id
38554      * @param {String} panelId
38555      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38556      */
38557     findPanel : function(panelId){
38558         var rs = this.regions;
38559         for(var target in rs){
38560             if(typeof rs[target] != "function"){
38561                 var p = rs[target].getPanel(panelId);
38562                 if(p){
38563                     return p;
38564                 }
38565             }
38566         }
38567         return null;
38568     },
38569
38570     /**
38571      * Searches all regions for a panel with the specified id and activates (shows) it.
38572      * @param {String/ContentPanel} panelId The panels id or the panel itself
38573      * @return {Roo.ContentPanel} The shown panel or null
38574      */
38575     showPanel : function(panelId) {
38576       var rs = this.regions;
38577       for(var target in rs){
38578          var r = rs[target];
38579          if(typeof r != "function"){
38580             if(r.hasPanel(panelId)){
38581                return r.showPanel(panelId);
38582             }
38583          }
38584       }
38585       return null;
38586    },
38587
38588    /**
38589      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38590      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38591      */
38592    /*
38593     restoreState : function(provider){
38594         if(!provider){
38595             provider = Roo.state.Manager;
38596         }
38597         var sm = new Roo.LayoutStateManager();
38598         sm.init(this, provider);
38599     },
38600 */
38601  
38602  
38603     /**
38604      * Adds a xtype elements to the layout.
38605      * <pre><code>
38606
38607 layout.addxtype({
38608        xtype : 'ContentPanel',
38609        region: 'west',
38610        items: [ .... ]
38611    }
38612 );
38613
38614 layout.addxtype({
38615         xtype : 'NestedLayoutPanel',
38616         region: 'west',
38617         layout: {
38618            center: { },
38619            west: { }   
38620         },
38621         items : [ ... list of content panels or nested layout panels.. ]
38622    }
38623 );
38624 </code></pre>
38625      * @param {Object} cfg Xtype definition of item to add.
38626      */
38627     addxtype : function(cfg)
38628     {
38629         // basically accepts a pannel...
38630         // can accept a layout region..!?!?
38631         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38632         
38633         
38634         // theory?  children can only be panels??
38635         
38636         //if (!cfg.xtype.match(/Panel$/)) {
38637         //    return false;
38638         //}
38639         var ret = false;
38640         
38641         if (typeof(cfg.region) == 'undefined') {
38642             Roo.log("Failed to add Panel, region was not set");
38643             Roo.log(cfg);
38644             return false;
38645         }
38646         var region = cfg.region;
38647         delete cfg.region;
38648         
38649           
38650         var xitems = [];
38651         if (cfg.items) {
38652             xitems = cfg.items;
38653             delete cfg.items;
38654         }
38655         var nb = false;
38656         
38657         if ( region == 'center') {
38658             Roo.log("Center: " + cfg.title);
38659         }
38660         
38661         
38662         switch(cfg.xtype) 
38663         {
38664             case 'Content':  // ContentPanel (el, cfg)
38665             case 'Scroll':  // ContentPanel (el, cfg)
38666             case 'View': 
38667                 cfg.autoCreate = cfg.autoCreate || true;
38668                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38669                 //} else {
38670                 //    var el = this.el.createChild();
38671                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38672                 //}
38673                 
38674                 this.add(region, ret);
38675                 break;
38676             
38677             /*
38678             case 'TreePanel': // our new panel!
38679                 cfg.el = this.el.createChild();
38680                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38681                 this.add(region, ret);
38682                 break;
38683             */
38684             
38685             case 'Nest': 
38686                 // create a new Layout (which is  a Border Layout...
38687                 
38688                 var clayout = cfg.layout;
38689                 clayout.el  = this.el.createChild();
38690                 clayout.items   = clayout.items  || [];
38691                 
38692                 delete cfg.layout;
38693                 
38694                 // replace this exitems with the clayout ones..
38695                 xitems = clayout.items;
38696                  
38697                 // force background off if it's in center...
38698                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38699                     cfg.background = false;
38700                 }
38701                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38702                 
38703                 
38704                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38705                 //console.log('adding nested layout panel '  + cfg.toSource());
38706                 this.add(region, ret);
38707                 nb = {}; /// find first...
38708                 break;
38709             
38710             case 'Grid':
38711                 
38712                 // needs grid and region
38713                 
38714                 //var el = this.getRegion(region).el.createChild();
38715                 /*
38716                  *var el = this.el.createChild();
38717                 // create the grid first...
38718                 cfg.grid.container = el;
38719                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38720                 */
38721                 
38722                 if (region == 'center' && this.active ) {
38723                     cfg.background = false;
38724                 }
38725                 
38726                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38727                 
38728                 this.add(region, ret);
38729                 /*
38730                 if (cfg.background) {
38731                     // render grid on panel activation (if panel background)
38732                     ret.on('activate', function(gp) {
38733                         if (!gp.grid.rendered) {
38734                     //        gp.grid.render(el);
38735                         }
38736                     });
38737                 } else {
38738                   //  cfg.grid.render(el);
38739                 }
38740                 */
38741                 break;
38742            
38743            
38744             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38745                 // it was the old xcomponent building that caused this before.
38746                 // espeically if border is the top element in the tree.
38747                 ret = this;
38748                 break; 
38749                 
38750                     
38751                 
38752                 
38753                 
38754             default:
38755                 /*
38756                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38757                     
38758                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38759                     this.add(region, ret);
38760                 } else {
38761                 */
38762                     Roo.log(cfg);
38763                     throw "Can not add '" + cfg.xtype + "' to Border";
38764                     return null;
38765              
38766                                 
38767              
38768         }
38769         this.beginUpdate();
38770         // add children..
38771         var region = '';
38772         var abn = {};
38773         Roo.each(xitems, function(i)  {
38774             region = nb && i.region ? i.region : false;
38775             
38776             var add = ret.addxtype(i);
38777            
38778             if (region) {
38779                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38780                 if (!i.background) {
38781                     abn[region] = nb[region] ;
38782                 }
38783             }
38784             
38785         });
38786         this.endUpdate();
38787
38788         // make the last non-background panel active..
38789         //if (nb) { Roo.log(abn); }
38790         if (nb) {
38791             
38792             for(var r in abn) {
38793                 region = this.getRegion(r);
38794                 if (region) {
38795                     // tried using nb[r], but it does not work..
38796                      
38797                     region.showPanel(abn[r]);
38798                    
38799                 }
38800             }
38801         }
38802         return ret;
38803         
38804     },
38805     
38806     
38807 // private
38808     factory : function(cfg)
38809     {
38810         
38811         var validRegions = Roo.bootstrap.layout.Border.regions;
38812
38813         var target = cfg.region;
38814         cfg.mgr = this;
38815         
38816         var r = Roo.bootstrap.layout;
38817         Roo.log(target);
38818         switch(target){
38819             case "north":
38820                 return new r.North(cfg);
38821             case "south":
38822                 return new r.South(cfg);
38823             case "east":
38824                 return new r.East(cfg);
38825             case "west":
38826                 return new r.West(cfg);
38827             case "center":
38828                 return new r.Center(cfg);
38829         }
38830         throw 'Layout region "'+target+'" not supported.';
38831     }
38832     
38833     
38834 });
38835  /*
38836  * Based on:
38837  * Ext JS Library 1.1.1
38838  * Copyright(c) 2006-2007, Ext JS, LLC.
38839  *
38840  * Originally Released Under LGPL - original licence link has changed is not relivant.
38841  *
38842  * Fork - LGPL
38843  * <script type="text/javascript">
38844  */
38845  
38846 /**
38847  * @class Roo.bootstrap.layout.Basic
38848  * @extends Roo.util.Observable
38849  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38850  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38851  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38852  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38853  * @cfg {string}   region  the region that it inhabits..
38854  * @cfg {bool}   skipConfig skip config?
38855  * 
38856
38857  */
38858 Roo.bootstrap.layout.Basic = function(config){
38859     
38860     this.mgr = config.mgr;
38861     
38862     this.position = config.region;
38863     
38864     var skipConfig = config.skipConfig;
38865     
38866     this.events = {
38867         /**
38868          * @scope Roo.BasicLayoutRegion
38869          */
38870         
38871         /**
38872          * @event beforeremove
38873          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38874          * @param {Roo.LayoutRegion} this
38875          * @param {Roo.ContentPanel} panel The panel
38876          * @param {Object} e The cancel event object
38877          */
38878         "beforeremove" : true,
38879         /**
38880          * @event invalidated
38881          * Fires when the layout for this region is changed.
38882          * @param {Roo.LayoutRegion} this
38883          */
38884         "invalidated" : true,
38885         /**
38886          * @event visibilitychange
38887          * Fires when this region is shown or hidden 
38888          * @param {Roo.LayoutRegion} this
38889          * @param {Boolean} visibility true or false
38890          */
38891         "visibilitychange" : true,
38892         /**
38893          * @event paneladded
38894          * Fires when a panel is added. 
38895          * @param {Roo.LayoutRegion} this
38896          * @param {Roo.ContentPanel} panel The panel
38897          */
38898         "paneladded" : true,
38899         /**
38900          * @event panelremoved
38901          * Fires when a panel is removed. 
38902          * @param {Roo.LayoutRegion} this
38903          * @param {Roo.ContentPanel} panel The panel
38904          */
38905         "panelremoved" : true,
38906         /**
38907          * @event beforecollapse
38908          * Fires when this region before collapse.
38909          * @param {Roo.LayoutRegion} this
38910          */
38911         "beforecollapse" : true,
38912         /**
38913          * @event collapsed
38914          * Fires when this region is collapsed.
38915          * @param {Roo.LayoutRegion} this
38916          */
38917         "collapsed" : true,
38918         /**
38919          * @event expanded
38920          * Fires when this region is expanded.
38921          * @param {Roo.LayoutRegion} this
38922          */
38923         "expanded" : true,
38924         /**
38925          * @event slideshow
38926          * Fires when this region is slid into view.
38927          * @param {Roo.LayoutRegion} this
38928          */
38929         "slideshow" : true,
38930         /**
38931          * @event slidehide
38932          * Fires when this region slides out of view. 
38933          * @param {Roo.LayoutRegion} this
38934          */
38935         "slidehide" : true,
38936         /**
38937          * @event panelactivated
38938          * Fires when a panel is activated. 
38939          * @param {Roo.LayoutRegion} this
38940          * @param {Roo.ContentPanel} panel The activated panel
38941          */
38942         "panelactivated" : true,
38943         /**
38944          * @event resized
38945          * Fires when the user resizes this region. 
38946          * @param {Roo.LayoutRegion} this
38947          * @param {Number} newSize The new size (width for east/west, height for north/south)
38948          */
38949         "resized" : true
38950     };
38951     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38952     this.panels = new Roo.util.MixedCollection();
38953     this.panels.getKey = this.getPanelId.createDelegate(this);
38954     this.box = null;
38955     this.activePanel = null;
38956     // ensure listeners are added...
38957     
38958     if (config.listeners || config.events) {
38959         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38960             listeners : config.listeners || {},
38961             events : config.events || {}
38962         });
38963     }
38964     
38965     if(skipConfig !== true){
38966         this.applyConfig(config);
38967     }
38968 };
38969
38970 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38971 {
38972     getPanelId : function(p){
38973         return p.getId();
38974     },
38975     
38976     applyConfig : function(config){
38977         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38978         this.config = config;
38979         
38980     },
38981     
38982     /**
38983      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38984      * the width, for horizontal (north, south) the height.
38985      * @param {Number} newSize The new width or height
38986      */
38987     resizeTo : function(newSize){
38988         var el = this.el ? this.el :
38989                  (this.activePanel ? this.activePanel.getEl() : null);
38990         if(el){
38991             switch(this.position){
38992                 case "east":
38993                 case "west":
38994                     el.setWidth(newSize);
38995                     this.fireEvent("resized", this, newSize);
38996                 break;
38997                 case "north":
38998                 case "south":
38999                     el.setHeight(newSize);
39000                     this.fireEvent("resized", this, newSize);
39001                 break;                
39002             }
39003         }
39004     },
39005     
39006     getBox : function(){
39007         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39008     },
39009     
39010     getMargins : function(){
39011         return this.margins;
39012     },
39013     
39014     updateBox : function(box){
39015         this.box = box;
39016         var el = this.activePanel.getEl();
39017         el.dom.style.left = box.x + "px";
39018         el.dom.style.top = box.y + "px";
39019         this.activePanel.setSize(box.width, box.height);
39020     },
39021     
39022     /**
39023      * Returns the container element for this region.
39024      * @return {Roo.Element}
39025      */
39026     getEl : function(){
39027         return this.activePanel;
39028     },
39029     
39030     /**
39031      * Returns true if this region is currently visible.
39032      * @return {Boolean}
39033      */
39034     isVisible : function(){
39035         return this.activePanel ? true : false;
39036     },
39037     
39038     setActivePanel : function(panel){
39039         panel = this.getPanel(panel);
39040         if(this.activePanel && this.activePanel != panel){
39041             this.activePanel.setActiveState(false);
39042             this.activePanel.getEl().setLeftTop(-10000,-10000);
39043         }
39044         this.activePanel = panel;
39045         panel.setActiveState(true);
39046         if(this.box){
39047             panel.setSize(this.box.width, this.box.height);
39048         }
39049         this.fireEvent("panelactivated", this, panel);
39050         this.fireEvent("invalidated");
39051     },
39052     
39053     /**
39054      * Show the specified panel.
39055      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39056      * @return {Roo.ContentPanel} The shown panel or null
39057      */
39058     showPanel : function(panel){
39059         panel = this.getPanel(panel);
39060         if(panel){
39061             this.setActivePanel(panel);
39062         }
39063         return panel;
39064     },
39065     
39066     /**
39067      * Get the active panel for this region.
39068      * @return {Roo.ContentPanel} The active panel or null
39069      */
39070     getActivePanel : function(){
39071         return this.activePanel;
39072     },
39073     
39074     /**
39075      * Add the passed ContentPanel(s)
39076      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39077      * @return {Roo.ContentPanel} The panel added (if only one was added)
39078      */
39079     add : function(panel){
39080         if(arguments.length > 1){
39081             for(var i = 0, len = arguments.length; i < len; i++) {
39082                 this.add(arguments[i]);
39083             }
39084             return null;
39085         }
39086         if(this.hasPanel(panel)){
39087             this.showPanel(panel);
39088             return panel;
39089         }
39090         var el = panel.getEl();
39091         if(el.dom.parentNode != this.mgr.el.dom){
39092             this.mgr.el.dom.appendChild(el.dom);
39093         }
39094         if(panel.setRegion){
39095             panel.setRegion(this);
39096         }
39097         this.panels.add(panel);
39098         el.setStyle("position", "absolute");
39099         if(!panel.background){
39100             this.setActivePanel(panel);
39101             if(this.config.initialSize && this.panels.getCount()==1){
39102                 this.resizeTo(this.config.initialSize);
39103             }
39104         }
39105         this.fireEvent("paneladded", this, panel);
39106         return panel;
39107     },
39108     
39109     /**
39110      * Returns true if the panel is in this region.
39111      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39112      * @return {Boolean}
39113      */
39114     hasPanel : function(panel){
39115         if(typeof panel == "object"){ // must be panel obj
39116             panel = panel.getId();
39117         }
39118         return this.getPanel(panel) ? true : false;
39119     },
39120     
39121     /**
39122      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39123      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39124      * @param {Boolean} preservePanel Overrides the config preservePanel option
39125      * @return {Roo.ContentPanel} The panel that was removed
39126      */
39127     remove : function(panel, preservePanel){
39128         panel = this.getPanel(panel);
39129         if(!panel){
39130             return null;
39131         }
39132         var e = {};
39133         this.fireEvent("beforeremove", this, panel, e);
39134         if(e.cancel === true){
39135             return null;
39136         }
39137         var panelId = panel.getId();
39138         this.panels.removeKey(panelId);
39139         return panel;
39140     },
39141     
39142     /**
39143      * Returns the panel specified or null if it's not in this region.
39144      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39145      * @return {Roo.ContentPanel}
39146      */
39147     getPanel : function(id){
39148         if(typeof id == "object"){ // must be panel obj
39149             return id;
39150         }
39151         return this.panels.get(id);
39152     },
39153     
39154     /**
39155      * Returns this regions position (north/south/east/west/center).
39156      * @return {String} 
39157      */
39158     getPosition: function(){
39159         return this.position;    
39160     }
39161 });/*
39162  * Based on:
39163  * Ext JS Library 1.1.1
39164  * Copyright(c) 2006-2007, Ext JS, LLC.
39165  *
39166  * Originally Released Under LGPL - original licence link has changed is not relivant.
39167  *
39168  * Fork - LGPL
39169  * <script type="text/javascript">
39170  */
39171  
39172 /**
39173  * @class Roo.bootstrap.layout.Region
39174  * @extends Roo.bootstrap.layout.Basic
39175  * This class represents a region in a layout manager.
39176  
39177  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39178  * @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})
39179  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39180  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39181  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39182  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39183  * @cfg {String}    title           The title for the region (overrides panel titles)
39184  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39185  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39186  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39187  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39188  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39189  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39190  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39191  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39192  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39193  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39194
39195  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39196  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39197  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39198  * @cfg {Number}    width           For East/West panels
39199  * @cfg {Number}    height          For North/South panels
39200  * @cfg {Boolean}   split           To show the splitter
39201  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39202  * 
39203  * @cfg {string}   cls             Extra CSS classes to add to region
39204  * 
39205  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39206  * @cfg {string}   region  the region that it inhabits..
39207  *
39208
39209  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39210  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39211
39212  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39213  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39214  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39215  */
39216 Roo.bootstrap.layout.Region = function(config)
39217 {
39218     this.applyConfig(config);
39219
39220     var mgr = config.mgr;
39221     var pos = config.region;
39222     config.skipConfig = true;
39223     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39224     
39225     if (mgr.el) {
39226         this.onRender(mgr.el);   
39227     }
39228      
39229     this.visible = true;
39230     this.collapsed = false;
39231     this.unrendered_panels = [];
39232 };
39233
39234 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39235
39236     position: '', // set by wrapper (eg. north/south etc..)
39237     unrendered_panels : null,  // unrendered panels.
39238     
39239     tabPosition : false,
39240     
39241     mgr: false, // points to 'Border'
39242     
39243     
39244     createBody : function(){
39245         /** This region's body element 
39246         * @type Roo.Element */
39247         this.bodyEl = this.el.createChild({
39248                 tag: "div",
39249                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39250         });
39251     },
39252
39253     onRender: function(ctr, pos)
39254     {
39255         var dh = Roo.DomHelper;
39256         /** This region's container element 
39257         * @type Roo.Element */
39258         this.el = dh.append(ctr.dom, {
39259                 tag: "div",
39260                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39261             }, true);
39262         /** This region's title element 
39263         * @type Roo.Element */
39264     
39265         this.titleEl = dh.append(this.el.dom,  {
39266                 tag: "div",
39267                 unselectable: "on",
39268                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39269                 children:[
39270                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39271                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39272                 ]
39273             }, true);
39274         
39275         this.titleEl.enableDisplayMode();
39276         /** This region's title text element 
39277         * @type HTMLElement */
39278         this.titleTextEl = this.titleEl.dom.firstChild;
39279         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39280         /*
39281         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39282         this.closeBtn.enableDisplayMode();
39283         this.closeBtn.on("click", this.closeClicked, this);
39284         this.closeBtn.hide();
39285     */
39286         this.createBody(this.config);
39287         if(this.config.hideWhenEmpty){
39288             this.hide();
39289             this.on("paneladded", this.validateVisibility, this);
39290             this.on("panelremoved", this.validateVisibility, this);
39291         }
39292         if(this.autoScroll){
39293             this.bodyEl.setStyle("overflow", "auto");
39294         }else{
39295             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39296         }
39297         //if(c.titlebar !== false){
39298             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39299                 this.titleEl.hide();
39300             }else{
39301                 this.titleEl.show();
39302                 if(this.config.title){
39303                     this.titleTextEl.innerHTML = this.config.title;
39304                 }
39305             }
39306         //}
39307         if(this.config.collapsed){
39308             this.collapse(true);
39309         }
39310         if(this.config.hidden){
39311             this.hide();
39312         }
39313         
39314         if (this.unrendered_panels && this.unrendered_panels.length) {
39315             for (var i =0;i< this.unrendered_panels.length; i++) {
39316                 this.add(this.unrendered_panels[i]);
39317             }
39318             this.unrendered_panels = null;
39319             
39320         }
39321         
39322     },
39323     
39324     applyConfig : function(c)
39325     {
39326         /*
39327          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39328             var dh = Roo.DomHelper;
39329             if(c.titlebar !== false){
39330                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39331                 this.collapseBtn.on("click", this.collapse, this);
39332                 this.collapseBtn.enableDisplayMode();
39333                 /*
39334                 if(c.showPin === true || this.showPin){
39335                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39336                     this.stickBtn.enableDisplayMode();
39337                     this.stickBtn.on("click", this.expand, this);
39338                     this.stickBtn.hide();
39339                 }
39340                 
39341             }
39342             */
39343             /** This region's collapsed element
39344             * @type Roo.Element */
39345             /*
39346              *
39347             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39348                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39349             ]}, true);
39350             
39351             if(c.floatable !== false){
39352                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39353                this.collapsedEl.on("click", this.collapseClick, this);
39354             }
39355
39356             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39357                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39358                    id: "message", unselectable: "on", style:{"float":"left"}});
39359                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39360              }
39361             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39362             this.expandBtn.on("click", this.expand, this);
39363             
39364         }
39365         
39366         if(this.collapseBtn){
39367             this.collapseBtn.setVisible(c.collapsible == true);
39368         }
39369         
39370         this.cmargins = c.cmargins || this.cmargins ||
39371                          (this.position == "west" || this.position == "east" ?
39372                              {top: 0, left: 2, right:2, bottom: 0} :
39373                              {top: 2, left: 0, right:0, bottom: 2});
39374         */
39375         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39376         
39377         
39378         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39379         
39380         this.autoScroll = c.autoScroll || false;
39381         
39382         
39383        
39384         
39385         this.duration = c.duration || .30;
39386         this.slideDuration = c.slideDuration || .45;
39387         this.config = c;
39388        
39389     },
39390     /**
39391      * Returns true if this region is currently visible.
39392      * @return {Boolean}
39393      */
39394     isVisible : function(){
39395         return this.visible;
39396     },
39397
39398     /**
39399      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39400      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39401      */
39402     //setCollapsedTitle : function(title){
39403     //    title = title || "&#160;";
39404      //   if(this.collapsedTitleTextEl){
39405       //      this.collapsedTitleTextEl.innerHTML = title;
39406        // }
39407     //},
39408
39409     getBox : function(){
39410         var b;
39411       //  if(!this.collapsed){
39412             b = this.el.getBox(false, true);
39413        // }else{
39414           //  b = this.collapsedEl.getBox(false, true);
39415         //}
39416         return b;
39417     },
39418
39419     getMargins : function(){
39420         return this.margins;
39421         //return this.collapsed ? this.cmargins : this.margins;
39422     },
39423 /*
39424     highlight : function(){
39425         this.el.addClass("x-layout-panel-dragover");
39426     },
39427
39428     unhighlight : function(){
39429         this.el.removeClass("x-layout-panel-dragover");
39430     },
39431 */
39432     updateBox : function(box)
39433     {
39434         if (!this.bodyEl) {
39435             return; // not rendered yet..
39436         }
39437         
39438         this.box = box;
39439         if(!this.collapsed){
39440             this.el.dom.style.left = box.x + "px";
39441             this.el.dom.style.top = box.y + "px";
39442             this.updateBody(box.width, box.height);
39443         }else{
39444             this.collapsedEl.dom.style.left = box.x + "px";
39445             this.collapsedEl.dom.style.top = box.y + "px";
39446             this.collapsedEl.setSize(box.width, box.height);
39447         }
39448         if(this.tabs){
39449             this.tabs.autoSizeTabs();
39450         }
39451     },
39452
39453     updateBody : function(w, h)
39454     {
39455         if(w !== null){
39456             this.el.setWidth(w);
39457             w -= this.el.getBorderWidth("rl");
39458             if(this.config.adjustments){
39459                 w += this.config.adjustments[0];
39460             }
39461         }
39462         if(h !== null && h > 0){
39463             this.el.setHeight(h);
39464             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39465             h -= this.el.getBorderWidth("tb");
39466             if(this.config.adjustments){
39467                 h += this.config.adjustments[1];
39468             }
39469             this.bodyEl.setHeight(h);
39470             if(this.tabs){
39471                 h = this.tabs.syncHeight(h);
39472             }
39473         }
39474         if(this.panelSize){
39475             w = w !== null ? w : this.panelSize.width;
39476             h = h !== null ? h : this.panelSize.height;
39477         }
39478         if(this.activePanel){
39479             var el = this.activePanel.getEl();
39480             w = w !== null ? w : el.getWidth();
39481             h = h !== null ? h : el.getHeight();
39482             this.panelSize = {width: w, height: h};
39483             this.activePanel.setSize(w, h);
39484         }
39485         if(Roo.isIE && this.tabs){
39486             this.tabs.el.repaint();
39487         }
39488     },
39489
39490     /**
39491      * Returns the container element for this region.
39492      * @return {Roo.Element}
39493      */
39494     getEl : function(){
39495         return this.el;
39496     },
39497
39498     /**
39499      * Hides this region.
39500      */
39501     hide : function(){
39502         //if(!this.collapsed){
39503             this.el.dom.style.left = "-2000px";
39504             this.el.hide();
39505         //}else{
39506          //   this.collapsedEl.dom.style.left = "-2000px";
39507          //   this.collapsedEl.hide();
39508        // }
39509         this.visible = false;
39510         this.fireEvent("visibilitychange", this, false);
39511     },
39512
39513     /**
39514      * Shows this region if it was previously hidden.
39515      */
39516     show : function(){
39517         //if(!this.collapsed){
39518             this.el.show();
39519         //}else{
39520         //    this.collapsedEl.show();
39521        // }
39522         this.visible = true;
39523         this.fireEvent("visibilitychange", this, true);
39524     },
39525 /*
39526     closeClicked : function(){
39527         if(this.activePanel){
39528             this.remove(this.activePanel);
39529         }
39530     },
39531
39532     collapseClick : function(e){
39533         if(this.isSlid){
39534            e.stopPropagation();
39535            this.slideIn();
39536         }else{
39537            e.stopPropagation();
39538            this.slideOut();
39539         }
39540     },
39541 */
39542     /**
39543      * Collapses this region.
39544      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39545      */
39546     /*
39547     collapse : function(skipAnim, skipCheck = false){
39548         if(this.collapsed) {
39549             return;
39550         }
39551         
39552         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39553             
39554             this.collapsed = true;
39555             if(this.split){
39556                 this.split.el.hide();
39557             }
39558             if(this.config.animate && skipAnim !== true){
39559                 this.fireEvent("invalidated", this);
39560                 this.animateCollapse();
39561             }else{
39562                 this.el.setLocation(-20000,-20000);
39563                 this.el.hide();
39564                 this.collapsedEl.show();
39565                 this.fireEvent("collapsed", this);
39566                 this.fireEvent("invalidated", this);
39567             }
39568         }
39569         
39570     },
39571 */
39572     animateCollapse : function(){
39573         // overridden
39574     },
39575
39576     /**
39577      * Expands this region if it was previously collapsed.
39578      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39579      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39580      */
39581     /*
39582     expand : function(e, skipAnim){
39583         if(e) {
39584             e.stopPropagation();
39585         }
39586         if(!this.collapsed || this.el.hasActiveFx()) {
39587             return;
39588         }
39589         if(this.isSlid){
39590             this.afterSlideIn();
39591             skipAnim = true;
39592         }
39593         this.collapsed = false;
39594         if(this.config.animate && skipAnim !== true){
39595             this.animateExpand();
39596         }else{
39597             this.el.show();
39598             if(this.split){
39599                 this.split.el.show();
39600             }
39601             this.collapsedEl.setLocation(-2000,-2000);
39602             this.collapsedEl.hide();
39603             this.fireEvent("invalidated", this);
39604             this.fireEvent("expanded", this);
39605         }
39606     },
39607 */
39608     animateExpand : function(){
39609         // overridden
39610     },
39611
39612     initTabs : function()
39613     {
39614         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39615         
39616         var ts = new Roo.bootstrap.panel.Tabs({
39617             el: this.bodyEl.dom,
39618             region : this,
39619             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39620             disableTooltips: this.config.disableTabTips,
39621             toolbar : this.config.toolbar
39622         });
39623         
39624         if(this.config.hideTabs){
39625             ts.stripWrap.setDisplayed(false);
39626         }
39627         this.tabs = ts;
39628         ts.resizeTabs = this.config.resizeTabs === true;
39629         ts.minTabWidth = this.config.minTabWidth || 40;
39630         ts.maxTabWidth = this.config.maxTabWidth || 250;
39631         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39632         ts.monitorResize = false;
39633         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39634         ts.bodyEl.addClass('roo-layout-tabs-body');
39635         this.panels.each(this.initPanelAsTab, this);
39636     },
39637
39638     initPanelAsTab : function(panel){
39639         var ti = this.tabs.addTab(
39640             panel.getEl().id,
39641             panel.getTitle(),
39642             null,
39643             this.config.closeOnTab && panel.isClosable(),
39644             panel.tpl
39645         );
39646         if(panel.tabTip !== undefined){
39647             ti.setTooltip(panel.tabTip);
39648         }
39649         ti.on("activate", function(){
39650               this.setActivePanel(panel);
39651         }, this);
39652         
39653         if(this.config.closeOnTab){
39654             ti.on("beforeclose", function(t, e){
39655                 e.cancel = true;
39656                 this.remove(panel);
39657             }, this);
39658         }
39659         
39660         panel.tabItem = ti;
39661         
39662         return ti;
39663     },
39664
39665     updatePanelTitle : function(panel, title)
39666     {
39667         if(this.activePanel == panel){
39668             this.updateTitle(title);
39669         }
39670         if(this.tabs){
39671             var ti = this.tabs.getTab(panel.getEl().id);
39672             ti.setText(title);
39673             if(panel.tabTip !== undefined){
39674                 ti.setTooltip(panel.tabTip);
39675             }
39676         }
39677     },
39678
39679     updateTitle : function(title){
39680         if(this.titleTextEl && !this.config.title){
39681             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39682         }
39683     },
39684
39685     setActivePanel : function(panel)
39686     {
39687         panel = this.getPanel(panel);
39688         if(this.activePanel && this.activePanel != panel){
39689             if(this.activePanel.setActiveState(false) === false){
39690                 return;
39691             }
39692         }
39693         this.activePanel = panel;
39694         panel.setActiveState(true);
39695         if(this.panelSize){
39696             panel.setSize(this.panelSize.width, this.panelSize.height);
39697         }
39698         if(this.closeBtn){
39699             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39700         }
39701         this.updateTitle(panel.getTitle());
39702         if(this.tabs){
39703             this.fireEvent("invalidated", this);
39704         }
39705         this.fireEvent("panelactivated", this, panel);
39706     },
39707
39708     /**
39709      * Shows the specified panel.
39710      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39711      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39712      */
39713     showPanel : function(panel)
39714     {
39715         panel = this.getPanel(panel);
39716         if(panel){
39717             if(this.tabs){
39718                 var tab = this.tabs.getTab(panel.getEl().id);
39719                 if(tab.isHidden()){
39720                     this.tabs.unhideTab(tab.id);
39721                 }
39722                 tab.activate();
39723             }else{
39724                 this.setActivePanel(panel);
39725             }
39726         }
39727         return panel;
39728     },
39729
39730     /**
39731      * Get the active panel for this region.
39732      * @return {Roo.ContentPanel} The active panel or null
39733      */
39734     getActivePanel : function(){
39735         return this.activePanel;
39736     },
39737
39738     validateVisibility : function(){
39739         if(this.panels.getCount() < 1){
39740             this.updateTitle("&#160;");
39741             this.closeBtn.hide();
39742             this.hide();
39743         }else{
39744             if(!this.isVisible()){
39745                 this.show();
39746             }
39747         }
39748     },
39749
39750     /**
39751      * Adds the passed ContentPanel(s) to this region.
39752      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39753      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39754      */
39755     add : function(panel)
39756     {
39757         if(arguments.length > 1){
39758             for(var i = 0, len = arguments.length; i < len; i++) {
39759                 this.add(arguments[i]);
39760             }
39761             return null;
39762         }
39763         
39764         // if we have not been rendered yet, then we can not really do much of this..
39765         if (!this.bodyEl) {
39766             this.unrendered_panels.push(panel);
39767             return panel;
39768         }
39769         
39770         
39771         
39772         
39773         if(this.hasPanel(panel)){
39774             this.showPanel(panel);
39775             return panel;
39776         }
39777         panel.setRegion(this);
39778         this.panels.add(panel);
39779        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39780             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39781             // and hide them... ???
39782             this.bodyEl.dom.appendChild(panel.getEl().dom);
39783             if(panel.background !== true){
39784                 this.setActivePanel(panel);
39785             }
39786             this.fireEvent("paneladded", this, panel);
39787             return panel;
39788         }
39789         */
39790         if(!this.tabs){
39791             this.initTabs();
39792         }else{
39793             this.initPanelAsTab(panel);
39794         }
39795         
39796         
39797         if(panel.background !== true){
39798             this.tabs.activate(panel.getEl().id);
39799         }
39800         this.fireEvent("paneladded", this, panel);
39801         return panel;
39802     },
39803
39804     /**
39805      * Hides the tab for the specified panel.
39806      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39807      */
39808     hidePanel : function(panel){
39809         if(this.tabs && (panel = this.getPanel(panel))){
39810             this.tabs.hideTab(panel.getEl().id);
39811         }
39812     },
39813
39814     /**
39815      * Unhides the tab for a previously hidden panel.
39816      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39817      */
39818     unhidePanel : function(panel){
39819         if(this.tabs && (panel = this.getPanel(panel))){
39820             this.tabs.unhideTab(panel.getEl().id);
39821         }
39822     },
39823
39824     clearPanels : function(){
39825         while(this.panels.getCount() > 0){
39826              this.remove(this.panels.first());
39827         }
39828     },
39829
39830     /**
39831      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39832      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39833      * @param {Boolean} preservePanel Overrides the config preservePanel option
39834      * @return {Roo.ContentPanel} The panel that was removed
39835      */
39836     remove : function(panel, preservePanel)
39837     {
39838         panel = this.getPanel(panel);
39839         if(!panel){
39840             return null;
39841         }
39842         var e = {};
39843         this.fireEvent("beforeremove", this, panel, e);
39844         if(e.cancel === true){
39845             return null;
39846         }
39847         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39848         var panelId = panel.getId();
39849         this.panels.removeKey(panelId);
39850         if(preservePanel){
39851             document.body.appendChild(panel.getEl().dom);
39852         }
39853         if(this.tabs){
39854             this.tabs.removeTab(panel.getEl().id);
39855         }else if (!preservePanel){
39856             this.bodyEl.dom.removeChild(panel.getEl().dom);
39857         }
39858         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39859             var p = this.panels.first();
39860             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39861             tempEl.appendChild(p.getEl().dom);
39862             this.bodyEl.update("");
39863             this.bodyEl.dom.appendChild(p.getEl().dom);
39864             tempEl = null;
39865             this.updateTitle(p.getTitle());
39866             this.tabs = null;
39867             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39868             this.setActivePanel(p);
39869         }
39870         panel.setRegion(null);
39871         if(this.activePanel == panel){
39872             this.activePanel = null;
39873         }
39874         if(this.config.autoDestroy !== false && preservePanel !== true){
39875             try{panel.destroy();}catch(e){}
39876         }
39877         this.fireEvent("panelremoved", this, panel);
39878         return panel;
39879     },
39880
39881     /**
39882      * Returns the TabPanel component used by this region
39883      * @return {Roo.TabPanel}
39884      */
39885     getTabs : function(){
39886         return this.tabs;
39887     },
39888
39889     createTool : function(parentEl, className){
39890         var btn = Roo.DomHelper.append(parentEl, {
39891             tag: "div",
39892             cls: "x-layout-tools-button",
39893             children: [ {
39894                 tag: "div",
39895                 cls: "roo-layout-tools-button-inner " + className,
39896                 html: "&#160;"
39897             }]
39898         }, true);
39899         btn.addClassOnOver("roo-layout-tools-button-over");
39900         return btn;
39901     }
39902 });/*
39903  * Based on:
39904  * Ext JS Library 1.1.1
39905  * Copyright(c) 2006-2007, Ext JS, LLC.
39906  *
39907  * Originally Released Under LGPL - original licence link has changed is not relivant.
39908  *
39909  * Fork - LGPL
39910  * <script type="text/javascript">
39911  */
39912  
39913
39914
39915 /**
39916  * @class Roo.SplitLayoutRegion
39917  * @extends Roo.LayoutRegion
39918  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39919  */
39920 Roo.bootstrap.layout.Split = function(config){
39921     this.cursor = config.cursor;
39922     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39923 };
39924
39925 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39926 {
39927     splitTip : "Drag to resize.",
39928     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39929     useSplitTips : false,
39930
39931     applyConfig : function(config){
39932         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39933     },
39934     
39935     onRender : function(ctr,pos) {
39936         
39937         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39938         if(!this.config.split){
39939             return;
39940         }
39941         if(!this.split){
39942             
39943             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39944                             tag: "div",
39945                             id: this.el.id + "-split",
39946                             cls: "roo-layout-split roo-layout-split-"+this.position,
39947                             html: "&#160;"
39948             });
39949             /** The SplitBar for this region 
39950             * @type Roo.SplitBar */
39951             // does not exist yet...
39952             Roo.log([this.position, this.orientation]);
39953             
39954             this.split = new Roo.bootstrap.SplitBar({
39955                 dragElement : splitEl,
39956                 resizingElement: this.el,
39957                 orientation : this.orientation
39958             });
39959             
39960             this.split.on("moved", this.onSplitMove, this);
39961             this.split.useShim = this.config.useShim === true;
39962             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39963             if(this.useSplitTips){
39964                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39965             }
39966             //if(config.collapsible){
39967             //    this.split.el.on("dblclick", this.collapse,  this);
39968             //}
39969         }
39970         if(typeof this.config.minSize != "undefined"){
39971             this.split.minSize = this.config.minSize;
39972         }
39973         if(typeof this.config.maxSize != "undefined"){
39974             this.split.maxSize = this.config.maxSize;
39975         }
39976         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39977             this.hideSplitter();
39978         }
39979         
39980     },
39981
39982     getHMaxSize : function(){
39983          var cmax = this.config.maxSize || 10000;
39984          var center = this.mgr.getRegion("center");
39985          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39986     },
39987
39988     getVMaxSize : function(){
39989          var cmax = this.config.maxSize || 10000;
39990          var center = this.mgr.getRegion("center");
39991          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39992     },
39993
39994     onSplitMove : function(split, newSize){
39995         this.fireEvent("resized", this, newSize);
39996     },
39997     
39998     /** 
39999      * Returns the {@link Roo.SplitBar} for this region.
40000      * @return {Roo.SplitBar}
40001      */
40002     getSplitBar : function(){
40003         return this.split;
40004     },
40005     
40006     hide : function(){
40007         this.hideSplitter();
40008         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40009     },
40010
40011     hideSplitter : function(){
40012         if(this.split){
40013             this.split.el.setLocation(-2000,-2000);
40014             this.split.el.hide();
40015         }
40016     },
40017
40018     show : function(){
40019         if(this.split){
40020             this.split.el.show();
40021         }
40022         Roo.bootstrap.layout.Split.superclass.show.call(this);
40023     },
40024     
40025     beforeSlide: function(){
40026         if(Roo.isGecko){// firefox overflow auto bug workaround
40027             this.bodyEl.clip();
40028             if(this.tabs) {
40029                 this.tabs.bodyEl.clip();
40030             }
40031             if(this.activePanel){
40032                 this.activePanel.getEl().clip();
40033                 
40034                 if(this.activePanel.beforeSlide){
40035                     this.activePanel.beforeSlide();
40036                 }
40037             }
40038         }
40039     },
40040     
40041     afterSlide : function(){
40042         if(Roo.isGecko){// firefox overflow auto bug workaround
40043             this.bodyEl.unclip();
40044             if(this.tabs) {
40045                 this.tabs.bodyEl.unclip();
40046             }
40047             if(this.activePanel){
40048                 this.activePanel.getEl().unclip();
40049                 if(this.activePanel.afterSlide){
40050                     this.activePanel.afterSlide();
40051                 }
40052             }
40053         }
40054     },
40055
40056     initAutoHide : function(){
40057         if(this.autoHide !== false){
40058             if(!this.autoHideHd){
40059                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40060                 this.autoHideHd = {
40061                     "mouseout": function(e){
40062                         if(!e.within(this.el, true)){
40063                             st.delay(500);
40064                         }
40065                     },
40066                     "mouseover" : function(e){
40067                         st.cancel();
40068                     },
40069                     scope : this
40070                 };
40071             }
40072             this.el.on(this.autoHideHd);
40073         }
40074     },
40075
40076     clearAutoHide : function(){
40077         if(this.autoHide !== false){
40078             this.el.un("mouseout", this.autoHideHd.mouseout);
40079             this.el.un("mouseover", this.autoHideHd.mouseover);
40080         }
40081     },
40082
40083     clearMonitor : function(){
40084         Roo.get(document).un("click", this.slideInIf, this);
40085     },
40086
40087     // these names are backwards but not changed for compat
40088     slideOut : function(){
40089         if(this.isSlid || this.el.hasActiveFx()){
40090             return;
40091         }
40092         this.isSlid = true;
40093         if(this.collapseBtn){
40094             this.collapseBtn.hide();
40095         }
40096         this.closeBtnState = this.closeBtn.getStyle('display');
40097         this.closeBtn.hide();
40098         if(this.stickBtn){
40099             this.stickBtn.show();
40100         }
40101         this.el.show();
40102         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40103         this.beforeSlide();
40104         this.el.setStyle("z-index", 10001);
40105         this.el.slideIn(this.getSlideAnchor(), {
40106             callback: function(){
40107                 this.afterSlide();
40108                 this.initAutoHide();
40109                 Roo.get(document).on("click", this.slideInIf, this);
40110                 this.fireEvent("slideshow", this);
40111             },
40112             scope: this,
40113             block: true
40114         });
40115     },
40116
40117     afterSlideIn : function(){
40118         this.clearAutoHide();
40119         this.isSlid = false;
40120         this.clearMonitor();
40121         this.el.setStyle("z-index", "");
40122         if(this.collapseBtn){
40123             this.collapseBtn.show();
40124         }
40125         this.closeBtn.setStyle('display', this.closeBtnState);
40126         if(this.stickBtn){
40127             this.stickBtn.hide();
40128         }
40129         this.fireEvent("slidehide", this);
40130     },
40131
40132     slideIn : function(cb){
40133         if(!this.isSlid || this.el.hasActiveFx()){
40134             Roo.callback(cb);
40135             return;
40136         }
40137         this.isSlid = false;
40138         this.beforeSlide();
40139         this.el.slideOut(this.getSlideAnchor(), {
40140             callback: function(){
40141                 this.el.setLeftTop(-10000, -10000);
40142                 this.afterSlide();
40143                 this.afterSlideIn();
40144                 Roo.callback(cb);
40145             },
40146             scope: this,
40147             block: true
40148         });
40149     },
40150     
40151     slideInIf : function(e){
40152         if(!e.within(this.el)){
40153             this.slideIn();
40154         }
40155     },
40156
40157     animateCollapse : function(){
40158         this.beforeSlide();
40159         this.el.setStyle("z-index", 20000);
40160         var anchor = this.getSlideAnchor();
40161         this.el.slideOut(anchor, {
40162             callback : function(){
40163                 this.el.setStyle("z-index", "");
40164                 this.collapsedEl.slideIn(anchor, {duration:.3});
40165                 this.afterSlide();
40166                 this.el.setLocation(-10000,-10000);
40167                 this.el.hide();
40168                 this.fireEvent("collapsed", this);
40169             },
40170             scope: this,
40171             block: true
40172         });
40173     },
40174
40175     animateExpand : function(){
40176         this.beforeSlide();
40177         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40178         this.el.setStyle("z-index", 20000);
40179         this.collapsedEl.hide({
40180             duration:.1
40181         });
40182         this.el.slideIn(this.getSlideAnchor(), {
40183             callback : function(){
40184                 this.el.setStyle("z-index", "");
40185                 this.afterSlide();
40186                 if(this.split){
40187                     this.split.el.show();
40188                 }
40189                 this.fireEvent("invalidated", this);
40190                 this.fireEvent("expanded", this);
40191             },
40192             scope: this,
40193             block: true
40194         });
40195     },
40196
40197     anchors : {
40198         "west" : "left",
40199         "east" : "right",
40200         "north" : "top",
40201         "south" : "bottom"
40202     },
40203
40204     sanchors : {
40205         "west" : "l",
40206         "east" : "r",
40207         "north" : "t",
40208         "south" : "b"
40209     },
40210
40211     canchors : {
40212         "west" : "tl-tr",
40213         "east" : "tr-tl",
40214         "north" : "tl-bl",
40215         "south" : "bl-tl"
40216     },
40217
40218     getAnchor : function(){
40219         return this.anchors[this.position];
40220     },
40221
40222     getCollapseAnchor : function(){
40223         return this.canchors[this.position];
40224     },
40225
40226     getSlideAnchor : function(){
40227         return this.sanchors[this.position];
40228     },
40229
40230     getAlignAdj : function(){
40231         var cm = this.cmargins;
40232         switch(this.position){
40233             case "west":
40234                 return [0, 0];
40235             break;
40236             case "east":
40237                 return [0, 0];
40238             break;
40239             case "north":
40240                 return [0, 0];
40241             break;
40242             case "south":
40243                 return [0, 0];
40244             break;
40245         }
40246     },
40247
40248     getExpandAdj : function(){
40249         var c = this.collapsedEl, cm = this.cmargins;
40250         switch(this.position){
40251             case "west":
40252                 return [-(cm.right+c.getWidth()+cm.left), 0];
40253             break;
40254             case "east":
40255                 return [cm.right+c.getWidth()+cm.left, 0];
40256             break;
40257             case "north":
40258                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40259             break;
40260             case "south":
40261                 return [0, cm.top+cm.bottom+c.getHeight()];
40262             break;
40263         }
40264     }
40265 });/*
40266  * Based on:
40267  * Ext JS Library 1.1.1
40268  * Copyright(c) 2006-2007, Ext JS, LLC.
40269  *
40270  * Originally Released Under LGPL - original licence link has changed is not relivant.
40271  *
40272  * Fork - LGPL
40273  * <script type="text/javascript">
40274  */
40275 /*
40276  * These classes are private internal classes
40277  */
40278 Roo.bootstrap.layout.Center = function(config){
40279     config.region = "center";
40280     Roo.bootstrap.layout.Region.call(this, config);
40281     this.visible = true;
40282     this.minWidth = config.minWidth || 20;
40283     this.minHeight = config.minHeight || 20;
40284 };
40285
40286 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40287     hide : function(){
40288         // center panel can't be hidden
40289     },
40290     
40291     show : function(){
40292         // center panel can't be hidden
40293     },
40294     
40295     getMinWidth: function(){
40296         return this.minWidth;
40297     },
40298     
40299     getMinHeight: function(){
40300         return this.minHeight;
40301     }
40302 });
40303
40304
40305
40306
40307  
40308
40309
40310
40311
40312
40313
40314 Roo.bootstrap.layout.North = function(config)
40315 {
40316     config.region = 'north';
40317     config.cursor = 'n-resize';
40318     
40319     Roo.bootstrap.layout.Split.call(this, config);
40320     
40321     
40322     if(this.split){
40323         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40324         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40325         this.split.el.addClass("roo-layout-split-v");
40326     }
40327     //var size = config.initialSize || config.height;
40328     //if(this.el && typeof size != "undefined"){
40329     //    this.el.setHeight(size);
40330     //}
40331 };
40332 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40333 {
40334     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40335      
40336      
40337     onRender : function(ctr, pos)
40338     {
40339         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40340         var size = this.config.initialSize || this.config.height;
40341         if(this.el && typeof size != "undefined"){
40342             this.el.setHeight(size);
40343         }
40344     
40345     },
40346     
40347     getBox : function(){
40348         if(this.collapsed){
40349             return this.collapsedEl.getBox();
40350         }
40351         var box = this.el.getBox();
40352         if(this.split){
40353             box.height += this.split.el.getHeight();
40354         }
40355         return box;
40356     },
40357     
40358     updateBox : function(box){
40359         if(this.split && !this.collapsed){
40360             box.height -= this.split.el.getHeight();
40361             this.split.el.setLeft(box.x);
40362             this.split.el.setTop(box.y+box.height);
40363             this.split.el.setWidth(box.width);
40364         }
40365         if(this.collapsed){
40366             this.updateBody(box.width, null);
40367         }
40368         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40369     }
40370 });
40371
40372
40373
40374
40375
40376 Roo.bootstrap.layout.South = function(config){
40377     config.region = 'south';
40378     config.cursor = 's-resize';
40379     Roo.bootstrap.layout.Split.call(this, config);
40380     if(this.split){
40381         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40382         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40383         this.split.el.addClass("roo-layout-split-v");
40384     }
40385     
40386 };
40387
40388 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40389     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40390     
40391     onRender : function(ctr, pos)
40392     {
40393         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40394         var size = this.config.initialSize || this.config.height;
40395         if(this.el && typeof size != "undefined"){
40396             this.el.setHeight(size);
40397         }
40398     
40399     },
40400     
40401     getBox : function(){
40402         if(this.collapsed){
40403             return this.collapsedEl.getBox();
40404         }
40405         var box = this.el.getBox();
40406         if(this.split){
40407             var sh = this.split.el.getHeight();
40408             box.height += sh;
40409             box.y -= sh;
40410         }
40411         return box;
40412     },
40413     
40414     updateBox : function(box){
40415         if(this.split && !this.collapsed){
40416             var sh = this.split.el.getHeight();
40417             box.height -= sh;
40418             box.y += sh;
40419             this.split.el.setLeft(box.x);
40420             this.split.el.setTop(box.y-sh);
40421             this.split.el.setWidth(box.width);
40422         }
40423         if(this.collapsed){
40424             this.updateBody(box.width, null);
40425         }
40426         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40427     }
40428 });
40429
40430 Roo.bootstrap.layout.East = function(config){
40431     config.region = "east";
40432     config.cursor = "e-resize";
40433     Roo.bootstrap.layout.Split.call(this, config);
40434     if(this.split){
40435         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40436         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40437         this.split.el.addClass("roo-layout-split-h");
40438     }
40439     
40440 };
40441 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40442     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40443     
40444     onRender : function(ctr, pos)
40445     {
40446         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40447         var size = this.config.initialSize || this.config.width;
40448         if(this.el && typeof size != "undefined"){
40449             this.el.setWidth(size);
40450         }
40451     
40452     },
40453     
40454     getBox : function(){
40455         if(this.collapsed){
40456             return this.collapsedEl.getBox();
40457         }
40458         var box = this.el.getBox();
40459         if(this.split){
40460             var sw = this.split.el.getWidth();
40461             box.width += sw;
40462             box.x -= sw;
40463         }
40464         return box;
40465     },
40466
40467     updateBox : function(box){
40468         if(this.split && !this.collapsed){
40469             var sw = this.split.el.getWidth();
40470             box.width -= sw;
40471             this.split.el.setLeft(box.x);
40472             this.split.el.setTop(box.y);
40473             this.split.el.setHeight(box.height);
40474             box.x += sw;
40475         }
40476         if(this.collapsed){
40477             this.updateBody(null, box.height);
40478         }
40479         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40480     }
40481 });
40482
40483 Roo.bootstrap.layout.West = function(config){
40484     config.region = "west";
40485     config.cursor = "w-resize";
40486     
40487     Roo.bootstrap.layout.Split.call(this, config);
40488     if(this.split){
40489         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40490         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40491         this.split.el.addClass("roo-layout-split-h");
40492     }
40493     
40494 };
40495 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40496     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40497     
40498     onRender: function(ctr, pos)
40499     {
40500         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40501         var size = this.config.initialSize || this.config.width;
40502         if(typeof size != "undefined"){
40503             this.el.setWidth(size);
40504         }
40505     },
40506     
40507     getBox : function(){
40508         if(this.collapsed){
40509             return this.collapsedEl.getBox();
40510         }
40511         var box = this.el.getBox();
40512         if (box.width == 0) {
40513             box.width = this.config.width; // kludge?
40514         }
40515         if(this.split){
40516             box.width += this.split.el.getWidth();
40517         }
40518         return box;
40519     },
40520     
40521     updateBox : function(box){
40522         if(this.split && !this.collapsed){
40523             var sw = this.split.el.getWidth();
40524             box.width -= sw;
40525             this.split.el.setLeft(box.x+box.width);
40526             this.split.el.setTop(box.y);
40527             this.split.el.setHeight(box.height);
40528         }
40529         if(this.collapsed){
40530             this.updateBody(null, box.height);
40531         }
40532         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40533     }
40534 });Roo.namespace("Roo.bootstrap.panel");/*
40535  * Based on:
40536  * Ext JS Library 1.1.1
40537  * Copyright(c) 2006-2007, Ext JS, LLC.
40538  *
40539  * Originally Released Under LGPL - original licence link has changed is not relivant.
40540  *
40541  * Fork - LGPL
40542  * <script type="text/javascript">
40543  */
40544 /**
40545  * @class Roo.ContentPanel
40546  * @extends Roo.util.Observable
40547  * A basic ContentPanel element.
40548  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40549  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40550  * @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
40551  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40552  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40553  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40554  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40555  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40556  * @cfg {String} title          The title for this panel
40557  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40558  * @cfg {String} url            Calls {@link #setUrl} with this value
40559  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40560  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40561  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40562  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40563  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40564  * @cfg {Boolean} badges render the badges
40565  * @cfg {String} cls  extra classes to use  
40566  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40567
40568  * @constructor
40569  * Create a new ContentPanel.
40570  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40571  * @param {String/Object} config A string to set only the title or a config object
40572  * @param {String} content (optional) Set the HTML content for this panel
40573  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40574  */
40575 Roo.bootstrap.panel.Content = function( config){
40576     
40577     this.tpl = config.tpl || false;
40578     
40579     var el = config.el;
40580     var content = config.content;
40581
40582     if(config.autoCreate){ // xtype is available if this is called from factory
40583         el = Roo.id();
40584     }
40585     this.el = Roo.get(el);
40586     if(!this.el && config && config.autoCreate){
40587         if(typeof config.autoCreate == "object"){
40588             if(!config.autoCreate.id){
40589                 config.autoCreate.id = config.id||el;
40590             }
40591             this.el = Roo.DomHelper.append(document.body,
40592                         config.autoCreate, true);
40593         }else{
40594             var elcfg =  {
40595                 tag: "div",
40596                 cls: (config.cls || '') +
40597                     (config.background ? ' bg-' + config.background : '') +
40598                     " roo-layout-inactive-content",
40599                 id: config.id||el
40600             };
40601             if (config.iframe) {
40602                 elcfg.cn = [
40603                     {
40604                         tag : 'iframe',
40605                         style : 'border: 0px',
40606                         src : 'about:blank'
40607                     }
40608                 ];
40609             }
40610               
40611             if (config.html) {
40612                 elcfg.html = config.html;
40613                 
40614             }
40615                         
40616             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40617             if (config.iframe) {
40618                 this.iframeEl = this.el.select('iframe',true).first();
40619             }
40620             
40621         }
40622     } 
40623     this.closable = false;
40624     this.loaded = false;
40625     this.active = false;
40626    
40627       
40628     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40629         
40630         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40631         
40632         this.wrapEl = this.el; //this.el.wrap();
40633         var ti = [];
40634         if (config.toolbar.items) {
40635             ti = config.toolbar.items ;
40636             delete config.toolbar.items ;
40637         }
40638         
40639         var nitems = [];
40640         this.toolbar.render(this.wrapEl, 'before');
40641         for(var i =0;i < ti.length;i++) {
40642           //  Roo.log(['add child', items[i]]);
40643             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40644         }
40645         this.toolbar.items = nitems;
40646         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40647         delete config.toolbar;
40648         
40649     }
40650     /*
40651     // xtype created footer. - not sure if will work as we normally have to render first..
40652     if (this.footer && !this.footer.el && this.footer.xtype) {
40653         if (!this.wrapEl) {
40654             this.wrapEl = this.el.wrap();
40655         }
40656     
40657         this.footer.container = this.wrapEl.createChild();
40658          
40659         this.footer = Roo.factory(this.footer, Roo);
40660         
40661     }
40662     */
40663     
40664      if(typeof config == "string"){
40665         this.title = config;
40666     }else{
40667         Roo.apply(this, config);
40668     }
40669     
40670     if(this.resizeEl){
40671         this.resizeEl = Roo.get(this.resizeEl, true);
40672     }else{
40673         this.resizeEl = this.el;
40674     }
40675     // handle view.xtype
40676     
40677  
40678     
40679     
40680     this.addEvents({
40681         /**
40682          * @event activate
40683          * Fires when this panel is activated. 
40684          * @param {Roo.ContentPanel} this
40685          */
40686         "activate" : true,
40687         /**
40688          * @event deactivate
40689          * Fires when this panel is activated. 
40690          * @param {Roo.ContentPanel} this
40691          */
40692         "deactivate" : true,
40693
40694         /**
40695          * @event resize
40696          * Fires when this panel is resized if fitToFrame is true.
40697          * @param {Roo.ContentPanel} this
40698          * @param {Number} width The width after any component adjustments
40699          * @param {Number} height The height after any component adjustments
40700          */
40701         "resize" : true,
40702         
40703          /**
40704          * @event render
40705          * Fires when this tab is created
40706          * @param {Roo.ContentPanel} this
40707          */
40708         "render" : true,
40709         
40710           /**
40711          * @event scroll
40712          * Fires when this content is scrolled
40713          * @param {Roo.ContentPanel} this
40714          * @param {Event} scrollEvent
40715          */
40716         "scroll" : true
40717         
40718         
40719         
40720     });
40721     
40722
40723     
40724     
40725     if(this.autoScroll && !this.iframe){
40726         this.resizeEl.setStyle("overflow", "auto");
40727         this.resizeEl.on('scroll', this.onScroll, this);
40728     } else {
40729         // fix randome scrolling
40730         //this.el.on('scroll', function() {
40731         //    Roo.log('fix random scolling');
40732         //    this.scrollTo('top',0); 
40733         //});
40734     }
40735     content = content || this.content;
40736     if(content){
40737         this.setContent(content);
40738     }
40739     if(config && config.url){
40740         this.setUrl(this.url, this.params, this.loadOnce);
40741     }
40742     
40743     
40744     
40745     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40746     
40747     if (this.view && typeof(this.view.xtype) != 'undefined') {
40748         this.view.el = this.el.appendChild(document.createElement("div"));
40749         this.view = Roo.factory(this.view); 
40750         this.view.render  &&  this.view.render(false, '');  
40751     }
40752     
40753     
40754     this.fireEvent('render', this);
40755 };
40756
40757 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40758     
40759     cls : '',
40760     background : '',
40761     
40762     tabTip : '',
40763     
40764     iframe : false,
40765     iframeEl : false,
40766     
40767     /* Resize Element - use this to work out scroll etc. */
40768     resizeEl : false,
40769     
40770     setRegion : function(region){
40771         this.region = region;
40772         this.setActiveClass(region && !this.background);
40773     },
40774     
40775     
40776     setActiveClass: function(state)
40777     {
40778         if(state){
40779            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40780            this.el.setStyle('position','relative');
40781         }else{
40782            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40783            this.el.setStyle('position', 'absolute');
40784         } 
40785     },
40786     
40787     /**
40788      * Returns the toolbar for this Panel if one was configured. 
40789      * @return {Roo.Toolbar} 
40790      */
40791     getToolbar : function(){
40792         return this.toolbar;
40793     },
40794     
40795     setActiveState : function(active)
40796     {
40797         this.active = active;
40798         this.setActiveClass(active);
40799         if(!active){
40800             if(this.fireEvent("deactivate", this) === false){
40801                 return false;
40802             }
40803             return true;
40804         }
40805         this.fireEvent("activate", this);
40806         return true;
40807     },
40808     /**
40809      * Updates this panel's element (not for iframe)
40810      * @param {String} content The new content
40811      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40812     */
40813     setContent : function(content, loadScripts){
40814         if (this.iframe) {
40815             return;
40816         }
40817         
40818         this.el.update(content, loadScripts);
40819     },
40820
40821     ignoreResize : function(w, h){
40822         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40823             return true;
40824         }else{
40825             this.lastSize = {width: w, height: h};
40826             return false;
40827         }
40828     },
40829     /**
40830      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40831      * @return {Roo.UpdateManager} The UpdateManager
40832      */
40833     getUpdateManager : function(){
40834         if (this.iframe) {
40835             return false;
40836         }
40837         return this.el.getUpdateManager();
40838     },
40839      /**
40840      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40841      * Does not work with IFRAME contents
40842      * @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:
40843 <pre><code>
40844 panel.load({
40845     url: "your-url.php",
40846     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40847     callback: yourFunction,
40848     scope: yourObject, //(optional scope)
40849     discardUrl: false,
40850     nocache: false,
40851     text: "Loading...",
40852     timeout: 30,
40853     scripts: false
40854 });
40855 </code></pre>
40856      
40857      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40858      * 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.
40859      * @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}
40860      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40861      * @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.
40862      * @return {Roo.ContentPanel} this
40863      */
40864     load : function(){
40865         
40866         if (this.iframe) {
40867             return this;
40868         }
40869         
40870         var um = this.el.getUpdateManager();
40871         um.update.apply(um, arguments);
40872         return this;
40873     },
40874
40875
40876     /**
40877      * 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.
40878      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40879      * @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)
40880      * @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)
40881      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40882      */
40883     setUrl : function(url, params, loadOnce){
40884         if (this.iframe) {
40885             this.iframeEl.dom.src = url;
40886             return false;
40887         }
40888         
40889         if(this.refreshDelegate){
40890             this.removeListener("activate", this.refreshDelegate);
40891         }
40892         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40893         this.on("activate", this.refreshDelegate);
40894         return this.el.getUpdateManager();
40895     },
40896     
40897     _handleRefresh : function(url, params, loadOnce){
40898         if(!loadOnce || !this.loaded){
40899             var updater = this.el.getUpdateManager();
40900             updater.update(url, params, this._setLoaded.createDelegate(this));
40901         }
40902     },
40903     
40904     _setLoaded : function(){
40905         this.loaded = true;
40906     }, 
40907     
40908     /**
40909      * Returns this panel's id
40910      * @return {String} 
40911      */
40912     getId : function(){
40913         return this.el.id;
40914     },
40915     
40916     /** 
40917      * Returns this panel's element - used by regiosn to add.
40918      * @return {Roo.Element} 
40919      */
40920     getEl : function(){
40921         return this.wrapEl || this.el;
40922     },
40923     
40924    
40925     
40926     adjustForComponents : function(width, height)
40927     {
40928         //Roo.log('adjustForComponents ');
40929         if(this.resizeEl != this.el){
40930             width -= this.el.getFrameWidth('lr');
40931             height -= this.el.getFrameWidth('tb');
40932         }
40933         if(this.toolbar){
40934             var te = this.toolbar.getEl();
40935             te.setWidth(width);
40936             height -= te.getHeight();
40937         }
40938         if(this.footer){
40939             var te = this.footer.getEl();
40940             te.setWidth(width);
40941             height -= te.getHeight();
40942         }
40943         
40944         
40945         if(this.adjustments){
40946             width += this.adjustments[0];
40947             height += this.adjustments[1];
40948         }
40949         return {"width": width, "height": height};
40950     },
40951     
40952     setSize : function(width, height){
40953         if(this.fitToFrame && !this.ignoreResize(width, height)){
40954             if(this.fitContainer && this.resizeEl != this.el){
40955                 this.el.setSize(width, height);
40956             }
40957             var size = this.adjustForComponents(width, height);
40958             if (this.iframe) {
40959                 this.iframeEl.setSize(width,height);
40960             }
40961             
40962             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40963             this.fireEvent('resize', this, size.width, size.height);
40964             
40965             
40966         }
40967     },
40968     
40969     /**
40970      * Returns this panel's title
40971      * @return {String} 
40972      */
40973     getTitle : function(){
40974         
40975         if (typeof(this.title) != 'object') {
40976             return this.title;
40977         }
40978         
40979         var t = '';
40980         for (var k in this.title) {
40981             if (!this.title.hasOwnProperty(k)) {
40982                 continue;
40983             }
40984             
40985             if (k.indexOf('-') >= 0) {
40986                 var s = k.split('-');
40987                 for (var i = 0; i<s.length; i++) {
40988                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40989                 }
40990             } else {
40991                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40992             }
40993         }
40994         return t;
40995     },
40996     
40997     /**
40998      * Set this panel's title
40999      * @param {String} title
41000      */
41001     setTitle : function(title){
41002         this.title = title;
41003         if(this.region){
41004             this.region.updatePanelTitle(this, title);
41005         }
41006     },
41007     
41008     /**
41009      * Returns true is this panel was configured to be closable
41010      * @return {Boolean} 
41011      */
41012     isClosable : function(){
41013         return this.closable;
41014     },
41015     
41016     beforeSlide : function(){
41017         this.el.clip();
41018         this.resizeEl.clip();
41019     },
41020     
41021     afterSlide : function(){
41022         this.el.unclip();
41023         this.resizeEl.unclip();
41024     },
41025     
41026     /**
41027      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41028      *   Will fail silently if the {@link #setUrl} method has not been called.
41029      *   This does not activate the panel, just updates its content.
41030      */
41031     refresh : function(){
41032         if(this.refreshDelegate){
41033            this.loaded = false;
41034            this.refreshDelegate();
41035         }
41036     },
41037     
41038     /**
41039      * Destroys this panel
41040      */
41041     destroy : function(){
41042         this.el.removeAllListeners();
41043         var tempEl = document.createElement("span");
41044         tempEl.appendChild(this.el.dom);
41045         tempEl.innerHTML = "";
41046         this.el.remove();
41047         this.el = null;
41048     },
41049     
41050     /**
41051      * form - if the content panel contains a form - this is a reference to it.
41052      * @type {Roo.form.Form}
41053      */
41054     form : false,
41055     /**
41056      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41057      *    This contains a reference to it.
41058      * @type {Roo.View}
41059      */
41060     view : false,
41061     
41062       /**
41063      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41064      * <pre><code>
41065
41066 layout.addxtype({
41067        xtype : 'Form',
41068        items: [ .... ]
41069    }
41070 );
41071
41072 </code></pre>
41073      * @param {Object} cfg Xtype definition of item to add.
41074      */
41075     
41076     
41077     getChildContainer: function () {
41078         return this.getEl();
41079     },
41080     
41081     
41082     onScroll : function(e)
41083     {
41084         this.fireEvent('scroll', this, e);
41085     }
41086     
41087     
41088     /*
41089         var  ret = new Roo.factory(cfg);
41090         return ret;
41091         
41092         
41093         // add form..
41094         if (cfg.xtype.match(/^Form$/)) {
41095             
41096             var el;
41097             //if (this.footer) {
41098             //    el = this.footer.container.insertSibling(false, 'before');
41099             //} else {
41100                 el = this.el.createChild();
41101             //}
41102
41103             this.form = new  Roo.form.Form(cfg);
41104             
41105             
41106             if ( this.form.allItems.length) {
41107                 this.form.render(el.dom);
41108             }
41109             return this.form;
41110         }
41111         // should only have one of theses..
41112         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41113             // views.. should not be just added - used named prop 'view''
41114             
41115             cfg.el = this.el.appendChild(document.createElement("div"));
41116             // factory?
41117             
41118             var ret = new Roo.factory(cfg);
41119              
41120              ret.render && ret.render(false, ''); // render blank..
41121             this.view = ret;
41122             return ret;
41123         }
41124         return false;
41125     }
41126     \*/
41127 });
41128  
41129 /**
41130  * @class Roo.bootstrap.panel.Grid
41131  * @extends Roo.bootstrap.panel.Content
41132  * @constructor
41133  * Create a new GridPanel.
41134  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41135  * @param {Object} config A the config object
41136   
41137  */
41138
41139
41140
41141 Roo.bootstrap.panel.Grid = function(config)
41142 {
41143     
41144       
41145     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41146         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41147
41148     config.el = this.wrapper;
41149     //this.el = this.wrapper;
41150     
41151       if (config.container) {
41152         // ctor'ed from a Border/panel.grid
41153         
41154         
41155         this.wrapper.setStyle("overflow", "hidden");
41156         this.wrapper.addClass('roo-grid-container');
41157
41158     }
41159     
41160     
41161     if(config.toolbar){
41162         var tool_el = this.wrapper.createChild();    
41163         this.toolbar = Roo.factory(config.toolbar);
41164         var ti = [];
41165         if (config.toolbar.items) {
41166             ti = config.toolbar.items ;
41167             delete config.toolbar.items ;
41168         }
41169         
41170         var nitems = [];
41171         this.toolbar.render(tool_el);
41172         for(var i =0;i < ti.length;i++) {
41173           //  Roo.log(['add child', items[i]]);
41174             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41175         }
41176         this.toolbar.items = nitems;
41177         
41178         delete config.toolbar;
41179     }
41180     
41181     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41182     config.grid.scrollBody = true;;
41183     config.grid.monitorWindowResize = false; // turn off autosizing
41184     config.grid.autoHeight = false;
41185     config.grid.autoWidth = false;
41186     
41187     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41188     
41189     if (config.background) {
41190         // render grid on panel activation (if panel background)
41191         this.on('activate', function(gp) {
41192             if (!gp.grid.rendered) {
41193                 gp.grid.render(this.wrapper);
41194                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41195             }
41196         });
41197             
41198     } else {
41199         this.grid.render(this.wrapper);
41200         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41201
41202     }
41203     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41204     // ??? needed ??? config.el = this.wrapper;
41205     
41206     
41207     
41208   
41209     // xtype created footer. - not sure if will work as we normally have to render first..
41210     if (this.footer && !this.footer.el && this.footer.xtype) {
41211         
41212         var ctr = this.grid.getView().getFooterPanel(true);
41213         this.footer.dataSource = this.grid.dataSource;
41214         this.footer = Roo.factory(this.footer, Roo);
41215         this.footer.render(ctr);
41216         
41217     }
41218     
41219     
41220     
41221     
41222      
41223 };
41224
41225 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41226     getId : function(){
41227         return this.grid.id;
41228     },
41229     
41230     /**
41231      * Returns the grid for this panel
41232      * @return {Roo.bootstrap.Table} 
41233      */
41234     getGrid : function(){
41235         return this.grid;    
41236     },
41237     
41238     setSize : function(width, height){
41239         if(!this.ignoreResize(width, height)){
41240             var grid = this.grid;
41241             var size = this.adjustForComponents(width, height);
41242             // tfoot is not a footer?
41243           
41244             
41245             var gridel = grid.getGridEl();
41246             gridel.setSize(size.width, size.height);
41247             
41248             var tbd = grid.getGridEl().select('tbody', true).first();
41249             var thd = grid.getGridEl().select('thead',true).first();
41250             var tbf= grid.getGridEl().select('tfoot', true).first();
41251
41252             if (tbf) {
41253                 size.height -= tbf.getHeight();
41254             }
41255             if (thd) {
41256                 size.height -= thd.getHeight();
41257             }
41258             
41259             tbd.setSize(size.width, size.height );
41260             // this is for the account management tab -seems to work there.
41261             var thd = grid.getGridEl().select('thead',true).first();
41262             //if (tbd) {
41263             //    tbd.setSize(size.width, size.height - thd.getHeight());
41264             //}
41265              
41266             grid.autoSize();
41267         }
41268     },
41269      
41270     
41271     
41272     beforeSlide : function(){
41273         this.grid.getView().scroller.clip();
41274     },
41275     
41276     afterSlide : function(){
41277         this.grid.getView().scroller.unclip();
41278     },
41279     
41280     destroy : function(){
41281         this.grid.destroy();
41282         delete this.grid;
41283         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41284     }
41285 });
41286
41287 /**
41288  * @class Roo.bootstrap.panel.Nest
41289  * @extends Roo.bootstrap.panel.Content
41290  * @constructor
41291  * Create a new Panel, that can contain a layout.Border.
41292  * 
41293  * 
41294  * @param {Roo.BorderLayout} layout The layout for this panel
41295  * @param {String/Object} config A string to set only the title or a config object
41296  */
41297 Roo.bootstrap.panel.Nest = function(config)
41298 {
41299     // construct with only one argument..
41300     /* FIXME - implement nicer consturctors
41301     if (layout.layout) {
41302         config = layout;
41303         layout = config.layout;
41304         delete config.layout;
41305     }
41306     if (layout.xtype && !layout.getEl) {
41307         // then layout needs constructing..
41308         layout = Roo.factory(layout, Roo);
41309     }
41310     */
41311     
41312     config.el =  config.layout.getEl();
41313     
41314     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41315     
41316     config.layout.monitorWindowResize = false; // turn off autosizing
41317     this.layout = config.layout;
41318     this.layout.getEl().addClass("roo-layout-nested-layout");
41319     this.layout.parent = this;
41320     
41321     
41322     
41323     
41324 };
41325
41326 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41327
41328     setSize : function(width, height){
41329         if(!this.ignoreResize(width, height)){
41330             var size = this.adjustForComponents(width, height);
41331             var el = this.layout.getEl();
41332             if (size.height < 1) {
41333                 el.setWidth(size.width);   
41334             } else {
41335                 el.setSize(size.width, size.height);
41336             }
41337             var touch = el.dom.offsetWidth;
41338             this.layout.layout();
41339             // ie requires a double layout on the first pass
41340             if(Roo.isIE && !this.initialized){
41341                 this.initialized = true;
41342                 this.layout.layout();
41343             }
41344         }
41345     },
41346     
41347     // activate all subpanels if not currently active..
41348     
41349     setActiveState : function(active){
41350         this.active = active;
41351         this.setActiveClass(active);
41352         
41353         if(!active){
41354             this.fireEvent("deactivate", this);
41355             return;
41356         }
41357         
41358         this.fireEvent("activate", this);
41359         // not sure if this should happen before or after..
41360         if (!this.layout) {
41361             return; // should not happen..
41362         }
41363         var reg = false;
41364         for (var r in this.layout.regions) {
41365             reg = this.layout.getRegion(r);
41366             if (reg.getActivePanel()) {
41367                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41368                 reg.setActivePanel(reg.getActivePanel());
41369                 continue;
41370             }
41371             if (!reg.panels.length) {
41372                 continue;
41373             }
41374             reg.showPanel(reg.getPanel(0));
41375         }
41376         
41377         
41378         
41379         
41380     },
41381     
41382     /**
41383      * Returns the nested BorderLayout for this panel
41384      * @return {Roo.BorderLayout} 
41385      */
41386     getLayout : function(){
41387         return this.layout;
41388     },
41389     
41390      /**
41391      * Adds a xtype elements to the layout of the nested panel
41392      * <pre><code>
41393
41394 panel.addxtype({
41395        xtype : 'ContentPanel',
41396        region: 'west',
41397        items: [ .... ]
41398    }
41399 );
41400
41401 panel.addxtype({
41402         xtype : 'NestedLayoutPanel',
41403         region: 'west',
41404         layout: {
41405            center: { },
41406            west: { }   
41407         },
41408         items : [ ... list of content panels or nested layout panels.. ]
41409    }
41410 );
41411 </code></pre>
41412      * @param {Object} cfg Xtype definition of item to add.
41413      */
41414     addxtype : function(cfg) {
41415         return this.layout.addxtype(cfg);
41416     
41417     }
41418 });/*
41419  * Based on:
41420  * Ext JS Library 1.1.1
41421  * Copyright(c) 2006-2007, Ext JS, LLC.
41422  *
41423  * Originally Released Under LGPL - original licence link has changed is not relivant.
41424  *
41425  * Fork - LGPL
41426  * <script type="text/javascript">
41427  */
41428 /**
41429  * @class Roo.TabPanel
41430  * @extends Roo.util.Observable
41431  * A lightweight tab container.
41432  * <br><br>
41433  * Usage:
41434  * <pre><code>
41435 // basic tabs 1, built from existing content
41436 var tabs = new Roo.TabPanel("tabs1");
41437 tabs.addTab("script", "View Script");
41438 tabs.addTab("markup", "View Markup");
41439 tabs.activate("script");
41440
41441 // more advanced tabs, built from javascript
41442 var jtabs = new Roo.TabPanel("jtabs");
41443 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41444
41445 // set up the UpdateManager
41446 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41447 var updater = tab2.getUpdateManager();
41448 updater.setDefaultUrl("ajax1.htm");
41449 tab2.on('activate', updater.refresh, updater, true);
41450
41451 // Use setUrl for Ajax loading
41452 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41453 tab3.setUrl("ajax2.htm", null, true);
41454
41455 // Disabled tab
41456 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41457 tab4.disable();
41458
41459 jtabs.activate("jtabs-1");
41460  * </code></pre>
41461  * @constructor
41462  * Create a new TabPanel.
41463  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41464  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41465  */
41466 Roo.bootstrap.panel.Tabs = function(config){
41467     /**
41468     * The container element for this TabPanel.
41469     * @type Roo.Element
41470     */
41471     this.el = Roo.get(config.el);
41472     delete config.el;
41473     if(config){
41474         if(typeof config == "boolean"){
41475             this.tabPosition = config ? "bottom" : "top";
41476         }else{
41477             Roo.apply(this, config);
41478         }
41479     }
41480     
41481     if(this.tabPosition == "bottom"){
41482         // if tabs are at the bottom = create the body first.
41483         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41484         this.el.addClass("roo-tabs-bottom");
41485     }
41486     // next create the tabs holders
41487     
41488     if (this.tabPosition == "west"){
41489         
41490         var reg = this.region; // fake it..
41491         while (reg) {
41492             if (!reg.mgr.parent) {
41493                 break;
41494             }
41495             reg = reg.mgr.parent.region;
41496         }
41497         Roo.log("got nest?");
41498         Roo.log(reg);
41499         if (reg.mgr.getRegion('west')) {
41500             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41501             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41502             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41503             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41504             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41505         
41506             
41507         }
41508         
41509         
41510     } else {
41511      
41512         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41513         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41514         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41515         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41516     }
41517     
41518     
41519     if(Roo.isIE){
41520         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41521     }
41522     
41523     // finally - if tabs are at the top, then create the body last..
41524     if(this.tabPosition != "bottom"){
41525         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41526          * @type Roo.Element
41527          */
41528         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41529         this.el.addClass("roo-tabs-top");
41530     }
41531     this.items = [];
41532
41533     this.bodyEl.setStyle("position", "relative");
41534
41535     this.active = null;
41536     this.activateDelegate = this.activate.createDelegate(this);
41537
41538     this.addEvents({
41539         /**
41540          * @event tabchange
41541          * Fires when the active tab changes
41542          * @param {Roo.TabPanel} this
41543          * @param {Roo.TabPanelItem} activePanel The new active tab
41544          */
41545         "tabchange": true,
41546         /**
41547          * @event beforetabchange
41548          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41549          * @param {Roo.TabPanel} this
41550          * @param {Object} e Set cancel to true on this object to cancel the tab change
41551          * @param {Roo.TabPanelItem} tab The tab being changed to
41552          */
41553         "beforetabchange" : true
41554     });
41555
41556     Roo.EventManager.onWindowResize(this.onResize, this);
41557     this.cpad = this.el.getPadding("lr");
41558     this.hiddenCount = 0;
41559
41560
41561     // toolbar on the tabbar support...
41562     if (this.toolbar) {
41563         alert("no toolbar support yet");
41564         this.toolbar  = false;
41565         /*
41566         var tcfg = this.toolbar;
41567         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41568         this.toolbar = new Roo.Toolbar(tcfg);
41569         if (Roo.isSafari) {
41570             var tbl = tcfg.container.child('table', true);
41571             tbl.setAttribute('width', '100%');
41572         }
41573         */
41574         
41575     }
41576    
41577
41578
41579     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41580 };
41581
41582 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41583     /*
41584      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41585      */
41586     tabPosition : "top",
41587     /*
41588      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41589      */
41590     currentTabWidth : 0,
41591     /*
41592      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41593      */
41594     minTabWidth : 40,
41595     /*
41596      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41597      */
41598     maxTabWidth : 250,
41599     /*
41600      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41601      */
41602     preferredTabWidth : 175,
41603     /*
41604      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41605      */
41606     resizeTabs : false,
41607     /*
41608      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41609      */
41610     monitorResize : true,
41611     /*
41612      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41613      */
41614     toolbar : false,  // set by caller..
41615     
41616     region : false, /// set by caller
41617     
41618     disableTooltips : true, // not used yet...
41619
41620     /**
41621      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41622      * @param {String} id The id of the div to use <b>or create</b>
41623      * @param {String} text The text for the tab
41624      * @param {String} content (optional) Content to put in the TabPanelItem body
41625      * @param {Boolean} closable (optional) True to create a close icon on the tab
41626      * @return {Roo.TabPanelItem} The created TabPanelItem
41627      */
41628     addTab : function(id, text, content, closable, tpl)
41629     {
41630         var item = new Roo.bootstrap.panel.TabItem({
41631             panel: this,
41632             id : id,
41633             text : text,
41634             closable : closable,
41635             tpl : tpl
41636         });
41637         this.addTabItem(item);
41638         if(content){
41639             item.setContent(content);
41640         }
41641         return item;
41642     },
41643
41644     /**
41645      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41646      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41647      * @return {Roo.TabPanelItem}
41648      */
41649     getTab : function(id){
41650         return this.items[id];
41651     },
41652
41653     /**
41654      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41655      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41656      */
41657     hideTab : function(id){
41658         var t = this.items[id];
41659         if(!t.isHidden()){
41660            t.setHidden(true);
41661            this.hiddenCount++;
41662            this.autoSizeTabs();
41663         }
41664     },
41665
41666     /**
41667      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41668      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41669      */
41670     unhideTab : function(id){
41671         var t = this.items[id];
41672         if(t.isHidden()){
41673            t.setHidden(false);
41674            this.hiddenCount--;
41675            this.autoSizeTabs();
41676         }
41677     },
41678
41679     /**
41680      * Adds an existing {@link Roo.TabPanelItem}.
41681      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41682      */
41683     addTabItem : function(item)
41684     {
41685         this.items[item.id] = item;
41686         this.items.push(item);
41687         this.autoSizeTabs();
41688       //  if(this.resizeTabs){
41689     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41690   //         this.autoSizeTabs();
41691 //        }else{
41692 //            item.autoSize();
41693        // }
41694     },
41695
41696     /**
41697      * Removes a {@link Roo.TabPanelItem}.
41698      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41699      */
41700     removeTab : function(id){
41701         var items = this.items;
41702         var tab = items[id];
41703         if(!tab) { return; }
41704         var index = items.indexOf(tab);
41705         if(this.active == tab && items.length > 1){
41706             var newTab = this.getNextAvailable(index);
41707             if(newTab) {
41708                 newTab.activate();
41709             }
41710         }
41711         this.stripEl.dom.removeChild(tab.pnode.dom);
41712         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41713             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41714         }
41715         items.splice(index, 1);
41716         delete this.items[tab.id];
41717         tab.fireEvent("close", tab);
41718         tab.purgeListeners();
41719         this.autoSizeTabs();
41720     },
41721
41722     getNextAvailable : function(start){
41723         var items = this.items;
41724         var index = start;
41725         // look for a next tab that will slide over to
41726         // replace the one being removed
41727         while(index < items.length){
41728             var item = items[++index];
41729             if(item && !item.isHidden()){
41730                 return item;
41731             }
41732         }
41733         // if one isn't found select the previous tab (on the left)
41734         index = start;
41735         while(index >= 0){
41736             var item = items[--index];
41737             if(item && !item.isHidden()){
41738                 return item;
41739             }
41740         }
41741         return null;
41742     },
41743
41744     /**
41745      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41746      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41747      */
41748     disableTab : function(id){
41749         var tab = this.items[id];
41750         if(tab && this.active != tab){
41751             tab.disable();
41752         }
41753     },
41754
41755     /**
41756      * Enables a {@link Roo.TabPanelItem} that is disabled.
41757      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41758      */
41759     enableTab : function(id){
41760         var tab = this.items[id];
41761         tab.enable();
41762     },
41763
41764     /**
41765      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41766      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41767      * @return {Roo.TabPanelItem} The TabPanelItem.
41768      */
41769     activate : function(id)
41770     {
41771         //Roo.log('activite:'  + id);
41772         
41773         var tab = this.items[id];
41774         if(!tab){
41775             return null;
41776         }
41777         if(tab == this.active || tab.disabled){
41778             return tab;
41779         }
41780         var e = {};
41781         this.fireEvent("beforetabchange", this, e, tab);
41782         if(e.cancel !== true && !tab.disabled){
41783             if(this.active){
41784                 this.active.hide();
41785             }
41786             this.active = this.items[id];
41787             this.active.show();
41788             this.fireEvent("tabchange", this, this.active);
41789         }
41790         return tab;
41791     },
41792
41793     /**
41794      * Gets the active {@link Roo.TabPanelItem}.
41795      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41796      */
41797     getActiveTab : function(){
41798         return this.active;
41799     },
41800
41801     /**
41802      * Updates the tab body element to fit the height of the container element
41803      * for overflow scrolling
41804      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41805      */
41806     syncHeight : function(targetHeight){
41807         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41808         var bm = this.bodyEl.getMargins();
41809         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41810         this.bodyEl.setHeight(newHeight);
41811         return newHeight;
41812     },
41813
41814     onResize : function(){
41815         if(this.monitorResize){
41816             this.autoSizeTabs();
41817         }
41818     },
41819
41820     /**
41821      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41822      */
41823     beginUpdate : function(){
41824         this.updating = true;
41825     },
41826
41827     /**
41828      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41829      */
41830     endUpdate : function(){
41831         this.updating = false;
41832         this.autoSizeTabs();
41833     },
41834
41835     /**
41836      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41837      */
41838     autoSizeTabs : function()
41839     {
41840         var count = this.items.length;
41841         var vcount = count - this.hiddenCount;
41842         
41843         if (vcount < 2) {
41844             this.stripEl.hide();
41845         } else {
41846             this.stripEl.show();
41847         }
41848         
41849         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41850             return;
41851         }
41852         
41853         
41854         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41855         var availWidth = Math.floor(w / vcount);
41856         var b = this.stripBody;
41857         if(b.getWidth() > w){
41858             var tabs = this.items;
41859             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41860             if(availWidth < this.minTabWidth){
41861                 /*if(!this.sleft){    // incomplete scrolling code
41862                     this.createScrollButtons();
41863                 }
41864                 this.showScroll();
41865                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41866             }
41867         }else{
41868             if(this.currentTabWidth < this.preferredTabWidth){
41869                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41870             }
41871         }
41872     },
41873
41874     /**
41875      * Returns the number of tabs in this TabPanel.
41876      * @return {Number}
41877      */
41878      getCount : function(){
41879          return this.items.length;
41880      },
41881
41882     /**
41883      * Resizes all the tabs to the passed width
41884      * @param {Number} The new width
41885      */
41886     setTabWidth : function(width){
41887         this.currentTabWidth = width;
41888         for(var i = 0, len = this.items.length; i < len; i++) {
41889                 if(!this.items[i].isHidden()) {
41890                 this.items[i].setWidth(width);
41891             }
41892         }
41893     },
41894
41895     /**
41896      * Destroys this TabPanel
41897      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41898      */
41899     destroy : function(removeEl){
41900         Roo.EventManager.removeResizeListener(this.onResize, this);
41901         for(var i = 0, len = this.items.length; i < len; i++){
41902             this.items[i].purgeListeners();
41903         }
41904         if(removeEl === true){
41905             this.el.update("");
41906             this.el.remove();
41907         }
41908     },
41909     
41910     createStrip : function(container)
41911     {
41912         var strip = document.createElement("nav");
41913         strip.className = Roo.bootstrap.version == 4 ?
41914             "navbar-light bg-light" : 
41915             "navbar navbar-default"; //"x-tabs-wrap";
41916         container.appendChild(strip);
41917         return strip;
41918     },
41919     
41920     createStripList : function(strip)
41921     {
41922         // div wrapper for retard IE
41923         // returns the "tr" element.
41924         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41925         //'<div class="x-tabs-strip-wrap">'+
41926           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41927           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41928         return strip.firstChild; //.firstChild.firstChild.firstChild;
41929     },
41930     createBody : function(container)
41931     {
41932         var body = document.createElement("div");
41933         Roo.id(body, "tab-body");
41934         //Roo.fly(body).addClass("x-tabs-body");
41935         Roo.fly(body).addClass("tab-content");
41936         container.appendChild(body);
41937         return body;
41938     },
41939     createItemBody :function(bodyEl, id){
41940         var body = Roo.getDom(id);
41941         if(!body){
41942             body = document.createElement("div");
41943             body.id = id;
41944         }
41945         //Roo.fly(body).addClass("x-tabs-item-body");
41946         Roo.fly(body).addClass("tab-pane");
41947          bodyEl.insertBefore(body, bodyEl.firstChild);
41948         return body;
41949     },
41950     /** @private */
41951     createStripElements :  function(stripEl, text, closable, tpl)
41952     {
41953         var td = document.createElement("li"); // was td..
41954         td.className = 'nav-item';
41955         
41956         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41957         
41958         
41959         stripEl.appendChild(td);
41960         /*if(closable){
41961             td.className = "x-tabs-closable";
41962             if(!this.closeTpl){
41963                 this.closeTpl = new Roo.Template(
41964                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41965                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41966                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41967                 );
41968             }
41969             var el = this.closeTpl.overwrite(td, {"text": text});
41970             var close = el.getElementsByTagName("div")[0];
41971             var inner = el.getElementsByTagName("em")[0];
41972             return {"el": el, "close": close, "inner": inner};
41973         } else {
41974         */
41975         // not sure what this is..
41976 //            if(!this.tabTpl){
41977                 //this.tabTpl = new Roo.Template(
41978                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41979                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41980                 //);
41981 //                this.tabTpl = new Roo.Template(
41982 //                   '<a href="#">' +
41983 //                   '<span unselectable="on"' +
41984 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41985 //                            ' >{text}</span></a>'
41986 //                );
41987 //                
41988 //            }
41989
41990
41991             var template = tpl || this.tabTpl || false;
41992             
41993             if(!template){
41994                 template =  new Roo.Template(
41995                         Roo.bootstrap.version == 4 ? 
41996                             (
41997                                 '<a class="nav-link" href="#" unselectable="on"' +
41998                                      (this.disableTooltips ? '' : ' title="{text}"') +
41999                                      ' >{text}</a>'
42000                             ) : (
42001                                 '<a class="nav-link" href="#">' +
42002                                 '<span unselectable="on"' +
42003                                          (this.disableTooltips ? '' : ' title="{text}"') +
42004                                     ' >{text}</span></a>'
42005                             )
42006                 );
42007             }
42008             
42009             switch (typeof(template)) {
42010                 case 'object' :
42011                     break;
42012                 case 'string' :
42013                     template = new Roo.Template(template);
42014                     break;
42015                 default :
42016                     break;
42017             }
42018             
42019             var el = template.overwrite(td, {"text": text});
42020             
42021             var inner = el.getElementsByTagName("span")[0];
42022             
42023             return {"el": el, "inner": inner};
42024             
42025     }
42026         
42027     
42028 });
42029
42030 /**
42031  * @class Roo.TabPanelItem
42032  * @extends Roo.util.Observable
42033  * Represents an individual item (tab plus body) in a TabPanel.
42034  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42035  * @param {String} id The id of this TabPanelItem
42036  * @param {String} text The text for the tab of this TabPanelItem
42037  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42038  */
42039 Roo.bootstrap.panel.TabItem = function(config){
42040     /**
42041      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42042      * @type Roo.TabPanel
42043      */
42044     this.tabPanel = config.panel;
42045     /**
42046      * The id for this TabPanelItem
42047      * @type String
42048      */
42049     this.id = config.id;
42050     /** @private */
42051     this.disabled = false;
42052     /** @private */
42053     this.text = config.text;
42054     /** @private */
42055     this.loaded = false;
42056     this.closable = config.closable;
42057
42058     /**
42059      * The body element for this TabPanelItem.
42060      * @type Roo.Element
42061      */
42062     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42063     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42064     this.bodyEl.setStyle("display", "block");
42065     this.bodyEl.setStyle("zoom", "1");
42066     //this.hideAction();
42067
42068     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42069     /** @private */
42070     this.el = Roo.get(els.el);
42071     this.inner = Roo.get(els.inner, true);
42072      this.textEl = Roo.bootstrap.version == 4 ?
42073         this.el : Roo.get(this.el.dom.firstChild, true);
42074
42075     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42076     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42077
42078     
42079 //    this.el.on("mousedown", this.onTabMouseDown, this);
42080     this.el.on("click", this.onTabClick, this);
42081     /** @private */
42082     if(config.closable){
42083         var c = Roo.get(els.close, true);
42084         c.dom.title = this.closeText;
42085         c.addClassOnOver("close-over");
42086         c.on("click", this.closeClick, this);
42087      }
42088
42089     this.addEvents({
42090          /**
42091          * @event activate
42092          * Fires when this tab becomes the active tab.
42093          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42094          * @param {Roo.TabPanelItem} this
42095          */
42096         "activate": true,
42097         /**
42098          * @event beforeclose
42099          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42100          * @param {Roo.TabPanelItem} this
42101          * @param {Object} e Set cancel to true on this object to cancel the close.
42102          */
42103         "beforeclose": true,
42104         /**
42105          * @event close
42106          * Fires when this tab is closed.
42107          * @param {Roo.TabPanelItem} this
42108          */
42109          "close": true,
42110         /**
42111          * @event deactivate
42112          * Fires when this tab is no longer the active tab.
42113          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42114          * @param {Roo.TabPanelItem} this
42115          */
42116          "deactivate" : true
42117     });
42118     this.hidden = false;
42119
42120     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42121 };
42122
42123 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42124            {
42125     purgeListeners : function(){
42126        Roo.util.Observable.prototype.purgeListeners.call(this);
42127        this.el.removeAllListeners();
42128     },
42129     /**
42130      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42131      */
42132     show : function(){
42133         this.status_node.addClass("active");
42134         this.showAction();
42135         if(Roo.isOpera){
42136             this.tabPanel.stripWrap.repaint();
42137         }
42138         this.fireEvent("activate", this.tabPanel, this);
42139     },
42140
42141     /**
42142      * Returns true if this tab is the active tab.
42143      * @return {Boolean}
42144      */
42145     isActive : function(){
42146         return this.tabPanel.getActiveTab() == this;
42147     },
42148
42149     /**
42150      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42151      */
42152     hide : function(){
42153         this.status_node.removeClass("active");
42154         this.hideAction();
42155         this.fireEvent("deactivate", this.tabPanel, this);
42156     },
42157
42158     hideAction : function(){
42159         this.bodyEl.hide();
42160         this.bodyEl.setStyle("position", "absolute");
42161         this.bodyEl.setLeft("-20000px");
42162         this.bodyEl.setTop("-20000px");
42163     },
42164
42165     showAction : function(){
42166         this.bodyEl.setStyle("position", "relative");
42167         this.bodyEl.setTop("");
42168         this.bodyEl.setLeft("");
42169         this.bodyEl.show();
42170     },
42171
42172     /**
42173      * Set the tooltip for the tab.
42174      * @param {String} tooltip The tab's tooltip
42175      */
42176     setTooltip : function(text){
42177         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42178             this.textEl.dom.qtip = text;
42179             this.textEl.dom.removeAttribute('title');
42180         }else{
42181             this.textEl.dom.title = text;
42182         }
42183     },
42184
42185     onTabClick : function(e){
42186         e.preventDefault();
42187         this.tabPanel.activate(this.id);
42188     },
42189
42190     onTabMouseDown : function(e){
42191         e.preventDefault();
42192         this.tabPanel.activate(this.id);
42193     },
42194 /*
42195     getWidth : function(){
42196         return this.inner.getWidth();
42197     },
42198
42199     setWidth : function(width){
42200         var iwidth = width - this.linode.getPadding("lr");
42201         this.inner.setWidth(iwidth);
42202         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42203         this.linode.setWidth(width);
42204     },
42205 */
42206     /**
42207      * Show or hide the tab
42208      * @param {Boolean} hidden True to hide or false to show.
42209      */
42210     setHidden : function(hidden){
42211         this.hidden = hidden;
42212         this.linode.setStyle("display", hidden ? "none" : "");
42213     },
42214
42215     /**
42216      * Returns true if this tab is "hidden"
42217      * @return {Boolean}
42218      */
42219     isHidden : function(){
42220         return this.hidden;
42221     },
42222
42223     /**
42224      * Returns the text for this tab
42225      * @return {String}
42226      */
42227     getText : function(){
42228         return this.text;
42229     },
42230     /*
42231     autoSize : function(){
42232         //this.el.beginMeasure();
42233         this.textEl.setWidth(1);
42234         /*
42235          *  #2804 [new] Tabs in Roojs
42236          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42237          */
42238         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42239         //this.el.endMeasure();
42240     //},
42241
42242     /**
42243      * Sets the text for the tab (Note: this also sets the tooltip text)
42244      * @param {String} text The tab's text and tooltip
42245      */
42246     setText : function(text){
42247         this.text = text;
42248         this.textEl.update(text);
42249         this.setTooltip(text);
42250         //if(!this.tabPanel.resizeTabs){
42251         //    this.autoSize();
42252         //}
42253     },
42254     /**
42255      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42256      */
42257     activate : function(){
42258         this.tabPanel.activate(this.id);
42259     },
42260
42261     /**
42262      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42263      */
42264     disable : function(){
42265         if(this.tabPanel.active != this){
42266             this.disabled = true;
42267             this.status_node.addClass("disabled");
42268         }
42269     },
42270
42271     /**
42272      * Enables this TabPanelItem if it was previously disabled.
42273      */
42274     enable : function(){
42275         this.disabled = false;
42276         this.status_node.removeClass("disabled");
42277     },
42278
42279     /**
42280      * Sets the content for this TabPanelItem.
42281      * @param {String} content The content
42282      * @param {Boolean} loadScripts true to look for and load scripts
42283      */
42284     setContent : function(content, loadScripts){
42285         this.bodyEl.update(content, loadScripts);
42286     },
42287
42288     /**
42289      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42290      * @return {Roo.UpdateManager} The UpdateManager
42291      */
42292     getUpdateManager : function(){
42293         return this.bodyEl.getUpdateManager();
42294     },
42295
42296     /**
42297      * Set a URL to be used to load the content for this TabPanelItem.
42298      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42299      * @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)
42300      * @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)
42301      * @return {Roo.UpdateManager} The UpdateManager
42302      */
42303     setUrl : function(url, params, loadOnce){
42304         if(this.refreshDelegate){
42305             this.un('activate', this.refreshDelegate);
42306         }
42307         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42308         this.on("activate", this.refreshDelegate);
42309         return this.bodyEl.getUpdateManager();
42310     },
42311
42312     /** @private */
42313     _handleRefresh : function(url, params, loadOnce){
42314         if(!loadOnce || !this.loaded){
42315             var updater = this.bodyEl.getUpdateManager();
42316             updater.update(url, params, this._setLoaded.createDelegate(this));
42317         }
42318     },
42319
42320     /**
42321      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42322      *   Will fail silently if the setUrl method has not been called.
42323      *   This does not activate the panel, just updates its content.
42324      */
42325     refresh : function(){
42326         if(this.refreshDelegate){
42327            this.loaded = false;
42328            this.refreshDelegate();
42329         }
42330     },
42331
42332     /** @private */
42333     _setLoaded : function(){
42334         this.loaded = true;
42335     },
42336
42337     /** @private */
42338     closeClick : function(e){
42339         var o = {};
42340         e.stopEvent();
42341         this.fireEvent("beforeclose", this, o);
42342         if(o.cancel !== true){
42343             this.tabPanel.removeTab(this.id);
42344         }
42345     },
42346     /**
42347      * The text displayed in the tooltip for the close icon.
42348      * @type String
42349      */
42350     closeText : "Close this tab"
42351 });
42352 /**
42353 *    This script refer to:
42354 *    Title: International Telephone Input
42355 *    Author: Jack O'Connor
42356 *    Code version:  v12.1.12
42357 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42358 **/
42359
42360 Roo.bootstrap.PhoneInputData = function() {
42361     var d = [
42362       [
42363         "Afghanistan (‫افغانستان‬‎)",
42364         "af",
42365         "93"
42366       ],
42367       [
42368         "Albania (Shqipëri)",
42369         "al",
42370         "355"
42371       ],
42372       [
42373         "Algeria (‫الجزائر‬‎)",
42374         "dz",
42375         "213"
42376       ],
42377       [
42378         "American Samoa",
42379         "as",
42380         "1684"
42381       ],
42382       [
42383         "Andorra",
42384         "ad",
42385         "376"
42386       ],
42387       [
42388         "Angola",
42389         "ao",
42390         "244"
42391       ],
42392       [
42393         "Anguilla",
42394         "ai",
42395         "1264"
42396       ],
42397       [
42398         "Antigua and Barbuda",
42399         "ag",
42400         "1268"
42401       ],
42402       [
42403         "Argentina",
42404         "ar",
42405         "54"
42406       ],
42407       [
42408         "Armenia (Հայաստան)",
42409         "am",
42410         "374"
42411       ],
42412       [
42413         "Aruba",
42414         "aw",
42415         "297"
42416       ],
42417       [
42418         "Australia",
42419         "au",
42420         "61",
42421         0
42422       ],
42423       [
42424         "Austria (Österreich)",
42425         "at",
42426         "43"
42427       ],
42428       [
42429         "Azerbaijan (Azərbaycan)",
42430         "az",
42431         "994"
42432       ],
42433       [
42434         "Bahamas",
42435         "bs",
42436         "1242"
42437       ],
42438       [
42439         "Bahrain (‫البحرين‬‎)",
42440         "bh",
42441         "973"
42442       ],
42443       [
42444         "Bangladesh (বাংলাদেশ)",
42445         "bd",
42446         "880"
42447       ],
42448       [
42449         "Barbados",
42450         "bb",
42451         "1246"
42452       ],
42453       [
42454         "Belarus (Беларусь)",
42455         "by",
42456         "375"
42457       ],
42458       [
42459         "Belgium (België)",
42460         "be",
42461         "32"
42462       ],
42463       [
42464         "Belize",
42465         "bz",
42466         "501"
42467       ],
42468       [
42469         "Benin (Bénin)",
42470         "bj",
42471         "229"
42472       ],
42473       [
42474         "Bermuda",
42475         "bm",
42476         "1441"
42477       ],
42478       [
42479         "Bhutan (འབྲུག)",
42480         "bt",
42481         "975"
42482       ],
42483       [
42484         "Bolivia",
42485         "bo",
42486         "591"
42487       ],
42488       [
42489         "Bosnia and Herzegovina (Босна и Херцеговина)",
42490         "ba",
42491         "387"
42492       ],
42493       [
42494         "Botswana",
42495         "bw",
42496         "267"
42497       ],
42498       [
42499         "Brazil (Brasil)",
42500         "br",
42501         "55"
42502       ],
42503       [
42504         "British Indian Ocean Territory",
42505         "io",
42506         "246"
42507       ],
42508       [
42509         "British Virgin Islands",
42510         "vg",
42511         "1284"
42512       ],
42513       [
42514         "Brunei",
42515         "bn",
42516         "673"
42517       ],
42518       [
42519         "Bulgaria (България)",
42520         "bg",
42521         "359"
42522       ],
42523       [
42524         "Burkina Faso",
42525         "bf",
42526         "226"
42527       ],
42528       [
42529         "Burundi (Uburundi)",
42530         "bi",
42531         "257"
42532       ],
42533       [
42534         "Cambodia (កម្ពុជា)",
42535         "kh",
42536         "855"
42537       ],
42538       [
42539         "Cameroon (Cameroun)",
42540         "cm",
42541         "237"
42542       ],
42543       [
42544         "Canada",
42545         "ca",
42546         "1",
42547         1,
42548         ["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"]
42549       ],
42550       [
42551         "Cape Verde (Kabu Verdi)",
42552         "cv",
42553         "238"
42554       ],
42555       [
42556         "Caribbean Netherlands",
42557         "bq",
42558         "599",
42559         1
42560       ],
42561       [
42562         "Cayman Islands",
42563         "ky",
42564         "1345"
42565       ],
42566       [
42567         "Central African Republic (République centrafricaine)",
42568         "cf",
42569         "236"
42570       ],
42571       [
42572         "Chad (Tchad)",
42573         "td",
42574         "235"
42575       ],
42576       [
42577         "Chile",
42578         "cl",
42579         "56"
42580       ],
42581       [
42582         "China (中国)",
42583         "cn",
42584         "86"
42585       ],
42586       [
42587         "Christmas Island",
42588         "cx",
42589         "61",
42590         2
42591       ],
42592       [
42593         "Cocos (Keeling) Islands",
42594         "cc",
42595         "61",
42596         1
42597       ],
42598       [
42599         "Colombia",
42600         "co",
42601         "57"
42602       ],
42603       [
42604         "Comoros (‫جزر القمر‬‎)",
42605         "km",
42606         "269"
42607       ],
42608       [
42609         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42610         "cd",
42611         "243"
42612       ],
42613       [
42614         "Congo (Republic) (Congo-Brazzaville)",
42615         "cg",
42616         "242"
42617       ],
42618       [
42619         "Cook Islands",
42620         "ck",
42621         "682"
42622       ],
42623       [
42624         "Costa Rica",
42625         "cr",
42626         "506"
42627       ],
42628       [
42629         "Côte d’Ivoire",
42630         "ci",
42631         "225"
42632       ],
42633       [
42634         "Croatia (Hrvatska)",
42635         "hr",
42636         "385"
42637       ],
42638       [
42639         "Cuba",
42640         "cu",
42641         "53"
42642       ],
42643       [
42644         "Curaçao",
42645         "cw",
42646         "599",
42647         0
42648       ],
42649       [
42650         "Cyprus (Κύπρος)",
42651         "cy",
42652         "357"
42653       ],
42654       [
42655         "Czech Republic (Česká republika)",
42656         "cz",
42657         "420"
42658       ],
42659       [
42660         "Denmark (Danmark)",
42661         "dk",
42662         "45"
42663       ],
42664       [
42665         "Djibouti",
42666         "dj",
42667         "253"
42668       ],
42669       [
42670         "Dominica",
42671         "dm",
42672         "1767"
42673       ],
42674       [
42675         "Dominican Republic (República Dominicana)",
42676         "do",
42677         "1",
42678         2,
42679         ["809", "829", "849"]
42680       ],
42681       [
42682         "Ecuador",
42683         "ec",
42684         "593"
42685       ],
42686       [
42687         "Egypt (‫مصر‬‎)",
42688         "eg",
42689         "20"
42690       ],
42691       [
42692         "El Salvador",
42693         "sv",
42694         "503"
42695       ],
42696       [
42697         "Equatorial Guinea (Guinea Ecuatorial)",
42698         "gq",
42699         "240"
42700       ],
42701       [
42702         "Eritrea",
42703         "er",
42704         "291"
42705       ],
42706       [
42707         "Estonia (Eesti)",
42708         "ee",
42709         "372"
42710       ],
42711       [
42712         "Ethiopia",
42713         "et",
42714         "251"
42715       ],
42716       [
42717         "Falkland Islands (Islas Malvinas)",
42718         "fk",
42719         "500"
42720       ],
42721       [
42722         "Faroe Islands (Føroyar)",
42723         "fo",
42724         "298"
42725       ],
42726       [
42727         "Fiji",
42728         "fj",
42729         "679"
42730       ],
42731       [
42732         "Finland (Suomi)",
42733         "fi",
42734         "358",
42735         0
42736       ],
42737       [
42738         "France",
42739         "fr",
42740         "33"
42741       ],
42742       [
42743         "French Guiana (Guyane française)",
42744         "gf",
42745         "594"
42746       ],
42747       [
42748         "French Polynesia (Polynésie française)",
42749         "pf",
42750         "689"
42751       ],
42752       [
42753         "Gabon",
42754         "ga",
42755         "241"
42756       ],
42757       [
42758         "Gambia",
42759         "gm",
42760         "220"
42761       ],
42762       [
42763         "Georgia (საქართველო)",
42764         "ge",
42765         "995"
42766       ],
42767       [
42768         "Germany (Deutschland)",
42769         "de",
42770         "49"
42771       ],
42772       [
42773         "Ghana (Gaana)",
42774         "gh",
42775         "233"
42776       ],
42777       [
42778         "Gibraltar",
42779         "gi",
42780         "350"
42781       ],
42782       [
42783         "Greece (Ελλάδα)",
42784         "gr",
42785         "30"
42786       ],
42787       [
42788         "Greenland (Kalaallit Nunaat)",
42789         "gl",
42790         "299"
42791       ],
42792       [
42793         "Grenada",
42794         "gd",
42795         "1473"
42796       ],
42797       [
42798         "Guadeloupe",
42799         "gp",
42800         "590",
42801         0
42802       ],
42803       [
42804         "Guam",
42805         "gu",
42806         "1671"
42807       ],
42808       [
42809         "Guatemala",
42810         "gt",
42811         "502"
42812       ],
42813       [
42814         "Guernsey",
42815         "gg",
42816         "44",
42817         1
42818       ],
42819       [
42820         "Guinea (Guinée)",
42821         "gn",
42822         "224"
42823       ],
42824       [
42825         "Guinea-Bissau (Guiné Bissau)",
42826         "gw",
42827         "245"
42828       ],
42829       [
42830         "Guyana",
42831         "gy",
42832         "592"
42833       ],
42834       [
42835         "Haiti",
42836         "ht",
42837         "509"
42838       ],
42839       [
42840         "Honduras",
42841         "hn",
42842         "504"
42843       ],
42844       [
42845         "Hong Kong (香港)",
42846         "hk",
42847         "852"
42848       ],
42849       [
42850         "Hungary (Magyarország)",
42851         "hu",
42852         "36"
42853       ],
42854       [
42855         "Iceland (Ísland)",
42856         "is",
42857         "354"
42858       ],
42859       [
42860         "India (भारत)",
42861         "in",
42862         "91"
42863       ],
42864       [
42865         "Indonesia",
42866         "id",
42867         "62"
42868       ],
42869       [
42870         "Iran (‫ایران‬‎)",
42871         "ir",
42872         "98"
42873       ],
42874       [
42875         "Iraq (‫العراق‬‎)",
42876         "iq",
42877         "964"
42878       ],
42879       [
42880         "Ireland",
42881         "ie",
42882         "353"
42883       ],
42884       [
42885         "Isle of Man",
42886         "im",
42887         "44",
42888         2
42889       ],
42890       [
42891         "Israel (‫ישראל‬‎)",
42892         "il",
42893         "972"
42894       ],
42895       [
42896         "Italy (Italia)",
42897         "it",
42898         "39",
42899         0
42900       ],
42901       [
42902         "Jamaica",
42903         "jm",
42904         "1876"
42905       ],
42906       [
42907         "Japan (日本)",
42908         "jp",
42909         "81"
42910       ],
42911       [
42912         "Jersey",
42913         "je",
42914         "44",
42915         3
42916       ],
42917       [
42918         "Jordan (‫الأردن‬‎)",
42919         "jo",
42920         "962"
42921       ],
42922       [
42923         "Kazakhstan (Казахстан)",
42924         "kz",
42925         "7",
42926         1
42927       ],
42928       [
42929         "Kenya",
42930         "ke",
42931         "254"
42932       ],
42933       [
42934         "Kiribati",
42935         "ki",
42936         "686"
42937       ],
42938       [
42939         "Kosovo",
42940         "xk",
42941         "383"
42942       ],
42943       [
42944         "Kuwait (‫الكويت‬‎)",
42945         "kw",
42946         "965"
42947       ],
42948       [
42949         "Kyrgyzstan (Кыргызстан)",
42950         "kg",
42951         "996"
42952       ],
42953       [
42954         "Laos (ລາວ)",
42955         "la",
42956         "856"
42957       ],
42958       [
42959         "Latvia (Latvija)",
42960         "lv",
42961         "371"
42962       ],
42963       [
42964         "Lebanon (‫لبنان‬‎)",
42965         "lb",
42966         "961"
42967       ],
42968       [
42969         "Lesotho",
42970         "ls",
42971         "266"
42972       ],
42973       [
42974         "Liberia",
42975         "lr",
42976         "231"
42977       ],
42978       [
42979         "Libya (‫ليبيا‬‎)",
42980         "ly",
42981         "218"
42982       ],
42983       [
42984         "Liechtenstein",
42985         "li",
42986         "423"
42987       ],
42988       [
42989         "Lithuania (Lietuva)",
42990         "lt",
42991         "370"
42992       ],
42993       [
42994         "Luxembourg",
42995         "lu",
42996         "352"
42997       ],
42998       [
42999         "Macau (澳門)",
43000         "mo",
43001         "853"
43002       ],
43003       [
43004         "Macedonia (FYROM) (Македонија)",
43005         "mk",
43006         "389"
43007       ],
43008       [
43009         "Madagascar (Madagasikara)",
43010         "mg",
43011         "261"
43012       ],
43013       [
43014         "Malawi",
43015         "mw",
43016         "265"
43017       ],
43018       [
43019         "Malaysia",
43020         "my",
43021         "60"
43022       ],
43023       [
43024         "Maldives",
43025         "mv",
43026         "960"
43027       ],
43028       [
43029         "Mali",
43030         "ml",
43031         "223"
43032       ],
43033       [
43034         "Malta",
43035         "mt",
43036         "356"
43037       ],
43038       [
43039         "Marshall Islands",
43040         "mh",
43041         "692"
43042       ],
43043       [
43044         "Martinique",
43045         "mq",
43046         "596"
43047       ],
43048       [
43049         "Mauritania (‫موريتانيا‬‎)",
43050         "mr",
43051         "222"
43052       ],
43053       [
43054         "Mauritius (Moris)",
43055         "mu",
43056         "230"
43057       ],
43058       [
43059         "Mayotte",
43060         "yt",
43061         "262",
43062         1
43063       ],
43064       [
43065         "Mexico (México)",
43066         "mx",
43067         "52"
43068       ],
43069       [
43070         "Micronesia",
43071         "fm",
43072         "691"
43073       ],
43074       [
43075         "Moldova (Republica Moldova)",
43076         "md",
43077         "373"
43078       ],
43079       [
43080         "Monaco",
43081         "mc",
43082         "377"
43083       ],
43084       [
43085         "Mongolia (Монгол)",
43086         "mn",
43087         "976"
43088       ],
43089       [
43090         "Montenegro (Crna Gora)",
43091         "me",
43092         "382"
43093       ],
43094       [
43095         "Montserrat",
43096         "ms",
43097         "1664"
43098       ],
43099       [
43100         "Morocco (‫المغرب‬‎)",
43101         "ma",
43102         "212",
43103         0
43104       ],
43105       [
43106         "Mozambique (Moçambique)",
43107         "mz",
43108         "258"
43109       ],
43110       [
43111         "Myanmar (Burma) (မြန်မာ)",
43112         "mm",
43113         "95"
43114       ],
43115       [
43116         "Namibia (Namibië)",
43117         "na",
43118         "264"
43119       ],
43120       [
43121         "Nauru",
43122         "nr",
43123         "674"
43124       ],
43125       [
43126         "Nepal (नेपाल)",
43127         "np",
43128         "977"
43129       ],
43130       [
43131         "Netherlands (Nederland)",
43132         "nl",
43133         "31"
43134       ],
43135       [
43136         "New Caledonia (Nouvelle-Calédonie)",
43137         "nc",
43138         "687"
43139       ],
43140       [
43141         "New Zealand",
43142         "nz",
43143         "64"
43144       ],
43145       [
43146         "Nicaragua",
43147         "ni",
43148         "505"
43149       ],
43150       [
43151         "Niger (Nijar)",
43152         "ne",
43153         "227"
43154       ],
43155       [
43156         "Nigeria",
43157         "ng",
43158         "234"
43159       ],
43160       [
43161         "Niue",
43162         "nu",
43163         "683"
43164       ],
43165       [
43166         "Norfolk Island",
43167         "nf",
43168         "672"
43169       ],
43170       [
43171         "North Korea (조선 민주주의 인민 공화국)",
43172         "kp",
43173         "850"
43174       ],
43175       [
43176         "Northern Mariana Islands",
43177         "mp",
43178         "1670"
43179       ],
43180       [
43181         "Norway (Norge)",
43182         "no",
43183         "47",
43184         0
43185       ],
43186       [
43187         "Oman (‫عُمان‬‎)",
43188         "om",
43189         "968"
43190       ],
43191       [
43192         "Pakistan (‫پاکستان‬‎)",
43193         "pk",
43194         "92"
43195       ],
43196       [
43197         "Palau",
43198         "pw",
43199         "680"
43200       ],
43201       [
43202         "Palestine (‫فلسطين‬‎)",
43203         "ps",
43204         "970"
43205       ],
43206       [
43207         "Panama (Panamá)",
43208         "pa",
43209         "507"
43210       ],
43211       [
43212         "Papua New Guinea",
43213         "pg",
43214         "675"
43215       ],
43216       [
43217         "Paraguay",
43218         "py",
43219         "595"
43220       ],
43221       [
43222         "Peru (Perú)",
43223         "pe",
43224         "51"
43225       ],
43226       [
43227         "Philippines",
43228         "ph",
43229         "63"
43230       ],
43231       [
43232         "Poland (Polska)",
43233         "pl",
43234         "48"
43235       ],
43236       [
43237         "Portugal",
43238         "pt",
43239         "351"
43240       ],
43241       [
43242         "Puerto Rico",
43243         "pr",
43244         "1",
43245         3,
43246         ["787", "939"]
43247       ],
43248       [
43249         "Qatar (‫قطر‬‎)",
43250         "qa",
43251         "974"
43252       ],
43253       [
43254         "Réunion (La Réunion)",
43255         "re",
43256         "262",
43257         0
43258       ],
43259       [
43260         "Romania (România)",
43261         "ro",
43262         "40"
43263       ],
43264       [
43265         "Russia (Россия)",
43266         "ru",
43267         "7",
43268         0
43269       ],
43270       [
43271         "Rwanda",
43272         "rw",
43273         "250"
43274       ],
43275       [
43276         "Saint Barthélemy",
43277         "bl",
43278         "590",
43279         1
43280       ],
43281       [
43282         "Saint Helena",
43283         "sh",
43284         "290"
43285       ],
43286       [
43287         "Saint Kitts and Nevis",
43288         "kn",
43289         "1869"
43290       ],
43291       [
43292         "Saint Lucia",
43293         "lc",
43294         "1758"
43295       ],
43296       [
43297         "Saint Martin (Saint-Martin (partie française))",
43298         "mf",
43299         "590",
43300         2
43301       ],
43302       [
43303         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43304         "pm",
43305         "508"
43306       ],
43307       [
43308         "Saint Vincent and the Grenadines",
43309         "vc",
43310         "1784"
43311       ],
43312       [
43313         "Samoa",
43314         "ws",
43315         "685"
43316       ],
43317       [
43318         "San Marino",
43319         "sm",
43320         "378"
43321       ],
43322       [
43323         "São Tomé and Príncipe (São Tomé e Príncipe)",
43324         "st",
43325         "239"
43326       ],
43327       [
43328         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43329         "sa",
43330         "966"
43331       ],
43332       [
43333         "Senegal (Sénégal)",
43334         "sn",
43335         "221"
43336       ],
43337       [
43338         "Serbia (Србија)",
43339         "rs",
43340         "381"
43341       ],
43342       [
43343         "Seychelles",
43344         "sc",
43345         "248"
43346       ],
43347       [
43348         "Sierra Leone",
43349         "sl",
43350         "232"
43351       ],
43352       [
43353         "Singapore",
43354         "sg",
43355         "65"
43356       ],
43357       [
43358         "Sint Maarten",
43359         "sx",
43360         "1721"
43361       ],
43362       [
43363         "Slovakia (Slovensko)",
43364         "sk",
43365         "421"
43366       ],
43367       [
43368         "Slovenia (Slovenija)",
43369         "si",
43370         "386"
43371       ],
43372       [
43373         "Solomon Islands",
43374         "sb",
43375         "677"
43376       ],
43377       [
43378         "Somalia (Soomaaliya)",
43379         "so",
43380         "252"
43381       ],
43382       [
43383         "South Africa",
43384         "za",
43385         "27"
43386       ],
43387       [
43388         "South Korea (대한민국)",
43389         "kr",
43390         "82"
43391       ],
43392       [
43393         "South Sudan (‫جنوب السودان‬‎)",
43394         "ss",
43395         "211"
43396       ],
43397       [
43398         "Spain (España)",
43399         "es",
43400         "34"
43401       ],
43402       [
43403         "Sri Lanka (ශ්‍රී ලංකාව)",
43404         "lk",
43405         "94"
43406       ],
43407       [
43408         "Sudan (‫السودان‬‎)",
43409         "sd",
43410         "249"
43411       ],
43412       [
43413         "Suriname",
43414         "sr",
43415         "597"
43416       ],
43417       [
43418         "Svalbard and Jan Mayen",
43419         "sj",
43420         "47",
43421         1
43422       ],
43423       [
43424         "Swaziland",
43425         "sz",
43426         "268"
43427       ],
43428       [
43429         "Sweden (Sverige)",
43430         "se",
43431         "46"
43432       ],
43433       [
43434         "Switzerland (Schweiz)",
43435         "ch",
43436         "41"
43437       ],
43438       [
43439         "Syria (‫سوريا‬‎)",
43440         "sy",
43441         "963"
43442       ],
43443       [
43444         "Taiwan (台灣)",
43445         "tw",
43446         "886"
43447       ],
43448       [
43449         "Tajikistan",
43450         "tj",
43451         "992"
43452       ],
43453       [
43454         "Tanzania",
43455         "tz",
43456         "255"
43457       ],
43458       [
43459         "Thailand (ไทย)",
43460         "th",
43461         "66"
43462       ],
43463       [
43464         "Timor-Leste",
43465         "tl",
43466         "670"
43467       ],
43468       [
43469         "Togo",
43470         "tg",
43471         "228"
43472       ],
43473       [
43474         "Tokelau",
43475         "tk",
43476         "690"
43477       ],
43478       [
43479         "Tonga",
43480         "to",
43481         "676"
43482       ],
43483       [
43484         "Trinidad and Tobago",
43485         "tt",
43486         "1868"
43487       ],
43488       [
43489         "Tunisia (‫تونس‬‎)",
43490         "tn",
43491         "216"
43492       ],
43493       [
43494         "Turkey (Türkiye)",
43495         "tr",
43496         "90"
43497       ],
43498       [
43499         "Turkmenistan",
43500         "tm",
43501         "993"
43502       ],
43503       [
43504         "Turks and Caicos Islands",
43505         "tc",
43506         "1649"
43507       ],
43508       [
43509         "Tuvalu",
43510         "tv",
43511         "688"
43512       ],
43513       [
43514         "U.S. Virgin Islands",
43515         "vi",
43516         "1340"
43517       ],
43518       [
43519         "Uganda",
43520         "ug",
43521         "256"
43522       ],
43523       [
43524         "Ukraine (Україна)",
43525         "ua",
43526         "380"
43527       ],
43528       [
43529         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43530         "ae",
43531         "971"
43532       ],
43533       [
43534         "United Kingdom",
43535         "gb",
43536         "44",
43537         0
43538       ],
43539       [
43540         "United States",
43541         "us",
43542         "1",
43543         0
43544       ],
43545       [
43546         "Uruguay",
43547         "uy",
43548         "598"
43549       ],
43550       [
43551         "Uzbekistan (Oʻzbekiston)",
43552         "uz",
43553         "998"
43554       ],
43555       [
43556         "Vanuatu",
43557         "vu",
43558         "678"
43559       ],
43560       [
43561         "Vatican City (Città del Vaticano)",
43562         "va",
43563         "39",
43564         1
43565       ],
43566       [
43567         "Venezuela",
43568         "ve",
43569         "58"
43570       ],
43571       [
43572         "Vietnam (Việt Nam)",
43573         "vn",
43574         "84"
43575       ],
43576       [
43577         "Wallis and Futuna (Wallis-et-Futuna)",
43578         "wf",
43579         "681"
43580       ],
43581       [
43582         "Western Sahara (‫الصحراء الغربية‬‎)",
43583         "eh",
43584         "212",
43585         1
43586       ],
43587       [
43588         "Yemen (‫اليمن‬‎)",
43589         "ye",
43590         "967"
43591       ],
43592       [
43593         "Zambia",
43594         "zm",
43595         "260"
43596       ],
43597       [
43598         "Zimbabwe",
43599         "zw",
43600         "263"
43601       ],
43602       [
43603         "Åland Islands",
43604         "ax",
43605         "358",
43606         1
43607       ]
43608   ];
43609   
43610   return d;
43611 }/**
43612 *    This script refer to:
43613 *    Title: International Telephone Input
43614 *    Author: Jack O'Connor
43615 *    Code version:  v12.1.12
43616 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43617 **/
43618
43619 /**
43620  * @class Roo.bootstrap.PhoneInput
43621  * @extends Roo.bootstrap.TriggerField
43622  * An input with International dial-code selection
43623  
43624  * @cfg {String} defaultDialCode default '+852'
43625  * @cfg {Array} preferedCountries default []
43626   
43627  * @constructor
43628  * Create a new PhoneInput.
43629  * @param {Object} config Configuration options
43630  */
43631
43632 Roo.bootstrap.PhoneInput = function(config) {
43633     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43634 };
43635
43636 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43637         /**
43638         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43639         */
43640         listWidth: undefined,
43641         
43642         selectedClass: 'active',
43643         
43644         invalidClass : "has-warning",
43645         
43646         validClass: 'has-success',
43647         
43648         allowed: '0123456789',
43649         
43650         max_length: 15,
43651         
43652         /**
43653          * @cfg {String} defaultDialCode The default dial code when initializing the input
43654          */
43655         defaultDialCode: '+852',
43656         
43657         /**
43658          * @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
43659          */
43660         preferedCountries: false,
43661         
43662         getAutoCreate : function()
43663         {
43664             var data = Roo.bootstrap.PhoneInputData();
43665             var align = this.labelAlign || this.parentLabelAlign();
43666             var id = Roo.id();
43667             
43668             this.allCountries = [];
43669             this.dialCodeMapping = [];
43670             
43671             for (var i = 0; i < data.length; i++) {
43672               var c = data[i];
43673               this.allCountries[i] = {
43674                 name: c[0],
43675                 iso2: c[1],
43676                 dialCode: c[2],
43677                 priority: c[3] || 0,
43678                 areaCodes: c[4] || null
43679               };
43680               this.dialCodeMapping[c[2]] = {
43681                   name: c[0],
43682                   iso2: c[1],
43683                   priority: c[3] || 0,
43684                   areaCodes: c[4] || null
43685               };
43686             }
43687             
43688             var cfg = {
43689                 cls: 'form-group',
43690                 cn: []
43691             };
43692             
43693             var input =  {
43694                 tag: 'input',
43695                 id : id,
43696                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43697                 maxlength: this.max_length,
43698                 cls : 'form-control tel-input',
43699                 autocomplete: 'new-password'
43700             };
43701             
43702             var hiddenInput = {
43703                 tag: 'input',
43704                 type: 'hidden',
43705                 cls: 'hidden-tel-input'
43706             };
43707             
43708             if (this.name) {
43709                 hiddenInput.name = this.name;
43710             }
43711             
43712             if (this.disabled) {
43713                 input.disabled = true;
43714             }
43715             
43716             var flag_container = {
43717                 tag: 'div',
43718                 cls: 'flag-box',
43719                 cn: [
43720                     {
43721                         tag: 'div',
43722                         cls: 'flag'
43723                     },
43724                     {
43725                         tag: 'div',
43726                         cls: 'caret'
43727                     }
43728                 ]
43729             };
43730             
43731             var box = {
43732                 tag: 'div',
43733                 cls: this.hasFeedback ? 'has-feedback' : '',
43734                 cn: [
43735                     hiddenInput,
43736                     input,
43737                     {
43738                         tag: 'input',
43739                         cls: 'dial-code-holder',
43740                         disabled: true
43741                     }
43742                 ]
43743             };
43744             
43745             var container = {
43746                 cls: 'roo-select2-container input-group',
43747                 cn: [
43748                     flag_container,
43749                     box
43750                 ]
43751             };
43752             
43753             if (this.fieldLabel.length) {
43754                 var indicator = {
43755                     tag: 'i',
43756                     tooltip: 'This field is required'
43757                 };
43758                 
43759                 var label = {
43760                     tag: 'label',
43761                     'for':  id,
43762                     cls: 'control-label',
43763                     cn: []
43764                 };
43765                 
43766                 var label_text = {
43767                     tag: 'span',
43768                     html: this.fieldLabel
43769                 };
43770                 
43771                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43772                 label.cn = [
43773                     indicator,
43774                     label_text
43775                 ];
43776                 
43777                 if(this.indicatorpos == 'right') {
43778                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43779                     label.cn = [
43780                         label_text,
43781                         indicator
43782                     ];
43783                 }
43784                 
43785                 if(align == 'left') {
43786                     container = {
43787                         tag: 'div',
43788                         cn: [
43789                             container
43790                         ]
43791                     };
43792                     
43793                     if(this.labelWidth > 12){
43794                         label.style = "width: " + this.labelWidth + 'px';
43795                     }
43796                     if(this.labelWidth < 13 && this.labelmd == 0){
43797                         this.labelmd = this.labelWidth;
43798                     }
43799                     if(this.labellg > 0){
43800                         label.cls += ' col-lg-' + this.labellg;
43801                         input.cls += ' col-lg-' + (12 - this.labellg);
43802                     }
43803                     if(this.labelmd > 0){
43804                         label.cls += ' col-md-' + this.labelmd;
43805                         container.cls += ' col-md-' + (12 - this.labelmd);
43806                     }
43807                     if(this.labelsm > 0){
43808                         label.cls += ' col-sm-' + this.labelsm;
43809                         container.cls += ' col-sm-' + (12 - this.labelsm);
43810                     }
43811                     if(this.labelxs > 0){
43812                         label.cls += ' col-xs-' + this.labelxs;
43813                         container.cls += ' col-xs-' + (12 - this.labelxs);
43814                     }
43815                 }
43816             }
43817             
43818             cfg.cn = [
43819                 label,
43820                 container
43821             ];
43822             
43823             var settings = this;
43824             
43825             ['xs','sm','md','lg'].map(function(size){
43826                 if (settings[size]) {
43827                     cfg.cls += ' col-' + size + '-' + settings[size];
43828                 }
43829             });
43830             
43831             this.store = new Roo.data.Store({
43832                 proxy : new Roo.data.MemoryProxy({}),
43833                 reader : new Roo.data.JsonReader({
43834                     fields : [
43835                         {
43836                             'name' : 'name',
43837                             'type' : 'string'
43838                         },
43839                         {
43840                             'name' : 'iso2',
43841                             'type' : 'string'
43842                         },
43843                         {
43844                             'name' : 'dialCode',
43845                             'type' : 'string'
43846                         },
43847                         {
43848                             'name' : 'priority',
43849                             'type' : 'string'
43850                         },
43851                         {
43852                             'name' : 'areaCodes',
43853                             'type' : 'string'
43854                         }
43855                     ]
43856                 })
43857             });
43858             
43859             if(!this.preferedCountries) {
43860                 this.preferedCountries = [
43861                     'hk',
43862                     'gb',
43863                     'us'
43864                 ];
43865             }
43866             
43867             var p = this.preferedCountries.reverse();
43868             
43869             if(p) {
43870                 for (var i = 0; i < p.length; i++) {
43871                     for (var j = 0; j < this.allCountries.length; j++) {
43872                         if(this.allCountries[j].iso2 == p[i]) {
43873                             var t = this.allCountries[j];
43874                             this.allCountries.splice(j,1);
43875                             this.allCountries.unshift(t);
43876                         }
43877                     } 
43878                 }
43879             }
43880             
43881             this.store.proxy.data = {
43882                 success: true,
43883                 data: this.allCountries
43884             };
43885             
43886             return cfg;
43887         },
43888         
43889         initEvents : function()
43890         {
43891             this.createList();
43892             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43893             
43894             this.indicator = this.indicatorEl();
43895             this.flag = this.flagEl();
43896             this.dialCodeHolder = this.dialCodeHolderEl();
43897             
43898             this.trigger = this.el.select('div.flag-box',true).first();
43899             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43900             
43901             var _this = this;
43902             
43903             (function(){
43904                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43905                 _this.list.setWidth(lw);
43906             }).defer(100);
43907             
43908             this.list.on('mouseover', this.onViewOver, this);
43909             this.list.on('mousemove', this.onViewMove, this);
43910             this.inputEl().on("keyup", this.onKeyUp, this);
43911             this.inputEl().on("keypress", this.onKeyPress, this);
43912             
43913             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43914
43915             this.view = new Roo.View(this.list, this.tpl, {
43916                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43917             });
43918             
43919             this.view.on('click', this.onViewClick, this);
43920             this.setValue(this.defaultDialCode);
43921         },
43922         
43923         onTriggerClick : function(e)
43924         {
43925             Roo.log('trigger click');
43926             if(this.disabled){
43927                 return;
43928             }
43929             
43930             if(this.isExpanded()){
43931                 this.collapse();
43932                 this.hasFocus = false;
43933             }else {
43934                 this.store.load({});
43935                 this.hasFocus = true;
43936                 this.expand();
43937             }
43938         },
43939         
43940         isExpanded : function()
43941         {
43942             return this.list.isVisible();
43943         },
43944         
43945         collapse : function()
43946         {
43947             if(!this.isExpanded()){
43948                 return;
43949             }
43950             this.list.hide();
43951             Roo.get(document).un('mousedown', this.collapseIf, this);
43952             Roo.get(document).un('mousewheel', this.collapseIf, this);
43953             this.fireEvent('collapse', this);
43954             this.validate();
43955         },
43956         
43957         expand : function()
43958         {
43959             Roo.log('expand');
43960
43961             if(this.isExpanded() || !this.hasFocus){
43962                 return;
43963             }
43964             
43965             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43966             this.list.setWidth(lw);
43967             
43968             this.list.show();
43969             this.restrictHeight();
43970             
43971             Roo.get(document).on('mousedown', this.collapseIf, this);
43972             Roo.get(document).on('mousewheel', this.collapseIf, this);
43973             
43974             this.fireEvent('expand', this);
43975         },
43976         
43977         restrictHeight : function()
43978         {
43979             this.list.alignTo(this.inputEl(), this.listAlign);
43980             this.list.alignTo(this.inputEl(), this.listAlign);
43981         },
43982         
43983         onViewOver : function(e, t)
43984         {
43985             if(this.inKeyMode){
43986                 return;
43987             }
43988             var item = this.view.findItemFromChild(t);
43989             
43990             if(item){
43991                 var index = this.view.indexOf(item);
43992                 this.select(index, false);
43993             }
43994         },
43995
43996         // private
43997         onViewClick : function(view, doFocus, el, e)
43998         {
43999             var index = this.view.getSelectedIndexes()[0];
44000             
44001             var r = this.store.getAt(index);
44002             
44003             if(r){
44004                 this.onSelect(r, index);
44005             }
44006             if(doFocus !== false && !this.blockFocus){
44007                 this.inputEl().focus();
44008             }
44009         },
44010         
44011         onViewMove : function(e, t)
44012         {
44013             this.inKeyMode = false;
44014         },
44015         
44016         select : function(index, scrollIntoView)
44017         {
44018             this.selectedIndex = index;
44019             this.view.select(index);
44020             if(scrollIntoView !== false){
44021                 var el = this.view.getNode(index);
44022                 if(el){
44023                     this.list.scrollChildIntoView(el, false);
44024                 }
44025             }
44026         },
44027         
44028         createList : function()
44029         {
44030             this.list = Roo.get(document.body).createChild({
44031                 tag: 'ul',
44032                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44033                 style: 'display:none'
44034             });
44035             
44036             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44037         },
44038         
44039         collapseIf : function(e)
44040         {
44041             var in_combo  = e.within(this.el);
44042             var in_list =  e.within(this.list);
44043             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44044             
44045             if (in_combo || in_list || is_list) {
44046                 return;
44047             }
44048             this.collapse();
44049         },
44050         
44051         onSelect : function(record, index)
44052         {
44053             if(this.fireEvent('beforeselect', this, record, index) !== false){
44054                 
44055                 this.setFlagClass(record.data.iso2);
44056                 this.setDialCode(record.data.dialCode);
44057                 this.hasFocus = false;
44058                 this.collapse();
44059                 this.fireEvent('select', this, record, index);
44060             }
44061         },
44062         
44063         flagEl : function()
44064         {
44065             var flag = this.el.select('div.flag',true).first();
44066             if(!flag){
44067                 return false;
44068             }
44069             return flag;
44070         },
44071         
44072         dialCodeHolderEl : function()
44073         {
44074             var d = this.el.select('input.dial-code-holder',true).first();
44075             if(!d){
44076                 return false;
44077             }
44078             return d;
44079         },
44080         
44081         setDialCode : function(v)
44082         {
44083             this.dialCodeHolder.dom.value = '+'+v;
44084         },
44085         
44086         setFlagClass : function(n)
44087         {
44088             this.flag.dom.className = 'flag '+n;
44089         },
44090         
44091         getValue : function()
44092         {
44093             var v = this.inputEl().getValue();
44094             if(this.dialCodeHolder) {
44095                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44096             }
44097             return v;
44098         },
44099         
44100         setValue : function(v)
44101         {
44102             var d = this.getDialCode(v);
44103             
44104             //invalid dial code
44105             if(v.length == 0 || !d || d.length == 0) {
44106                 if(this.rendered){
44107                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44108                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44109                 }
44110                 return;
44111             }
44112             
44113             //valid dial code
44114             this.setFlagClass(this.dialCodeMapping[d].iso2);
44115             this.setDialCode(d);
44116             this.inputEl().dom.value = v.replace('+'+d,'');
44117             this.hiddenEl().dom.value = this.getValue();
44118             
44119             this.validate();
44120         },
44121         
44122         getDialCode : function(v)
44123         {
44124             v = v ||  '';
44125             
44126             if (v.length == 0) {
44127                 return this.dialCodeHolder.dom.value;
44128             }
44129             
44130             var dialCode = "";
44131             if (v.charAt(0) != "+") {
44132                 return false;
44133             }
44134             var numericChars = "";
44135             for (var i = 1; i < v.length; i++) {
44136               var c = v.charAt(i);
44137               if (!isNaN(c)) {
44138                 numericChars += c;
44139                 if (this.dialCodeMapping[numericChars]) {
44140                   dialCode = v.substr(1, i);
44141                 }
44142                 if (numericChars.length == 4) {
44143                   break;
44144                 }
44145               }
44146             }
44147             return dialCode;
44148         },
44149         
44150         reset : function()
44151         {
44152             this.setValue(this.defaultDialCode);
44153             this.validate();
44154         },
44155         
44156         hiddenEl : function()
44157         {
44158             return this.el.select('input.hidden-tel-input',true).first();
44159         },
44160         
44161         // after setting val
44162         onKeyUp : function(e){
44163             this.setValue(this.getValue());
44164         },
44165         
44166         onKeyPress : function(e){
44167             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44168                 e.stopEvent();
44169             }
44170         }
44171         
44172 });
44173 /**
44174  * @class Roo.bootstrap.MoneyField
44175  * @extends Roo.bootstrap.ComboBox
44176  * Bootstrap MoneyField class
44177  * 
44178  * @constructor
44179  * Create a new MoneyField.
44180  * @param {Object} config Configuration options
44181  */
44182
44183 Roo.bootstrap.MoneyField = function(config) {
44184     
44185     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44186     
44187 };
44188
44189 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44190     
44191     /**
44192      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44193      */
44194     allowDecimals : true,
44195     /**
44196      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44197      */
44198     decimalSeparator : ".",
44199     /**
44200      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44201      */
44202     decimalPrecision : 0,
44203     /**
44204      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44205      */
44206     allowNegative : true,
44207     /**
44208      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44209      */
44210     allowZero: true,
44211     /**
44212      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44213      */
44214     minValue : Number.NEGATIVE_INFINITY,
44215     /**
44216      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44217      */
44218     maxValue : Number.MAX_VALUE,
44219     /**
44220      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44221      */
44222     minText : "The minimum value for this field is {0}",
44223     /**
44224      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44225      */
44226     maxText : "The maximum value for this field is {0}",
44227     /**
44228      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44229      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44230      */
44231     nanText : "{0} is not a valid number",
44232     /**
44233      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44234      */
44235     castInt : true,
44236     /**
44237      * @cfg {String} defaults currency of the MoneyField
44238      * value should be in lkey
44239      */
44240     defaultCurrency : false,
44241     /**
44242      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44243      */
44244     thousandsDelimiter : false,
44245     /**
44246      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44247      */
44248     max_length: false,
44249     
44250     inputlg : 9,
44251     inputmd : 9,
44252     inputsm : 9,
44253     inputxs : 6,
44254     
44255     store : false,
44256     
44257     getAutoCreate : function()
44258     {
44259         var align = this.labelAlign || this.parentLabelAlign();
44260         
44261         var id = Roo.id();
44262
44263         var cfg = {
44264             cls: 'form-group',
44265             cn: []
44266         };
44267
44268         var input =  {
44269             tag: 'input',
44270             id : id,
44271             cls : 'form-control roo-money-amount-input',
44272             autocomplete: 'new-password'
44273         };
44274         
44275         var hiddenInput = {
44276             tag: 'input',
44277             type: 'hidden',
44278             id: Roo.id(),
44279             cls: 'hidden-number-input'
44280         };
44281         
44282         if(this.max_length) {
44283             input.maxlength = this.max_length; 
44284         }
44285         
44286         if (this.name) {
44287             hiddenInput.name = this.name;
44288         }
44289
44290         if (this.disabled) {
44291             input.disabled = true;
44292         }
44293
44294         var clg = 12 - this.inputlg;
44295         var cmd = 12 - this.inputmd;
44296         var csm = 12 - this.inputsm;
44297         var cxs = 12 - this.inputxs;
44298         
44299         var container = {
44300             tag : 'div',
44301             cls : 'row roo-money-field',
44302             cn : [
44303                 {
44304                     tag : 'div',
44305                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44306                     cn : [
44307                         {
44308                             tag : 'div',
44309                             cls: 'roo-select2-container input-group',
44310                             cn: [
44311                                 {
44312                                     tag : 'input',
44313                                     cls : 'form-control roo-money-currency-input',
44314                                     autocomplete: 'new-password',
44315                                     readOnly : 1,
44316                                     name : this.currencyName
44317                                 },
44318                                 {
44319                                     tag :'span',
44320                                     cls : 'input-group-addon',
44321                                     cn : [
44322                                         {
44323                                             tag: 'span',
44324                                             cls: 'caret'
44325                                         }
44326                                     ]
44327                                 }
44328                             ]
44329                         }
44330                     ]
44331                 },
44332                 {
44333                     tag : 'div',
44334                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44335                     cn : [
44336                         {
44337                             tag: 'div',
44338                             cls: this.hasFeedback ? 'has-feedback' : '',
44339                             cn: [
44340                                 input
44341                             ]
44342                         }
44343                     ]
44344                 }
44345             ]
44346             
44347         };
44348         
44349         if (this.fieldLabel.length) {
44350             var indicator = {
44351                 tag: 'i',
44352                 tooltip: 'This field is required'
44353             };
44354
44355             var label = {
44356                 tag: 'label',
44357                 'for':  id,
44358                 cls: 'control-label',
44359                 cn: []
44360             };
44361
44362             var label_text = {
44363                 tag: 'span',
44364                 html: this.fieldLabel
44365             };
44366
44367             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44368             label.cn = [
44369                 indicator,
44370                 label_text
44371             ];
44372
44373             if(this.indicatorpos == 'right') {
44374                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44375                 label.cn = [
44376                     label_text,
44377                     indicator
44378                 ];
44379             }
44380
44381             if(align == 'left') {
44382                 container = {
44383                     tag: 'div',
44384                     cn: [
44385                         container
44386                     ]
44387                 };
44388
44389                 if(this.labelWidth > 12){
44390                     label.style = "width: " + this.labelWidth + 'px';
44391                 }
44392                 if(this.labelWidth < 13 && this.labelmd == 0){
44393                     this.labelmd = this.labelWidth;
44394                 }
44395                 if(this.labellg > 0){
44396                     label.cls += ' col-lg-' + this.labellg;
44397                     input.cls += ' col-lg-' + (12 - this.labellg);
44398                 }
44399                 if(this.labelmd > 0){
44400                     label.cls += ' col-md-' + this.labelmd;
44401                     container.cls += ' col-md-' + (12 - this.labelmd);
44402                 }
44403                 if(this.labelsm > 0){
44404                     label.cls += ' col-sm-' + this.labelsm;
44405                     container.cls += ' col-sm-' + (12 - this.labelsm);
44406                 }
44407                 if(this.labelxs > 0){
44408                     label.cls += ' col-xs-' + this.labelxs;
44409                     container.cls += ' col-xs-' + (12 - this.labelxs);
44410                 }
44411             }
44412         }
44413
44414         cfg.cn = [
44415             label,
44416             container,
44417             hiddenInput
44418         ];
44419         
44420         var settings = this;
44421
44422         ['xs','sm','md','lg'].map(function(size){
44423             if (settings[size]) {
44424                 cfg.cls += ' col-' + size + '-' + settings[size];
44425             }
44426         });
44427         
44428         return cfg;
44429     },
44430     
44431     initEvents : function()
44432     {
44433         this.indicator = this.indicatorEl();
44434         
44435         this.initCurrencyEvent();
44436         
44437         this.initNumberEvent();
44438     },
44439     
44440     initCurrencyEvent : function()
44441     {
44442         if (!this.store) {
44443             throw "can not find store for combo";
44444         }
44445         
44446         this.store = Roo.factory(this.store, Roo.data);
44447         this.store.parent = this;
44448         
44449         this.createList();
44450         
44451         this.triggerEl = this.el.select('.input-group-addon', true).first();
44452         
44453         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44454         
44455         var _this = this;
44456         
44457         (function(){
44458             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44459             _this.list.setWidth(lw);
44460         }).defer(100);
44461         
44462         this.list.on('mouseover', this.onViewOver, this);
44463         this.list.on('mousemove', this.onViewMove, this);
44464         this.list.on('scroll', this.onViewScroll, this);
44465         
44466         if(!this.tpl){
44467             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44468         }
44469         
44470         this.view = new Roo.View(this.list, this.tpl, {
44471             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44472         });
44473         
44474         this.view.on('click', this.onViewClick, this);
44475         
44476         this.store.on('beforeload', this.onBeforeLoad, this);
44477         this.store.on('load', this.onLoad, this);
44478         this.store.on('loadexception', this.onLoadException, this);
44479         
44480         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44481             "up" : function(e){
44482                 this.inKeyMode = true;
44483                 this.selectPrev();
44484             },
44485
44486             "down" : function(e){
44487                 if(!this.isExpanded()){
44488                     this.onTriggerClick();
44489                 }else{
44490                     this.inKeyMode = true;
44491                     this.selectNext();
44492                 }
44493             },
44494
44495             "enter" : function(e){
44496                 this.collapse();
44497                 
44498                 if(this.fireEvent("specialkey", this, e)){
44499                     this.onViewClick(false);
44500                 }
44501                 
44502                 return true;
44503             },
44504
44505             "esc" : function(e){
44506                 this.collapse();
44507             },
44508
44509             "tab" : function(e){
44510                 this.collapse();
44511                 
44512                 if(this.fireEvent("specialkey", this, e)){
44513                     this.onViewClick(false);
44514                 }
44515                 
44516                 return true;
44517             },
44518
44519             scope : this,
44520
44521             doRelay : function(foo, bar, hname){
44522                 if(hname == 'down' || this.scope.isExpanded()){
44523                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44524                 }
44525                 return true;
44526             },
44527
44528             forceKeyDown: true
44529         });
44530         
44531         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44532         
44533     },
44534     
44535     initNumberEvent : function(e)
44536     {
44537         this.inputEl().on("keydown" , this.fireKey,  this);
44538         this.inputEl().on("focus", this.onFocus,  this);
44539         this.inputEl().on("blur", this.onBlur,  this);
44540         
44541         this.inputEl().relayEvent('keyup', this);
44542         
44543         if(this.indicator){
44544             this.indicator.addClass('invisible');
44545         }
44546  
44547         this.originalValue = this.getValue();
44548         
44549         if(this.validationEvent == 'keyup'){
44550             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44551             this.inputEl().on('keyup', this.filterValidation, this);
44552         }
44553         else if(this.validationEvent !== false){
44554             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44555         }
44556         
44557         if(this.selectOnFocus){
44558             this.on("focus", this.preFocus, this);
44559             
44560         }
44561         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44562             this.inputEl().on("keypress", this.filterKeys, this);
44563         } else {
44564             this.inputEl().relayEvent('keypress', this);
44565         }
44566         
44567         var allowed = "0123456789";
44568         
44569         if(this.allowDecimals){
44570             allowed += this.decimalSeparator;
44571         }
44572         
44573         if(this.allowNegative){
44574             allowed += "-";
44575         }
44576         
44577         if(this.thousandsDelimiter) {
44578             allowed += ",";
44579         }
44580         
44581         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44582         
44583         var keyPress = function(e){
44584             
44585             var k = e.getKey();
44586             
44587             var c = e.getCharCode();
44588             
44589             if(
44590                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44591                     allowed.indexOf(String.fromCharCode(c)) === -1
44592             ){
44593                 e.stopEvent();
44594                 return;
44595             }
44596             
44597             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44598                 return;
44599             }
44600             
44601             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44602                 e.stopEvent();
44603             }
44604         };
44605         
44606         this.inputEl().on("keypress", keyPress, this);
44607         
44608     },
44609     
44610     onTriggerClick : function(e)
44611     {   
44612         if(this.disabled){
44613             return;
44614         }
44615         
44616         this.page = 0;
44617         this.loadNext = false;
44618         
44619         if(this.isExpanded()){
44620             this.collapse();
44621             return;
44622         }
44623         
44624         this.hasFocus = true;
44625         
44626         if(this.triggerAction == 'all') {
44627             this.doQuery(this.allQuery, true);
44628             return;
44629         }
44630         
44631         this.doQuery(this.getRawValue());
44632     },
44633     
44634     getCurrency : function()
44635     {   
44636         var v = this.currencyEl().getValue();
44637         
44638         return v;
44639     },
44640     
44641     restrictHeight : function()
44642     {
44643         this.list.alignTo(this.currencyEl(), this.listAlign);
44644         this.list.alignTo(this.currencyEl(), this.listAlign);
44645     },
44646     
44647     onViewClick : function(view, doFocus, el, e)
44648     {
44649         var index = this.view.getSelectedIndexes()[0];
44650         
44651         var r = this.store.getAt(index);
44652         
44653         if(r){
44654             this.onSelect(r, index);
44655         }
44656     },
44657     
44658     onSelect : function(record, index){
44659         
44660         if(this.fireEvent('beforeselect', this, record, index) !== false){
44661         
44662             this.setFromCurrencyData(index > -1 ? record.data : false);
44663             
44664             this.collapse();
44665             
44666             this.fireEvent('select', this, record, index);
44667         }
44668     },
44669     
44670     setFromCurrencyData : function(o)
44671     {
44672         var currency = '';
44673         
44674         this.lastCurrency = o;
44675         
44676         if (this.currencyField) {
44677             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44678         } else {
44679             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44680         }
44681         
44682         this.lastSelectionText = currency;
44683         
44684         //setting default currency
44685         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44686             this.setCurrency(this.defaultCurrency);
44687             return;
44688         }
44689         
44690         this.setCurrency(currency);
44691     },
44692     
44693     setFromData : function(o)
44694     {
44695         var c = {};
44696         
44697         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44698         
44699         this.setFromCurrencyData(c);
44700         
44701         var value = '';
44702         
44703         if (this.name) {
44704             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44705         } else {
44706             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44707         }
44708         
44709         this.setValue(value);
44710         
44711     },
44712     
44713     setCurrency : function(v)
44714     {   
44715         this.currencyValue = v;
44716         
44717         if(this.rendered){
44718             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44719             this.validate();
44720         }
44721     },
44722     
44723     setValue : function(v)
44724     {
44725         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44726         
44727         this.value = v;
44728         
44729         if(this.rendered){
44730             
44731             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44732             
44733             this.inputEl().dom.value = (v == '') ? '' :
44734                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44735             
44736             if(!this.allowZero && v === '0') {
44737                 this.hiddenEl().dom.value = '';
44738                 this.inputEl().dom.value = '';
44739             }
44740             
44741             this.validate();
44742         }
44743     },
44744     
44745     getRawValue : function()
44746     {
44747         var v = this.inputEl().getValue();
44748         
44749         return v;
44750     },
44751     
44752     getValue : function()
44753     {
44754         return this.fixPrecision(this.parseValue(this.getRawValue()));
44755     },
44756     
44757     parseValue : function(value)
44758     {
44759         if(this.thousandsDelimiter) {
44760             value += "";
44761             r = new RegExp(",", "g");
44762             value = value.replace(r, "");
44763         }
44764         
44765         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44766         return isNaN(value) ? '' : value;
44767         
44768     },
44769     
44770     fixPrecision : function(value)
44771     {
44772         if(this.thousandsDelimiter) {
44773             value += "";
44774             r = new RegExp(",", "g");
44775             value = value.replace(r, "");
44776         }
44777         
44778         var nan = isNaN(value);
44779         
44780         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44781             return nan ? '' : value;
44782         }
44783         return parseFloat(value).toFixed(this.decimalPrecision);
44784     },
44785     
44786     decimalPrecisionFcn : function(v)
44787     {
44788         return Math.floor(v);
44789     },
44790     
44791     validateValue : function(value)
44792     {
44793         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44794             return false;
44795         }
44796         
44797         var num = this.parseValue(value);
44798         
44799         if(isNaN(num)){
44800             this.markInvalid(String.format(this.nanText, value));
44801             return false;
44802         }
44803         
44804         if(num < this.minValue){
44805             this.markInvalid(String.format(this.minText, this.minValue));
44806             return false;
44807         }
44808         
44809         if(num > this.maxValue){
44810             this.markInvalid(String.format(this.maxText, this.maxValue));
44811             return false;
44812         }
44813         
44814         return true;
44815     },
44816     
44817     validate : function()
44818     {
44819         if(this.disabled || this.allowBlank){
44820             this.markValid();
44821             return true;
44822         }
44823         
44824         var currency = this.getCurrency();
44825         
44826         if(this.validateValue(this.getRawValue()) && currency.length){
44827             this.markValid();
44828             return true;
44829         }
44830         
44831         this.markInvalid();
44832         return false;
44833     },
44834     
44835     getName: function()
44836     {
44837         return this.name;
44838     },
44839     
44840     beforeBlur : function()
44841     {
44842         if(!this.castInt){
44843             return;
44844         }
44845         
44846         var v = this.parseValue(this.getRawValue());
44847         
44848         if(v || v == 0){
44849             this.setValue(v);
44850         }
44851     },
44852     
44853     onBlur : function()
44854     {
44855         this.beforeBlur();
44856         
44857         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44858             //this.el.removeClass(this.focusClass);
44859         }
44860         
44861         this.hasFocus = false;
44862         
44863         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44864             this.validate();
44865         }
44866         
44867         var v = this.getValue();
44868         
44869         if(String(v) !== String(this.startValue)){
44870             this.fireEvent('change', this, v, this.startValue);
44871         }
44872         
44873         this.fireEvent("blur", this);
44874     },
44875     
44876     inputEl : function()
44877     {
44878         return this.el.select('.roo-money-amount-input', true).first();
44879     },
44880     
44881     currencyEl : function()
44882     {
44883         return this.el.select('.roo-money-currency-input', true).first();
44884     },
44885     
44886     hiddenEl : function()
44887     {
44888         return this.el.select('input.hidden-number-input',true).first();
44889     }
44890     
44891 });/**
44892  * @class Roo.bootstrap.BezierSignature
44893  * @extends Roo.bootstrap.Component
44894  * Bootstrap BezierSignature class
44895  * This script refer to:
44896  *    Title: Signature Pad
44897  *    Author: szimek
44898  *    Availability: https://github.com/szimek/signature_pad
44899  *
44900  * @constructor
44901  * Create a new BezierSignature
44902  * @param {Object} config The config object
44903  */
44904
44905 Roo.bootstrap.BezierSignature = function(config){
44906     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44907     this.addEvents({
44908         "resize" : true
44909     });
44910 };
44911
44912 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44913 {
44914      
44915     curve_data: [],
44916     
44917     is_empty: true,
44918     
44919     mouse_btn_down: true,
44920     
44921     /**
44922      * @cfg {int} canvas height
44923      */
44924     canvas_height: '200px',
44925     
44926     /**
44927      * @cfg {float|function} Radius of a single dot.
44928      */ 
44929     dot_size: false,
44930     
44931     /**
44932      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44933      */
44934     min_width: 0.5,
44935     
44936     /**
44937      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44938      */
44939     max_width: 2.5,
44940     
44941     /**
44942      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44943      */
44944     throttle: 16,
44945     
44946     /**
44947      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44948      */
44949     min_distance: 5,
44950     
44951     /**
44952      * @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.
44953      */
44954     bg_color: 'rgba(0, 0, 0, 0)',
44955     
44956     /**
44957      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44958      */
44959     dot_color: 'black',
44960     
44961     /**
44962      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44963      */ 
44964     velocity_filter_weight: 0.7,
44965     
44966     /**
44967      * @cfg {function} Callback when stroke begin. 
44968      */
44969     onBegin: false,
44970     
44971     /**
44972      * @cfg {function} Callback when stroke end.
44973      */
44974     onEnd: false,
44975     
44976     getAutoCreate : function()
44977     {
44978         var cls = 'roo-signature column';
44979         
44980         if(this.cls){
44981             cls += ' ' + this.cls;
44982         }
44983         
44984         var col_sizes = [
44985             'lg',
44986             'md',
44987             'sm',
44988             'xs'
44989         ];
44990         
44991         for(var i = 0; i < col_sizes.length; i++) {
44992             if(this[col_sizes[i]]) {
44993                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44994             }
44995         }
44996         
44997         var cfg = {
44998             tag: 'div',
44999             cls: cls,
45000             cn: [
45001                 {
45002                     tag: 'div',
45003                     cls: 'roo-signature-body',
45004                     cn: [
45005                         {
45006                             tag: 'canvas',
45007                             cls: 'roo-signature-body-canvas',
45008                             height: this.canvas_height,
45009                             width: this.canvas_width
45010                         }
45011                     ]
45012                 },
45013                 {
45014                     tag: 'input',
45015                     type: 'file',
45016                     style: 'display: none'
45017                 }
45018             ]
45019         };
45020         
45021         return cfg;
45022     },
45023     
45024     initEvents: function() 
45025     {
45026         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45027         
45028         var canvas = this.canvasEl();
45029         
45030         // mouse && touch event swapping...
45031         canvas.dom.style.touchAction = 'none';
45032         canvas.dom.style.msTouchAction = 'none';
45033         
45034         this.mouse_btn_down = false;
45035         canvas.on('mousedown', this._handleMouseDown, this);
45036         canvas.on('mousemove', this._handleMouseMove, this);
45037         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45038         
45039         if (window.PointerEvent) {
45040             canvas.on('pointerdown', this._handleMouseDown, this);
45041             canvas.on('pointermove', this._handleMouseMove, this);
45042             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45043         }
45044         
45045         if ('ontouchstart' in window) {
45046             canvas.on('touchstart', this._handleTouchStart, this);
45047             canvas.on('touchmove', this._handleTouchMove, this);
45048             canvas.on('touchend', this._handleTouchEnd, this);
45049         }
45050         
45051         Roo.EventManager.onWindowResize(this.resize, this, true);
45052         
45053         // file input event
45054         this.fileEl().on('change', this.uploadImage, this);
45055         
45056         this.clear();
45057         
45058         this.resize();
45059     },
45060     
45061     resize: function(){
45062         
45063         var canvas = this.canvasEl().dom;
45064         var ctx = this.canvasElCtx();
45065         var img_data = false;
45066         
45067         if(canvas.width > 0) {
45068             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45069         }
45070         // setting canvas width will clean img data
45071         canvas.width = 0;
45072         
45073         var style = window.getComputedStyle ? 
45074             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45075             
45076         var padding_left = parseInt(style.paddingLeft) || 0;
45077         var padding_right = parseInt(style.paddingRight) || 0;
45078         
45079         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45080         
45081         if(img_data) {
45082             ctx.putImageData(img_data, 0, 0);
45083         }
45084     },
45085     
45086     _handleMouseDown: function(e)
45087     {
45088         if (e.browserEvent.which === 1) {
45089             this.mouse_btn_down = true;
45090             this.strokeBegin(e);
45091         }
45092     },
45093     
45094     _handleMouseMove: function (e)
45095     {
45096         if (this.mouse_btn_down) {
45097             this.strokeMoveUpdate(e);
45098         }
45099     },
45100     
45101     _handleMouseUp: function (e)
45102     {
45103         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45104             this.mouse_btn_down = false;
45105             this.strokeEnd(e);
45106         }
45107     },
45108     
45109     _handleTouchStart: function (e) {
45110         
45111         e.preventDefault();
45112         if (e.browserEvent.targetTouches.length === 1) {
45113             // var touch = e.browserEvent.changedTouches[0];
45114             // this.strokeBegin(touch);
45115             
45116              this.strokeBegin(e); // assume e catching the correct xy...
45117         }
45118     },
45119     
45120     _handleTouchMove: function (e) {
45121         e.preventDefault();
45122         // var touch = event.targetTouches[0];
45123         // _this._strokeMoveUpdate(touch);
45124         this.strokeMoveUpdate(e);
45125     },
45126     
45127     _handleTouchEnd: function (e) {
45128         var wasCanvasTouched = e.target === this.canvasEl().dom;
45129         if (wasCanvasTouched) {
45130             e.preventDefault();
45131             // var touch = event.changedTouches[0];
45132             // _this._strokeEnd(touch);
45133             this.strokeEnd(e);
45134         }
45135     },
45136     
45137     reset: function () {
45138         this._lastPoints = [];
45139         this._lastVelocity = 0;
45140         this._lastWidth = (this.min_width + this.max_width) / 2;
45141         this.canvasElCtx().fillStyle = this.dot_color;
45142     },
45143     
45144     strokeMoveUpdate: function(e)
45145     {
45146         this.strokeUpdate(e);
45147         
45148         if (this.throttle) {
45149             this.throttleStroke(this.strokeUpdate, this.throttle);
45150         }
45151         else {
45152             this.strokeUpdate(e);
45153         }
45154     },
45155     
45156     strokeBegin: function(e)
45157     {
45158         var newPointGroup = {
45159             color: this.dot_color,
45160             points: []
45161         };
45162         
45163         if (typeof this.onBegin === 'function') {
45164             this.onBegin(e);
45165         }
45166         
45167         this.curve_data.push(newPointGroup);
45168         this.reset();
45169         this.strokeUpdate(e);
45170     },
45171     
45172     strokeUpdate: function(e)
45173     {
45174         var rect = this.canvasEl().dom.getBoundingClientRect();
45175         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45176         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45177         var lastPoints = lastPointGroup.points;
45178         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45179         var isLastPointTooClose = lastPoint
45180             ? point.distanceTo(lastPoint) <= this.min_distance
45181             : false;
45182         var color = lastPointGroup.color;
45183         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45184             var curve = this.addPoint(point);
45185             if (!lastPoint) {
45186                 this.drawDot({color: color, point: point});
45187             }
45188             else if (curve) {
45189                 this.drawCurve({color: color, curve: curve});
45190             }
45191             lastPoints.push({
45192                 time: point.time,
45193                 x: point.x,
45194                 y: point.y
45195             });
45196         }
45197     },
45198     
45199     strokeEnd: function(e)
45200     {
45201         this.strokeUpdate(e);
45202         if (typeof this.onEnd === 'function') {
45203             this.onEnd(e);
45204         }
45205     },
45206     
45207     addPoint:  function (point) {
45208         var _lastPoints = this._lastPoints;
45209         _lastPoints.push(point);
45210         if (_lastPoints.length > 2) {
45211             if (_lastPoints.length === 3) {
45212                 _lastPoints.unshift(_lastPoints[0]);
45213             }
45214             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45215             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45216             _lastPoints.shift();
45217             return curve;
45218         }
45219         return null;
45220     },
45221     
45222     calculateCurveWidths: function (startPoint, endPoint) {
45223         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45224             (1 - this.velocity_filter_weight) * this._lastVelocity;
45225
45226         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45227         var widths = {
45228             end: newWidth,
45229             start: this._lastWidth
45230         };
45231         
45232         this._lastVelocity = velocity;
45233         this._lastWidth = newWidth;
45234         return widths;
45235     },
45236     
45237     drawDot: function (_a) {
45238         var color = _a.color, point = _a.point;
45239         var ctx = this.canvasElCtx();
45240         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45241         ctx.beginPath();
45242         this.drawCurveSegment(point.x, point.y, width);
45243         ctx.closePath();
45244         ctx.fillStyle = color;
45245         ctx.fill();
45246     },
45247     
45248     drawCurve: function (_a) {
45249         var color = _a.color, curve = _a.curve;
45250         var ctx = this.canvasElCtx();
45251         var widthDelta = curve.endWidth - curve.startWidth;
45252         var drawSteps = Math.floor(curve.length()) * 2;
45253         ctx.beginPath();
45254         ctx.fillStyle = color;
45255         for (var i = 0; i < drawSteps; i += 1) {
45256         var t = i / drawSteps;
45257         var tt = t * t;
45258         var ttt = tt * t;
45259         var u = 1 - t;
45260         var uu = u * u;
45261         var uuu = uu * u;
45262         var x = uuu * curve.startPoint.x;
45263         x += 3 * uu * t * curve.control1.x;
45264         x += 3 * u * tt * curve.control2.x;
45265         x += ttt * curve.endPoint.x;
45266         var y = uuu * curve.startPoint.y;
45267         y += 3 * uu * t * curve.control1.y;
45268         y += 3 * u * tt * curve.control2.y;
45269         y += ttt * curve.endPoint.y;
45270         var width = curve.startWidth + ttt * widthDelta;
45271         this.drawCurveSegment(x, y, width);
45272         }
45273         ctx.closePath();
45274         ctx.fill();
45275     },
45276     
45277     drawCurveSegment: function (x, y, width) {
45278         var ctx = this.canvasElCtx();
45279         ctx.moveTo(x, y);
45280         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45281         this.is_empty = false;
45282     },
45283     
45284     clear: function()
45285     {
45286         var ctx = this.canvasElCtx();
45287         var canvas = this.canvasEl().dom;
45288         ctx.fillStyle = this.bg_color;
45289         ctx.clearRect(0, 0, canvas.width, canvas.height);
45290         ctx.fillRect(0, 0, canvas.width, canvas.height);
45291         this.curve_data = [];
45292         this.reset();
45293         this.is_empty = true;
45294     },
45295     
45296     fileEl: function()
45297     {
45298         return  this.el.select('input',true).first();
45299     },
45300     
45301     canvasEl: function()
45302     {
45303         return this.el.select('canvas',true).first();
45304     },
45305     
45306     canvasElCtx: function()
45307     {
45308         return this.el.select('canvas',true).first().dom.getContext('2d');
45309     },
45310     
45311     getImage: function(type)
45312     {
45313         if(this.is_empty) {
45314             return false;
45315         }
45316         
45317         // encryption ?
45318         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45319     },
45320     
45321     drawFromImage: function(img_src)
45322     {
45323         var img = new Image();
45324         
45325         img.onload = function(){
45326             this.canvasElCtx().drawImage(img, 0, 0);
45327         }.bind(this);
45328         
45329         img.src = img_src;
45330         
45331         this.is_empty = false;
45332     },
45333     
45334     selectImage: function()
45335     {
45336         this.fileEl().dom.click();
45337     },
45338     
45339     uploadImage: function(e)
45340     {
45341         var reader = new FileReader();
45342         
45343         reader.onload = function(e){
45344             var img = new Image();
45345             img.onload = function(){
45346                 this.reset();
45347                 this.canvasElCtx().drawImage(img, 0, 0);
45348             }.bind(this);
45349             img.src = e.target.result;
45350         }.bind(this);
45351         
45352         reader.readAsDataURL(e.target.files[0]);
45353     },
45354     
45355     // Bezier Point Constructor
45356     Point: (function () {
45357         function Point(x, y, time) {
45358             this.x = x;
45359             this.y = y;
45360             this.time = time || Date.now();
45361         }
45362         Point.prototype.distanceTo = function (start) {
45363             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45364         };
45365         Point.prototype.equals = function (other) {
45366             return this.x === other.x && this.y === other.y && this.time === other.time;
45367         };
45368         Point.prototype.velocityFrom = function (start) {
45369             return this.time !== start.time
45370             ? this.distanceTo(start) / (this.time - start.time)
45371             : 0;
45372         };
45373         return Point;
45374     }()),
45375     
45376     
45377     // Bezier Constructor
45378     Bezier: (function () {
45379         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45380             this.startPoint = startPoint;
45381             this.control2 = control2;
45382             this.control1 = control1;
45383             this.endPoint = endPoint;
45384             this.startWidth = startWidth;
45385             this.endWidth = endWidth;
45386         }
45387         Bezier.fromPoints = function (points, widths, scope) {
45388             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45389             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45390             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45391         };
45392         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45393             var dx1 = s1.x - s2.x;
45394             var dy1 = s1.y - s2.y;
45395             var dx2 = s2.x - s3.x;
45396             var dy2 = s2.y - s3.y;
45397             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45398             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45399             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45400             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45401             var dxm = m1.x - m2.x;
45402             var dym = m1.y - m2.y;
45403             var k = l2 / (l1 + l2);
45404             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45405             var tx = s2.x - cm.x;
45406             var ty = s2.y - cm.y;
45407             return {
45408                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45409                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45410             };
45411         };
45412         Bezier.prototype.length = function () {
45413             var steps = 10;
45414             var length = 0;
45415             var px;
45416             var py;
45417             for (var i = 0; i <= steps; i += 1) {
45418                 var t = i / steps;
45419                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45420                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45421                 if (i > 0) {
45422                     var xdiff = cx - px;
45423                     var ydiff = cy - py;
45424                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45425                 }
45426                 px = cx;
45427                 py = cy;
45428             }
45429             return length;
45430         };
45431         Bezier.prototype.point = function (t, start, c1, c2, end) {
45432             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45433             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45434             + (3.0 * c2 * (1.0 - t) * t * t)
45435             + (end * t * t * t);
45436         };
45437         return Bezier;
45438     }()),
45439     
45440     throttleStroke: function(fn, wait) {
45441       if (wait === void 0) { wait = 250; }
45442       var previous = 0;
45443       var timeout = null;
45444       var result;
45445       var storedContext;
45446       var storedArgs;
45447       var later = function () {
45448           previous = Date.now();
45449           timeout = null;
45450           result = fn.apply(storedContext, storedArgs);
45451           if (!timeout) {
45452               storedContext = null;
45453               storedArgs = [];
45454           }
45455       };
45456       return function wrapper() {
45457           var args = [];
45458           for (var _i = 0; _i < arguments.length; _i++) {
45459               args[_i] = arguments[_i];
45460           }
45461           var now = Date.now();
45462           var remaining = wait - (now - previous);
45463           storedContext = this;
45464           storedArgs = args;
45465           if (remaining <= 0 || remaining > wait) {
45466               if (timeout) {
45467                   clearTimeout(timeout);
45468                   timeout = null;
45469               }
45470               previous = now;
45471               result = fn.apply(storedContext, storedArgs);
45472               if (!timeout) {
45473                   storedContext = null;
45474                   storedArgs = [];
45475               }
45476           }
45477           else if (!timeout) {
45478               timeout = window.setTimeout(later, remaining);
45479           }
45480           return result;
45481       };
45482   }
45483   
45484 });
45485
45486  
45487
45488