Roo/data/DataProxy.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * Bootstrap Component base class
227  * @cfg {String} cls css class
228  * @cfg {String} style any extra css
229  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
230  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
231  * @cfg {string} dataId cutomer id
232  * @cfg {string} name Specifies name attribute
233  * @cfg {string} tooltip  Text for the tooltip
234  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
235  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
236  
237  * @constructor
238  * Do not use directly - it does not do anything..
239  * @param {Object} config The config object
240  */
241
242
243
244 Roo.bootstrap.Component = function(config){
245     Roo.bootstrap.Component.superclass.constructor.call(this, config);
246        
247     this.addEvents({
248         /**
249          * @event childrenrendered
250          * Fires when the children have been rendered..
251          * @param {Roo.bootstrap.Component} this
252          */
253         "childrenrendered" : true
254         
255         
256         
257     });
258     
259     
260 };
261
262 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
263     
264     
265     allowDomMove : false, // to stop relocations in parent onRender...
266     
267     cls : false,
268     
269     style : false,
270     
271     autoCreate : false,
272     
273     tooltip : null,
274     /**
275      * Initialize Events for the element
276      */
277     initEvents : function() { },
278     
279     xattr : false,
280     
281     parentId : false,
282     
283     can_build_overlaid : true,
284     
285     container_method : false,
286     
287     dataId : false,
288     
289     name : false,
290     
291     parent: function() {
292         // returns the parent component..
293         return Roo.ComponentMgr.get(this.parentId)
294         
295         
296     },
297     
298     // private
299     onRender : function(ct, position)
300     {
301        // Roo.log("Call onRender: " + this.xtype);
302         
303         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
304         
305         if(this.el){
306             if (this.el.attr('xtype')) {
307                 this.el.attr('xtypex', this.el.attr('xtype'));
308                 this.el.dom.removeAttribute('xtype');
309                 
310                 this.initEvents();
311             }
312             
313             return;
314         }
315         
316          
317         
318         var cfg = Roo.apply({},  this.getAutoCreate());
319         
320         cfg.id = this.id || Roo.id();
321         
322         // fill in the extra attributes 
323         if (this.xattr && typeof(this.xattr) =='object') {
324             for (var i in this.xattr) {
325                 cfg[i] = this.xattr[i];
326             }
327         }
328         
329         if(this.dataId){
330             cfg.dataId = this.dataId;
331         }
332         
333         if (this.cls) {
334             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
335         }
336         
337         if (this.style) { // fixme needs to support more complex style data.
338             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
339         }
340         
341         if(this.name){
342             cfg.name = this.name;
343         }
344         
345         this.el = ct.createChild(cfg, position);
346         
347         if (this.tooltip) {
348             this.tooltipEl().attr('tooltip', this.tooltip);
349         }
350         
351         if(this.tabIndex !== undefined){
352             this.el.dom.setAttribute('tabIndex', this.tabIndex);
353         }
354         
355         this.initEvents();
356         
357     },
358     /**
359      * Fetch the element to add children to
360      * @return {Roo.Element} defaults to this.el
361      */
362     getChildContainer : function()
363     {
364         return this.el;
365     },
366     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367     {
368         return Roo.get(document.body);
369     },
370     
371     /**
372      * Fetch the element to display the tooltip on.
373      * @return {Roo.Element} defaults to this.el
374      */
375     tooltipEl : function()
376     {
377         return this.el;
378     },
379         
380     addxtype  : function(tree,cntr)
381     {
382         var cn = this;
383         
384         cn = Roo.factory(tree);
385         //Roo.log(['addxtype', cn]);
386            
387         cn.parentType = this.xtype; //??
388         cn.parentId = this.id;
389         
390         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
391         if (typeof(cn.container_method) == 'string') {
392             cntr = cn.container_method;
393         }
394         
395         
396         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
397         
398         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
399         
400         var build_from_html =  Roo.XComponent.build_from_html;
401           
402         var is_body  = (tree.xtype == 'Body') ;
403           
404         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405           
406         var self_cntr_el = Roo.get(this[cntr](false));
407         
408         // do not try and build conditional elements 
409         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
410             return false;
411         }
412         
413         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
414             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
415                 return this.addxtypeChild(tree,cntr, is_body);
416             }
417             
418             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
419                 
420             if(echild){
421                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
422             }
423             
424             Roo.log('skipping render');
425             return cn;
426             
427         }
428         
429         var ret = false;
430         if (!build_from_html) {
431             return false;
432         }
433         
434         // this i think handles overlaying multiple children of the same type
435         // with the sam eelement.. - which might be buggy..
436         while (true) {
437             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
438             
439             if (!echild) {
440                 break;
441             }
442             
443             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
444                 break;
445             }
446             
447             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448         }
449        
450         return ret;
451     },
452     
453     
454     addxtypeChild : function (tree, cntr, is_body)
455     {
456         Roo.debug && Roo.log('addxtypeChild:' + cntr);
457         var cn = this;
458         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
459         
460         
461         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
462                     (typeof(tree['flexy:foreach']) != 'undefined');
463           
464     
465         
466         skip_children = false;
467         // render the element if it's not BODY.
468         if (!is_body) {
469             
470             // if parent was disabled, then do not try and create the children..
471             if(!this[cntr](true)){
472                 tree.items = [];
473                 return tree;
474             }
475            
476             cn = Roo.factory(tree);
477            
478             cn.parentType = this.xtype; //??
479             cn.parentId = this.id;
480             
481             var build_from_html =  Roo.XComponent.build_from_html;
482             
483             
484             // does the container contain child eleemnts with 'xtype' attributes.
485             // that match this xtype..
486             // note - when we render we create these as well..
487             // so we should check to see if body has xtype set.
488             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489                
490                 var self_cntr_el = Roo.get(this[cntr](false));
491                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492                 if (echild) { 
493                     //Roo.log(Roo.XComponent.build_from_html);
494                     //Roo.log("got echild:");
495                     //Roo.log(echild);
496                 }
497                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
498                 // and are not displayed -this causes this to use up the wrong element when matching.
499                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
500                 
501                 
502                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
503                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
504                   
505                   
506                   
507                     cn.el = echild;
508                   //  Roo.log("GOT");
509                     //echild.dom.removeAttribute('xtype');
510                 } else {
511                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
512                     Roo.debug && Roo.log(self_cntr_el);
513                     Roo.debug && Roo.log(echild);
514                     Roo.debug && Roo.log(cn);
515                 }
516             }
517            
518             
519            
520             // if object has flexy:if - then it may or may not be rendered.
521             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
522                 // skip a flexy if element.
523                 Roo.debug && Roo.log('skipping render');
524                 Roo.debug && Roo.log(tree);
525                 if (!cn.el) {
526                     Roo.debug && Roo.log('skipping all children');
527                     skip_children = true;
528                 }
529                 
530              } else {
531                  
532                 // actually if flexy:foreach is found, we really want to create 
533                 // multiple copies here...
534                 //Roo.log('render');
535                 //Roo.log(this[cntr]());
536                 // some elements do not have render methods.. like the layouts...
537                 /*
538                 if(this[cntr](true) === false){
539                     cn.items = [];
540                     return cn;
541                 }
542                 */
543                 cn.render && cn.render(this[cntr](true));
544                 
545              }
546             // then add the element..
547         }
548          
549         // handle the kids..
550         
551         var nitems = [];
552         /*
553         if (typeof (tree.menu) != 'undefined') {
554             tree.menu.parentType = cn.xtype;
555             tree.menu.triggerEl = cn.el;
556             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
557             
558         }
559         */
560         if (!tree.items || !tree.items.length) {
561             cn.items = nitems;
562             //Roo.log(["no children", this]);
563             
564             return cn;
565         }
566          
567         var items = tree.items;
568         delete tree.items;
569         
570         //Roo.log(items.length);
571             // add the items..
572         if (!skip_children) {    
573             for(var i =0;i < items.length;i++) {
574               //  Roo.log(['add child', items[i]]);
575                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
576             }
577         }
578         
579         cn.items = nitems;
580         
581         //Roo.log("fire childrenrendered");
582         
583         cn.fireEvent('childrenrendered', this);
584         
585         return cn;
586     },
587     
588     /**
589      * Set the element that will be used to show or hide
590      */
591     setVisibilityEl : function(el)
592     {
593         this.visibilityEl = el;
594     },
595     
596      /**
597      * Get the element that will be used to show or hide
598      */
599     getVisibilityEl : function()
600     {
601         if (typeof(this.visibilityEl) == 'object') {
602             return this.visibilityEl;
603         }
604         
605         if (typeof(this.visibilityEl) == 'string') {
606             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607         }
608         
609         return this.getEl();
610     },
611     
612     /**
613      * Show a component - removes 'hidden' class
614      */
615     show : function()
616     {
617         if(!this.getVisibilityEl()){
618             return;
619         }
620          
621         this.getVisibilityEl().removeClass(['hidden','d-none']);
622         
623         this.fireEvent('show', this);
624         
625         
626     },
627     /**
628      * Hide a component - adds 'hidden' class
629      */
630     hide: function()
631     {
632         if(!this.getVisibilityEl()){
633             return;
634         }
635         
636         this.getVisibilityEl().addClass(['hidden','d-none']);
637         
638         this.fireEvent('hide', this);
639         
640     }
641 });
642
643  /*
644  * - LGPL
645  *
646  * element
647  * 
648  */
649
650 /**
651  * @class Roo.bootstrap.Element
652  * @extends Roo.bootstrap.Component
653  * Bootstrap Element class
654  * @cfg {String} html contents of the element
655  * @cfg {String} tag tag of the element
656  * @cfg {String} cls class of the element
657  * @cfg {Boolean} preventDefault (true|false) default false
658  * @cfg {Boolean} clickable (true|false) default false
659  * @cfg {String} role default blank - set to button to force cursor pointer
660  
661  * 
662  * @constructor
663  * Create a new Element
664  * @param {Object} config The config object
665  */
666
667 Roo.bootstrap.Element = function(config){
668     Roo.bootstrap.Element.superclass.constructor.call(this, config);
669     
670     this.addEvents({
671         // raw events
672         /**
673          * @event click
674          * When a element is chick
675          * @param {Roo.bootstrap.Element} this
676          * @param {Roo.EventObject} e
677          */
678         "click" : true 
679         
680       
681     });
682 };
683
684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
685     
686     tag: 'div',
687     cls: '',
688     html: '',
689     preventDefault: false, 
690     clickable: false,
691     tapedTwice : false,
692     role : false,
693     
694     getAutoCreate : function(){
695         
696         var cfg = {
697             tag: this.tag,
698             // cls: this.cls, double assign in parent class Component.js :: onRender
699             html: this.html
700         };
701         if (this.role !== false) {
702             cfg.role = this.role;
703         }
704         
705         return cfg;
706     },
707     
708     initEvents: function() 
709     {
710         Roo.bootstrap.Element.superclass.initEvents.call(this);
711         
712         if(this.clickable){
713             this.el.on('click', this.onClick, this);
714         }
715         
716         
717     },
718     
719     onClick : function(e)
720     {
721         if(this.preventDefault){
722             e.preventDefault();
723         }
724         
725         this.fireEvent('click', this, e); // why was this double click before?
726     },
727     
728     
729     
730
731     
732     
733     getValue : function()
734     {
735         return this.el.dom.innerHTML;
736     },
737     
738     setValue : function(value)
739     {
740         this.el.dom.innerHTML = value;
741     }
742    
743 });
744
745  
746
747  /*
748  * - LGPL
749  *
750  * dropable area
751  * 
752  */
753
754 /**
755  * @class Roo.bootstrap.DropTarget
756  * @extends Roo.bootstrap.Element
757  * Bootstrap DropTarget class
758  
759  * @cfg {string} name dropable name
760  * 
761  * @constructor
762  * Create a new Dropable Area
763  * @param {Object} config The config object
764  */
765
766 Roo.bootstrap.DropTarget = function(config){
767     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
768     
769     this.addEvents({
770         // raw events
771         /**
772          * @event click
773          * When a element is chick
774          * @param {Roo.bootstrap.Element} this
775          * @param {Roo.EventObject} e
776          */
777         "drop" : true
778     });
779 };
780
781 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
782     
783     
784     getAutoCreate : function(){
785         
786          
787     },
788     
789     initEvents: function() 
790     {
791         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
792         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
793             ddGroup: this.name,
794             listeners : {
795                 drop : this.dragDrop.createDelegate(this),
796                 enter : this.dragEnter.createDelegate(this),
797                 out : this.dragOut.createDelegate(this),
798                 over : this.dragOver.createDelegate(this)
799             }
800             
801         });
802         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
803     },
804     
805     dragDrop : function(source,e,data)
806     {
807         // user has to decide how to impliment this.
808         Roo.log('drop');
809         Roo.log(this);
810         //this.fireEvent('drop', this, source, e ,data);
811         return false;
812     },
813     
814     dragEnter : function(n, dd, e, data)
815     {
816         // probably want to resize the element to match the dropped element..
817         Roo.log("enter");
818         this.originalSize = this.el.getSize();
819         this.el.setSize( n.el.getSize());
820         this.dropZone.DDM.refreshCache(this.name);
821         Roo.log([n, dd, e, data]);
822     },
823     
824     dragOut : function(value)
825     {
826         // resize back to normal
827         Roo.log("out");
828         this.el.setSize(this.originalSize);
829         this.dropZone.resetConstraints();
830     },
831     
832     dragOver : function()
833     {
834         // ??? do nothing?
835     }
836    
837 });
838
839  
840
841  /*
842  * - LGPL
843  *
844  * Body
845  *
846  */
847
848 /**
849  * @class Roo.bootstrap.Body
850  * @extends Roo.bootstrap.Component
851  * Bootstrap Body class
852  *
853  * @constructor
854  * Create a new body
855  * @param {Object} config The config object
856  */
857
858 Roo.bootstrap.Body = function(config){
859
860     config = config || {};
861
862     Roo.bootstrap.Body.superclass.constructor.call(this, config);
863     this.el = Roo.get(config.el ? config.el : document.body );
864     if (this.cls && this.cls.length) {
865         Roo.get(document.body).addClass(this.cls);
866     }
867 };
868
869 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
870
871     is_body : true,// just to make sure it's constructed?
872
873         autoCreate : {
874         cls: 'container'
875     },
876     onRender : function(ct, position)
877     {
878        /* Roo.log("Roo.bootstrap.Body - onRender");
879         if (this.cls && this.cls.length) {
880             Roo.get(document.body).addClass(this.cls);
881         }
882         // style??? xttr???
883         */
884     }
885
886
887
888
889 });
890 /*
891  * - LGPL
892  *
893  * button group
894  * 
895  */
896
897
898 /**
899  * @class Roo.bootstrap.ButtonGroup
900  * @extends Roo.bootstrap.Component
901  * Bootstrap ButtonGroup class
902  * @cfg {String} size lg | sm | xs (default empty normal)
903  * @cfg {String} align vertical | justified  (default none)
904  * @cfg {String} direction up | down (default down)
905  * @cfg {Boolean} toolbar false | true
906  * @cfg {Boolean} btn true | false
907  * 
908  * 
909  * @constructor
910  * Create a new Input
911  * @param {Object} config The config object
912  */
913
914 Roo.bootstrap.ButtonGroup = function(config){
915     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
916 };
917
918 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
919     
920     size: '',
921     align: '',
922     direction: '',
923     toolbar: false,
924     btn: true,
925
926     getAutoCreate : function(){
927         var cfg = {
928             cls: 'btn-group',
929             html : null
930         };
931         
932         cfg.html = this.html || cfg.html;
933         
934         if (this.toolbar) {
935             cfg = {
936                 cls: 'btn-toolbar',
937                 html: null
938             };
939             
940             return cfg;
941         }
942         
943         if (['vertical','justified'].indexOf(this.align)!==-1) {
944             cfg.cls = 'btn-group-' + this.align;
945             
946             if (this.align == 'justified') {
947                 console.log(this.items);
948             }
949         }
950         
951         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
952             cfg.cls += ' btn-group-' + this.size;
953         }
954         
955         if (this.direction == 'up') {
956             cfg.cls += ' dropup' ;
957         }
958         
959         return cfg;
960     },
961     /**
962      * Add a button to the group (similar to NavItem API.)
963      */
964     addItem : function(cfg)
965     {
966         var cn = new Roo.bootstrap.Button(cfg);
967         //this.register(cn);
968         cn.parentId = this.id;
969         cn.onRender(this.el, null);
970         return cn;
971     }
972    
973 });
974
975  /*
976  * - LGPL
977  *
978  * button
979  * 
980  */
981
982 /**
983  * @class Roo.bootstrap.Button
984  * @extends Roo.bootstrap.Component
985  * Bootstrap Button class
986  * @cfg {String} html The button content
987  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
988  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
989  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
990  * @cfg {String} size (lg|sm|xs)
991  * @cfg {String} tag (a|input|submit)
992  * @cfg {String} href empty or href
993  * @cfg {Boolean} disabled default false;
994  * @cfg {Boolean} isClose default false;
995  * @cfg {String} glyphicon depricated - use fa
996  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
997  * @cfg {String} badge text for badge
998  * @cfg {String} theme (default|glow)  
999  * @cfg {Boolean} inverse dark themed version
1000  * @cfg {Boolean} toggle is it a slidy toggle button
1001  * @cfg {Boolean} pressed   default null - if the button ahs active state
1002  * @cfg {String} ontext text for on slidy toggle state
1003  * @cfg {String} offtext text for off slidy toggle state
1004  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1005  * @cfg {Boolean} removeClass remove the standard class..
1006  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1007  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1008  * 
1009  * @constructor
1010  * Create a new button
1011  * @param {Object} config The config object
1012  */
1013
1014
1015 Roo.bootstrap.Button = function(config){
1016     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020         /**
1021          * @event click
1022          * When a button is pressed
1023          * @param {Roo.bootstrap.Button} btn
1024          * @param {Roo.EventObject} e
1025          */
1026         "click" : true,
1027         /**
1028          * @event dblclick
1029          * When a button is double clicked
1030          * @param {Roo.bootstrap.Button} btn
1031          * @param {Roo.EventObject} e
1032          */
1033         "dblclick" : true,
1034          /**
1035          * @event toggle
1036          * After the button has been toggles
1037          * @param {Roo.bootstrap.Button} btn
1038          * @param {Roo.EventObject} e
1039          * @param {boolean} pressed (also available as button.pressed)
1040          */
1041         "toggle" : true
1042     });
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1046     html: false,
1047     active: false,
1048     weight: '',
1049     badge_weight: '',
1050     outline : false,
1051     size: '',
1052     tag: 'button',
1053     href: '',
1054     disabled: false,
1055     isClose: false,
1056     glyphicon: '',
1057     fa: '',
1058     badge: '',
1059     theme: 'default',
1060     inverse: false,
1061     
1062     toggle: false,
1063     ontext: 'ON',
1064     offtext: 'OFF',
1065     defaulton: true,
1066     preventDefault: true,
1067     removeClass: false,
1068     name: false,
1069     target: false,
1070     group : false,
1071      
1072     pressed : null,
1073      
1074     
1075     getAutoCreate : function(){
1076         
1077         var cfg = {
1078             tag : 'button',
1079             cls : 'roo-button',
1080             html: ''
1081         };
1082         
1083         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1084             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1085             this.tag = 'button';
1086         } else {
1087             cfg.tag = this.tag;
1088         }
1089         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090         
1091         if (this.toggle == true) {
1092             cfg={
1093                 tag: 'div',
1094                 cls: 'slider-frame roo-button',
1095                 cn: [
1096                     {
1097                         tag: 'span',
1098                         'data-on-text':'ON',
1099                         'data-off-text':'OFF',
1100                         cls: 'slider-button',
1101                         html: this.offtext
1102                     }
1103                 ]
1104             };
1105             // why are we validating the weights?
1106             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1107                 cfg.cls +=  ' ' + this.weight;
1108             }
1109             
1110             return cfg;
1111         }
1112         
1113         if (this.isClose) {
1114             cfg.cls += ' close';
1115             
1116             cfg["aria-hidden"] = true;
1117             
1118             cfg.html = "&times;";
1119             
1120             return cfg;
1121         }
1122              
1123         
1124         if (this.theme==='default') {
1125             cfg.cls = 'btn roo-button';
1126             
1127             //if (this.parentType != 'Navbar') {
1128             this.weight = this.weight.length ?  this.weight : 'default';
1129             //}
1130             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131                 
1132                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1133                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1134                 cfg.cls += ' btn-' + outline + weight;
1135                 if (this.weight == 'default') {
1136                     // BC
1137                     cfg.cls += ' btn-' + this.weight;
1138                 }
1139             }
1140         } else if (this.theme==='glow') {
1141             
1142             cfg.tag = 'a';
1143             cfg.cls = 'btn-glow roo-button';
1144             
1145             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146                 
1147                 cfg.cls += ' ' + this.weight;
1148             }
1149         }
1150    
1151         
1152         if (this.inverse) {
1153             this.cls += ' inverse';
1154         }
1155         
1156         
1157         if (this.active || this.pressed === true) {
1158             cfg.cls += ' active';
1159         }
1160         
1161         if (this.disabled) {
1162             cfg.disabled = 'disabled';
1163         }
1164         
1165         if (this.items) {
1166             Roo.log('changing to ul' );
1167             cfg.tag = 'ul';
1168             this.glyphicon = 'caret';
1169             if (Roo.bootstrap.version == 4) {
1170                 this.fa = 'caret-down';
1171             }
1172             
1173         }
1174         
1175         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176          
1177         //gsRoo.log(this.parentType);
1178         if (this.parentType === 'Navbar' && !this.parent().bar) {
1179             Roo.log('changing to li?');
1180             
1181             cfg.tag = 'li';
1182             
1183             cfg.cls = '';
1184             cfg.cn =  [{
1185                 tag : 'a',
1186                 cls : 'roo-button',
1187                 html : this.html,
1188                 href : this.href || '#'
1189             }];
1190             if (this.menu) {
1191                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1192                 cfg.cls += ' dropdown';
1193             }   
1194             
1195             delete cfg.html;
1196             
1197         }
1198         
1199        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1200         
1201         if (this.glyphicon) {
1202             cfg.html = ' ' + cfg.html;
1203             
1204             cfg.cn = [
1205                 {
1206                     tag: 'span',
1207                     cls: 'glyphicon glyphicon-' + this.glyphicon
1208                 }
1209             ];
1210         }
1211         if (this.fa) {
1212             cfg.html = ' ' + cfg.html;
1213             
1214             cfg.cn = [
1215                 {
1216                     tag: 'i',
1217                     cls: 'fa fas fa-' + this.fa
1218                 }
1219             ];
1220         }
1221         
1222         if (this.badge) {
1223             cfg.html += ' ';
1224             
1225             cfg.tag = 'a';
1226             
1227 //            cfg.cls='btn roo-button';
1228             
1229             cfg.href=this.href;
1230             
1231             var value = cfg.html;
1232             
1233             if(this.glyphicon){
1234                 value = {
1235                     tag: 'span',
1236                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1237                     html: this.html
1238                 };
1239             }
1240             if(this.fa){
1241                 value = {
1242                     tag: 'i',
1243                     cls: 'fa fas fa-' + this.fa,
1244                     html: this.html
1245                 };
1246             }
1247             
1248             var bw = this.badge_weight.length ? this.badge_weight :
1249                 (this.weight.length ? this.weight : 'secondary');
1250             bw = bw == 'default' ? 'secondary' : bw;
1251             
1252             cfg.cn = [
1253                 value,
1254                 {
1255                     tag: 'span',
1256                     cls: 'badge badge-' + bw,
1257                     html: this.badge
1258                 }
1259             ];
1260             
1261             cfg.html='';
1262         }
1263         
1264         if (this.menu) {
1265             cfg.cls += ' dropdown';
1266             cfg.html = typeof(cfg.html) != 'undefined' ?
1267                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1268         }
1269         
1270         if (cfg.tag !== 'a' && this.href !== '') {
1271             throw "Tag must be a to set href.";
1272         } else if (this.href.length > 0) {
1273             cfg.href = this.href;
1274         }
1275         
1276         if(this.removeClass){
1277             cfg.cls = '';
1278         }
1279         
1280         if(this.target){
1281             cfg.target = this.target;
1282         }
1283         
1284         return cfg;
1285     },
1286     initEvents: function() {
1287        // Roo.log('init events?');
1288 //        Roo.log(this.el.dom);
1289         // add the menu...
1290         
1291         if (typeof (this.menu) != 'undefined') {
1292             this.menu.parentType = this.xtype;
1293             this.menu.triggerEl = this.el;
1294             this.addxtype(Roo.apply({}, this.menu));
1295         }
1296
1297
1298         if (this.el.hasClass('roo-button')) {
1299              this.el.on('click', this.onClick, this);
1300              this.el.on('dblclick', this.onDblClick, this);
1301         } else {
1302              this.el.select('.roo-button').on('click', this.onClick, this);
1303              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1304              
1305         }
1306         // why?
1307         if(this.removeClass){
1308             this.el.on('click', this.onClick, this);
1309         }
1310         
1311         if (this.group === true) {
1312              if (this.pressed === false || this.pressed === true) {
1313                 // nothing
1314             } else {
1315                 this.pressed = false;
1316                 this.setActive(this.pressed);
1317             }
1318             
1319         }
1320         
1321         this.el.enableDisplayMode();
1322         
1323     },
1324     onClick : function(e)
1325     {
1326         if (this.disabled) {
1327             return;
1328         }
1329         
1330         Roo.log('button on click ');
1331         if(this.preventDefault){
1332             e.preventDefault();
1333         }
1334         
1335         if (this.group) {
1336             if (this.pressed) {
1337                 // do nothing -
1338                 return;
1339             }
1340             this.setActive(true);
1341             var pi = this.parent().items;
1342             for (var i = 0;i < pi.length;i++) {
1343                 if (this == pi[i]) {
1344                     continue;
1345                 }
1346                 if (pi[i].el.hasClass('roo-button')) {
1347                     pi[i].setActive(false);
1348                 }
1349             }
1350             this.fireEvent('click', this, e);            
1351             return;
1352         }
1353         
1354         if (this.pressed === true || this.pressed === false) {
1355             this.toggleActive(e);
1356         }
1357         
1358         
1359         this.fireEvent('click', this, e);
1360     },
1361     onDblClick: function(e)
1362     {
1363         if (this.disabled) {
1364             return;
1365         }
1366         if(this.preventDefault){
1367             e.preventDefault();
1368         }
1369         this.fireEvent('dblclick', this, e);
1370     },
1371     /**
1372      * Enables this button
1373      */
1374     enable : function()
1375     {
1376         this.disabled = false;
1377         this.el.removeClass('disabled');
1378         this.el.dom.removeAttribute("disabled");
1379     },
1380     
1381     /**
1382      * Disable this button
1383      */
1384     disable : function()
1385     {
1386         this.disabled = true;
1387         this.el.addClass('disabled');
1388         this.el.attr("disabled", "disabled")
1389     },
1390      /**
1391      * sets the active state on/off, 
1392      * @param {Boolean} state (optional) Force a particular state
1393      */
1394     setActive : function(v) {
1395         
1396         this.el[v ? 'addClass' : 'removeClass']('active');
1397         this.pressed = v;
1398     },
1399      /**
1400      * toggles the current active state 
1401      */
1402     toggleActive : function(e)
1403     {
1404         this.setActive(!this.pressed); // this modifies pressed...
1405         this.fireEvent('toggle', this, e, this.pressed);
1406     },
1407      /**
1408      * get the current active state
1409      * @return {boolean} true if it's active
1410      */
1411     isActive : function()
1412     {
1413         return this.el.hasClass('active');
1414     },
1415     /**
1416      * set the text of the first selected button
1417      */
1418     setText : function(str)
1419     {
1420         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1421     },
1422     /**
1423      * get the text of the first selected button
1424      */
1425     getText : function()
1426     {
1427         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1428     },
1429     
1430     setWeight : function(str)
1431     {
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1433         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434         this.weight = str;
1435         var outline = this.outline ? 'outline-' : '';
1436         if (str == 'default') {
1437             this.el.addClass('btn-default btn-outline-secondary');        
1438             return;
1439         }
1440         this.el.addClass('btn-' + outline + str);        
1441     }
1442     
1443     
1444 });
1445 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446
1447 Roo.bootstrap.Button.weights = [
1448     'default',
1449     'secondary' ,
1450     'primary',
1451     'success',
1452     'info',
1453     'warning',
1454     'danger',
1455     'link',
1456     'light',
1457     'dark'              
1458    
1459 ];/*
1460  * - LGPL
1461  *
1462  * column
1463  * 
1464  */
1465
1466 /**
1467  * @class Roo.bootstrap.Column
1468  * @extends Roo.bootstrap.Component
1469  * Bootstrap Column class
1470  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1471  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1472  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1473  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1474  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1475  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1476  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1477  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1478  *
1479  * 
1480  * @cfg {Boolean} hidden (true|false) hide the element
1481  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1482  * @cfg {String} fa (ban|check|...) font awesome icon
1483  * @cfg {Number} fasize (1|2|....) font awsome size
1484
1485  * @cfg {String} icon (info-sign|check|...) glyphicon name
1486
1487  * @cfg {String} html content of column.
1488  * 
1489  * @constructor
1490  * Create a new Column
1491  * @param {Object} config The config object
1492  */
1493
1494 Roo.bootstrap.Column = function(config){
1495     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1496 };
1497
1498 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1499     
1500     xs: false,
1501     sm: false,
1502     md: false,
1503     lg: false,
1504     xsoff: false,
1505     smoff: false,
1506     mdoff: false,
1507     lgoff: false,
1508     html: '',
1509     offset: 0,
1510     alert: false,
1511     fa: false,
1512     icon : false,
1513     hidden : false,
1514     fasize : 1,
1515     
1516     getAutoCreate : function(){
1517         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1518         
1519         cfg = {
1520             tag: 'div',
1521             cls: 'column'
1522         };
1523         
1524         var settings=this;
1525         var sizes =   ['xs','sm','md','lg'];
1526         sizes.map(function(size ,ix){
1527             //Roo.log( size + ':' + settings[size]);
1528             
1529             if (settings[size+'off'] !== false) {
1530                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1531             }
1532             
1533             if (settings[size] === false) {
1534                 return;
1535             }
1536             
1537             if (!settings[size]) { // 0 = hidden
1538                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539                 // bootsrap4
1540                 for (var i = ix; i > -1; i--) {
1541                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1542                 }
1543                 
1544                 
1545                 return;
1546             }
1547             cfg.cls += ' col-' + size + '-' + settings[size] + (
1548                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1549             );
1550             
1551         });
1552         
1553         if (this.hidden) {
1554             cfg.cls += ' hidden';
1555         }
1556         
1557         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1558             cfg.cls +=' alert alert-' + this.alert;
1559         }
1560         
1561         
1562         if (this.html.length) {
1563             cfg.html = this.html;
1564         }
1565         if (this.fa) {
1566             var fasize = '';
1567             if (this.fasize > 1) {
1568                 fasize = ' fa-' + this.fasize + 'x';
1569             }
1570             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1571             
1572             
1573         }
1574         if (this.icon) {
1575             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1576         }
1577         
1578         return cfg;
1579     }
1580    
1581 });
1582
1583  
1584
1585  /*
1586  * - LGPL
1587  *
1588  * page container.
1589  * 
1590  */
1591
1592
1593 /**
1594  * @class Roo.bootstrap.Container
1595  * @extends Roo.bootstrap.Component
1596  * Bootstrap Container class
1597  * @cfg {Boolean} jumbotron is it a jumbotron element
1598  * @cfg {String} html content of element
1599  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1600  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1601  * @cfg {String} header content of header (for panel)
1602  * @cfg {String} footer content of footer (for panel)
1603  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1604  * @cfg {String} tag (header|aside|section) type of HTML tag.
1605  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1606  * @cfg {String} fa font awesome icon
1607  * @cfg {String} icon (info-sign|check|...) glyphicon name
1608  * @cfg {Boolean} hidden (true|false) hide the element
1609  * @cfg {Boolean} expandable (true|false) default false
1610  * @cfg {Boolean} expanded (true|false) default true
1611  * @cfg {String} rheader contet on the right of header
1612  * @cfg {Boolean} clickable (true|false) default false
1613
1614  *     
1615  * @constructor
1616  * Create a new Container
1617  * @param {Object} config The config object
1618  */
1619
1620 Roo.bootstrap.Container = function(config){
1621     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1622     
1623     this.addEvents({
1624         // raw events
1625          /**
1626          * @event expand
1627          * After the panel has been expand
1628          * 
1629          * @param {Roo.bootstrap.Container} this
1630          */
1631         "expand" : true,
1632         /**
1633          * @event collapse
1634          * After the panel has been collapsed
1635          * 
1636          * @param {Roo.bootstrap.Container} this
1637          */
1638         "collapse" : true,
1639         /**
1640          * @event click
1641          * When a element is chick
1642          * @param {Roo.bootstrap.Container} this
1643          * @param {Roo.EventObject} e
1644          */
1645         "click" : true
1646     });
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1650     
1651     jumbotron : false,
1652     well: '',
1653     panel : '',
1654     header: '',
1655     footer : '',
1656     sticky: '',
1657     tag : false,
1658     alert : false,
1659     fa: false,
1660     icon : false,
1661     expandable : false,
1662     rheader : '',
1663     expanded : true,
1664     clickable: false,
1665   
1666      
1667     getChildContainer : function() {
1668         
1669         if(!this.el){
1670             return false;
1671         }
1672         
1673         if (this.panel.length) {
1674             return this.el.select('.panel-body',true).first();
1675         }
1676         
1677         return this.el;
1678     },
1679     
1680     
1681     getAutoCreate : function(){
1682         
1683         var cfg = {
1684             tag : this.tag || 'div',
1685             html : '',
1686             cls : ''
1687         };
1688         if (this.jumbotron) {
1689             cfg.cls = 'jumbotron';
1690         }
1691         
1692         
1693         
1694         // - this is applied by the parent..
1695         //if (this.cls) {
1696         //    cfg.cls = this.cls + '';
1697         //}
1698         
1699         if (this.sticky.length) {
1700             
1701             var bd = Roo.get(document.body);
1702             if (!bd.hasClass('bootstrap-sticky')) {
1703                 bd.addClass('bootstrap-sticky');
1704                 Roo.select('html',true).setStyle('height', '100%');
1705             }
1706              
1707             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1708         }
1709         
1710         
1711         if (this.well.length) {
1712             switch (this.well) {
1713                 case 'lg':
1714                 case 'sm':
1715                     cfg.cls +=' well well-' +this.well;
1716                     break;
1717                 default:
1718                     cfg.cls +=' well';
1719                     break;
1720             }
1721         }
1722         
1723         if (this.hidden) {
1724             cfg.cls += ' hidden';
1725         }
1726         
1727         
1728         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1729             cfg.cls +=' alert alert-' + this.alert;
1730         }
1731         
1732         var body = cfg;
1733         
1734         if (this.panel.length) {
1735             cfg.cls += ' panel panel-' + this.panel;
1736             cfg.cn = [];
1737             if (this.header.length) {
1738                 
1739                 var h = [];
1740                 
1741                 if(this.expandable){
1742                     
1743                     cfg.cls = cfg.cls + ' expandable';
1744                     
1745                     h.push({
1746                         tag: 'i',
1747                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1748                     });
1749                     
1750                 }
1751                 
1752                 h.push(
1753                     {
1754                         tag: 'span',
1755                         cls : 'panel-title',
1756                         html : (this.expandable ? '&nbsp;' : '') + this.header
1757                     },
1758                     {
1759                         tag: 'span',
1760                         cls: 'panel-header-right',
1761                         html: this.rheader
1762                     }
1763                 );
1764                 
1765                 cfg.cn.push({
1766                     cls : 'panel-heading',
1767                     style : this.expandable ? 'cursor: pointer' : '',
1768                     cn : h
1769                 });
1770                 
1771             }
1772             
1773             body = false;
1774             cfg.cn.push({
1775                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1776                 html : this.html
1777             });
1778             
1779             
1780             if (this.footer.length) {
1781                 cfg.cn.push({
1782                     cls : 'panel-footer',
1783                     html : this.footer
1784                     
1785                 });
1786             }
1787             
1788         }
1789         
1790         if (body) {
1791             body.html = this.html || cfg.html;
1792             // prefix with the icons..
1793             if (this.fa) {
1794                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1795             }
1796             if (this.icon) {
1797                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1798             }
1799             
1800             
1801         }
1802         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1803             cfg.cls =  'container';
1804         }
1805         
1806         return cfg;
1807     },
1808     
1809     initEvents: function() 
1810     {
1811         if(this.expandable){
1812             var headerEl = this.headerEl();
1813         
1814             if(headerEl){
1815                 headerEl.on('click', this.onToggleClick, this);
1816             }
1817         }
1818         
1819         if(this.clickable){
1820             this.el.on('click', this.onClick, this);
1821         }
1822         
1823     },
1824     
1825     onToggleClick : function()
1826     {
1827         var headerEl = this.headerEl();
1828         
1829         if(!headerEl){
1830             return;
1831         }
1832         
1833         if(this.expanded){
1834             this.collapse();
1835             return;
1836         }
1837         
1838         this.expand();
1839     },
1840     
1841     expand : function()
1842     {
1843         if(this.fireEvent('expand', this)) {
1844             
1845             this.expanded = true;
1846             
1847             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848             
1849             this.el.select('.panel-body',true).first().removeClass('hide');
1850             
1851             var toggleEl = this.toggleEl();
1852
1853             if(!toggleEl){
1854                 return;
1855             }
1856
1857             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1858         }
1859         
1860     },
1861     
1862     collapse : function()
1863     {
1864         if(this.fireEvent('collapse', this)) {
1865             
1866             this.expanded = false;
1867             
1868             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1869             this.el.select('.panel-body',true).first().addClass('hide');
1870         
1871             var toggleEl = this.toggleEl();
1872
1873             if(!toggleEl){
1874                 return;
1875             }
1876
1877             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1878         }
1879     },
1880     
1881     toggleEl : function()
1882     {
1883         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1884             return;
1885         }
1886         
1887         return this.el.select('.panel-heading .fa',true).first();
1888     },
1889     
1890     headerEl : function()
1891     {
1892         if(!this.el || !this.panel.length || !this.header.length){
1893             return;
1894         }
1895         
1896         return this.el.select('.panel-heading',true).first()
1897     },
1898     
1899     bodyEl : function()
1900     {
1901         if(!this.el || !this.panel.length){
1902             return;
1903         }
1904         
1905         return this.el.select('.panel-body',true).first()
1906     },
1907     
1908     titleEl : function()
1909     {
1910         if(!this.el || !this.panel.length || !this.header.length){
1911             return;
1912         }
1913         
1914         return this.el.select('.panel-title',true).first();
1915     },
1916     
1917     setTitle : function(v)
1918     {
1919         var titleEl = this.titleEl();
1920         
1921         if(!titleEl){
1922             return;
1923         }
1924         
1925         titleEl.dom.innerHTML = v;
1926     },
1927     
1928     getTitle : function()
1929     {
1930         
1931         var titleEl = this.titleEl();
1932         
1933         if(!titleEl){
1934             return '';
1935         }
1936         
1937         return titleEl.dom.innerHTML;
1938     },
1939     
1940     setRightTitle : function(v)
1941     {
1942         var t = this.el.select('.panel-header-right',true).first();
1943         
1944         if(!t){
1945             return;
1946         }
1947         
1948         t.dom.innerHTML = v;
1949     },
1950     
1951     onClick : function(e)
1952     {
1953         e.preventDefault();
1954         
1955         this.fireEvent('click', this, e);
1956     }
1957 });
1958
1959  /*
1960  *  - LGPL
1961  *
1962  *  This is BS4's Card element.. - similar to our containers probably..
1963  * 
1964  */
1965 /**
1966  * @class Roo.bootstrap.Card
1967  * @extends Roo.bootstrap.Component
1968  * Bootstrap Card class
1969  *
1970  *
1971  * possible... may not be implemented..
1972  * @cfg {String} header_image  src url of image.
1973  * @cfg {String|Object} header
1974  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1975  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1976  * 
1977  * @cfg {String} title
1978  * @cfg {String} subtitle
1979  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1980  * @cfg {String} footer
1981  
1982  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983  * 
1984  * @cfg {String} margin (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1990  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991  *
1992  * @cfg {String} padding (0|1|2|3|4|5)
1993  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1994  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1995  * @cfg {String} padding_left (0|1|2|3|4|5)
1996  * @cfg {String} padding_right (0|1|2|3|4|5)
1997  * @cfg {String} padding_x (0|1|2|3|4|5)
1998  * @cfg {String} padding_y (0|1|2|3|4|5)
1999  *
2000  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005  
2006  * @config {Boolean} dragable  if this card can be dragged.
2007  * @config {String} drag_group  group for drag
2008  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2009  * @config {String} drop_group  group for drag
2010  * 
2011  * @config {Boolean} collapsable can the body be collapsed.
2012  * @config {Boolean} collapsed is the body collapsed when rendered...
2013  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2014  * @config {Boolean} rotated is the body rotated when rendered...
2015  * 
2016  * @constructor
2017  * Create a new Container
2018  * @param {Object} config The config object
2019  */
2020
2021 Roo.bootstrap.Card = function(config){
2022     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2023     
2024     this.addEvents({
2025          // raw events
2026         /**
2027          * @event drop
2028          * When a element a card is dropped
2029          * @param {Roo.bootstrap.Card} this
2030          *
2031          * 
2032          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2033          * @param {String} position 'above' or 'below'
2034          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2035         
2036          */
2037         'drop' : true,
2038          /**
2039          * @event rotate
2040          * When a element a card is rotate
2041          * @param {Roo.bootstrap.Card} this
2042          * @param {Roo.Element} n the node being dropped?
2043          * @param {Boolean} rotate status
2044          */
2045         'rotate' : true,
2046         /**
2047          * @event cardover
2048          * When a card element is dragged over ready to drop (return false to block dropable)
2049          * @param {Roo.bootstrap.Card} this
2050          * @param {Object} data from dragdrop 
2051          */
2052          'cardover' : true
2053          
2054     });
2055 };
2056
2057
2058 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2059     
2060     
2061     weight : '',
2062     
2063     margin: '', /// may be better in component?
2064     margin_top: '', 
2065     margin_bottom: '', 
2066     margin_left: '',
2067     margin_right: '',
2068     margin_x: '',
2069     margin_y: '',
2070     
2071     padding : '',
2072     padding_top: '', 
2073     padding_bottom: '', 
2074     padding_left: '',
2075     padding_right: '',
2076     padding_x: '',
2077     padding_y: '',
2078     
2079     display: '', 
2080     display_xs: '', 
2081     display_sm: '', 
2082     display_lg: '',
2083     display_xl: '',
2084  
2085     header_image  : '',
2086     header : '',
2087     header_size : 0,
2088     title : '',
2089     subtitle : '',
2090     html : '',
2091     footer: '',
2092
2093     collapsable : false,
2094     collapsed : false,
2095     rotateable : false,
2096     rotated : false,
2097     
2098     dragable : false,
2099     drag_group : false,
2100     dropable : false,
2101     drop_group : false,
2102     childContainer : false,
2103     dropEl : false, /// the dom placeholde element that indicates drop location.
2104     containerEl: false, // body container
2105     bodyEl: false, // card-body
2106     headerContainerEl : false, //
2107     headerEl : false,
2108     header_imageEl : false,
2109     
2110     
2111     layoutCls : function()
2112     {
2113         var cls = '';
2114         var t = this;
2115         Roo.log(this.margin_bottom.length);
2116         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2117             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118             
2119             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2120                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2121             }
2122             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2123                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2124             }
2125         });
2126         
2127         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2128             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2130             }
2131         });
2132         
2133         // more generic support?
2134         if (this.hidden) {
2135             cls += ' d-none';
2136         }
2137         
2138         return cls;
2139     },
2140  
2141        // Roo.log("Call onRender: " + this.xtype);
2142         /*  We are looking at something like this.
2143 <div class="card">
2144     <img src="..." class="card-img-top" alt="...">
2145     <div class="card-body">
2146         <h5 class="card-title">Card title</h5>
2147          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148
2149         >> this bit is really the body...
2150         <div> << we will ad dthis in hopefully it will not break shit.
2151         
2152         ** card text does not actually have any styling...
2153         
2154             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2155         
2156         </div> <<
2157           <a href="#" class="card-link">Card link</a>
2158           
2159     </div>
2160     <div class="card-footer">
2161         <small class="text-muted">Last updated 3 mins ago</small>
2162     </div>
2163 </div>
2164          */
2165     getAutoCreate : function(){
2166         
2167         var cfg = {
2168             tag : 'div',
2169             cls : 'card',
2170             cn : [ ]
2171         };
2172         
2173         if (this.weight.length && this.weight != 'light') {
2174             cfg.cls += ' text-white';
2175         } else {
2176             cfg.cls += ' text-dark'; // need as it's nested..
2177         }
2178         if (this.weight.length) {
2179             cfg.cls += ' bg-' + this.weight;
2180         }
2181         
2182         cfg.cls += ' ' + this.layoutCls(); 
2183         
2184         var hdr = false;
2185         var hdr_ctr = false;
2186         if (this.header.length) {
2187             hdr = {
2188                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2189                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2190                 cn : []
2191             };
2192             cfg.cn.push(hdr);
2193             hdr_ctr = hdr;
2194         } else {
2195             hdr = {
2196                 tag : 'div',
2197                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2198                 cn : []
2199             };
2200             cfg.cn.push(hdr);
2201             hdr_ctr = hdr;
2202         }
2203         if (this.collapsable) {
2204             hdr_ctr = {
2205             tag : 'a',
2206             cls : 'd-block user-select-none',
2207             cn: [
2208                     {
2209                         tag: 'i',
2210                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2211                     }
2212                    
2213                 ]
2214             };
2215             hdr.cn.push(hdr_ctr);
2216         }
2217         
2218         hdr_ctr.cn.push(        {
2219             tag: 'span',
2220             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2221             html : this.header
2222         });
2223         
2224         
2225         if (this.header_image.length) {
2226             cfg.cn.push({
2227                 tag : 'img',
2228                 cls : 'card-img-top',
2229                 src: this.header_image // escape?
2230             });
2231         } else {
2232             cfg.cn.push({
2233                     tag : 'div',
2234                     cls : 'card-img-top d-none' 
2235                 });
2236         }
2237             
2238         var body = {
2239             tag : 'div',
2240             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2241             cn : []
2242         };
2243         var obody = body;
2244         if (this.collapsable || this.rotateable) {
2245             obody = {
2246                 tag: 'div',
2247                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2248                 cn : [  body ]
2249             };
2250         }
2251         
2252         cfg.cn.push(obody);
2253         
2254         if (this.title.length) {
2255             body.cn.push({
2256                 tag : 'div',
2257                 cls : 'card-title',
2258                 src: this.title // escape?
2259             });
2260         }  
2261         
2262         if (this.subtitle.length) {
2263             body.cn.push({
2264                 tag : 'div',
2265                 cls : 'card-title',
2266                 src: this.subtitle // escape?
2267             });
2268         }
2269         
2270         body.cn.push({
2271             tag : 'div',
2272             cls : 'roo-card-body-ctr'
2273         });
2274         
2275         if (this.html.length) {
2276             body.cn.push({
2277                 tag: 'div',
2278                 html : this.html
2279             });
2280         }
2281         // fixme ? handle objects?
2282         
2283         if (this.footer.length) {
2284            
2285             cfg.cn.push({
2286                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2287                 html : this.footer
2288             });
2289             
2290         } else {
2291             cfg.cn.push({cls : 'card-footer d-none'});
2292         }
2293         
2294         // footer...
2295         
2296         return cfg;
2297     },
2298     
2299     
2300     getCardHeader : function()
2301     {
2302         var  ret = this.el.select('.card-header',true).first();
2303         if (ret.hasClass('d-none')) {
2304             ret.removeClass('d-none');
2305         }
2306         
2307         return ret;
2308     },
2309     getCardFooter : function()
2310     {
2311         var  ret = this.el.select('.card-footer',true).first();
2312         if (ret.hasClass('d-none')) {
2313             ret.removeClass('d-none');
2314         }
2315         
2316         return ret;
2317     },
2318     getCardImageTop : function()
2319     {
2320         var  ret = this.header_imageEl;
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324             
2325         return ret;
2326     },
2327     
2328     getChildContainer : function()
2329     {
2330         
2331         if(!this.el){
2332             return false;
2333         }
2334         return this.el.select('.roo-card-body-ctr',true).first();    
2335     },
2336     
2337     initEvents: function() 
2338     {
2339         this.bodyEl = this.el.select('.card-body',true).first(); 
2340         this.containerEl = this.getChildContainer();
2341         if(this.dragable){
2342             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2343                     containerScroll: true,
2344                     ddGroup: this.drag_group || 'default_card_drag_group'
2345             });
2346             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347         }
2348         if (this.dropable) {
2349             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2350                 containerScroll: true,
2351                 ddGroup: this.drop_group || 'default_card_drag_group'
2352             });
2353             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2354             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2355             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2356             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2357             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2358         }
2359         
2360         if (this.collapsable) {
2361             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362         }
2363         if (this.rotateable) {
2364             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365         }
2366         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367          
2368         this.footerEl = this.el.select('.card-footer',true).first();
2369         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2370         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2371         this.headerEl = this.el.select('.card-header',true).first();
2372         
2373         if (this.rotated) {
2374             this.el.addClass('roo-card-rotated');
2375             this.fireEvent('rotate', this, true);
2376         }
2377         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2378         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2379         
2380     },
2381     getDragData : function(e)
2382     {
2383         var target = this.getEl();
2384         if (target) {
2385             //this.handleSelection(e);
2386             
2387             var dragData = {
2388                 source: this,
2389                 copy: false,
2390                 nodes: this.getEl(),
2391                 records: []
2392             };
2393             
2394             
2395             dragData.ddel = target.dom ;    // the div element
2396             Roo.log(target.getWidth( ));
2397             dragData.ddel.style.width = target.getWidth() + 'px';
2398             
2399             return dragData;
2400         }
2401         return false;
2402     },
2403     /**
2404     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2405     *    whole Element becomes the target, and this causes the drop gesture to append.
2406     *
2407     *    Returns an object:
2408     *     {
2409            
2410            position : 'below' or 'above'
2411            card  : relateive to card OBJECT (or true for no cards listed)
2412            items_n : relative to nth item in list
2413            card_n : relative to  nth card in list
2414     }
2415     *
2416     *    
2417     */
2418     getTargetFromEvent : function(e, dragged_card_el)
2419     {
2420         var target = e.getTarget();
2421         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2422             target = target.parentNode;
2423         }
2424         
2425         var ret = {
2426             position: '',
2427             cards : [],
2428             card_n : -1,
2429             items_n : -1,
2430             card : false 
2431         };
2432         
2433         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2434         // see if target is one of the 'cards'...
2435         
2436         
2437         //Roo.log(this.items.length);
2438         var pos = false;
2439         
2440         var last_card_n = 0;
2441         var cards_len  = 0;
2442         for (var i = 0;i< this.items.length;i++) {
2443             
2444             if (!this.items[i].el.hasClass('card')) {
2445                  continue;
2446             }
2447             pos = this.getDropPoint(e, this.items[i].el.dom);
2448             
2449             cards_len = ret.cards.length;
2450             //Roo.log(this.items[i].el.dom.id);
2451             ret.cards.push(this.items[i]);
2452             last_card_n  = i;
2453             if (ret.card_n < 0 && pos == 'above') {
2454                 ret.position = cards_len > 0 ? 'below' : pos;
2455                 ret.items_n = i > 0 ? i - 1 : 0;
2456                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2457                 ret.card = ret.cards[ret.card_n];
2458             }
2459         }
2460         if (!ret.cards.length) {
2461             ret.card = true;
2462             ret.position = 'below';
2463             ret.items_n;
2464             return ret;
2465         }
2466         // could not find a card.. stick it at the end..
2467         if (ret.card_n < 0) {
2468             ret.card_n = last_card_n;
2469             ret.card = ret.cards[last_card_n];
2470             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2471             ret.position = 'below';
2472         }
2473         
2474         if (this.items[ret.items_n].el == dragged_card_el) {
2475             return false;
2476         }
2477         
2478         if (ret.position == 'below') {
2479             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480             
2481             if (card_after  && card_after.el == dragged_card_el) {
2482                 return false;
2483             }
2484             return ret;
2485         }
2486         
2487         // its's after ..
2488         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489         
2490         if (card_before  && card_before.el == dragged_card_el) {
2491             return false;
2492         }
2493         
2494         return ret;
2495     },
2496     
2497     onNodeEnter : function(n, dd, e, data){
2498         return false;
2499     },
2500     onNodeOver : function(n, dd, e, data)
2501     {
2502        
2503         var target_info = this.getTargetFromEvent(e,data.source.el);
2504         if (target_info === false) {
2505             this.dropPlaceHolder('hide');
2506             return false;
2507         }
2508         Roo.log(['getTargetFromEvent', target_info ]);
2509         
2510         
2511         if (this.fireEvent('cardover', this, [ data ]) === false) {
2512             return false;
2513         }
2514         
2515         this.dropPlaceHolder('show', target_info,data);
2516         
2517         return false; 
2518     },
2519     onNodeOut : function(n, dd, e, data){
2520         this.dropPlaceHolder('hide');
2521      
2522     },
2523     onNodeDrop : function(n, dd, e, data)
2524     {
2525         
2526         // call drop - return false if
2527         
2528         // this could actually fail - if the Network drops..
2529         // we will ignore this at present..- client should probably reload
2530         // the whole set of cards if stuff like that fails.
2531         
2532         
2533         var info = this.getTargetFromEvent(e,data.source.el);
2534         if (info === false) {
2535             return false;
2536         }
2537         this.dropPlaceHolder('hide');
2538   
2539           
2540     
2541         this.acceptCard(data.source, info.position, info.card, info.items_n);
2542         return true;
2543          
2544     },
2545     firstChildCard : function()
2546     {
2547         for (var i = 0;i< this.items.length;i++) {
2548             
2549             if (!this.items[i].el.hasClass('card')) {
2550                  continue;
2551             }
2552             return this.items[i];
2553         }
2554         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2555     },
2556     /**
2557      * accept card
2558      *
2559      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2560      */
2561     acceptCard : function(move_card,  position, next_to_card )
2562     {
2563         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2564             return false;
2565         }
2566         
2567         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568         
2569         move_card.parent().removeCard(move_card);
2570         
2571         
2572         var dom = move_card.el.dom;
2573         dom.style.width = ''; // clear with - which is set by drag.
2574         
2575         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2576             var cardel = next_to_card.el.dom;
2577             
2578             if (position == 'above' ) {
2579                 cardel.parentNode.insertBefore(dom, cardel);
2580             } else if (cardel.nextSibling) {
2581                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582             } else {
2583                 cardel.parentNode.append(dom);
2584             }
2585         } else {
2586             // card container???
2587             this.containerEl.dom.append(dom);
2588         }
2589         
2590         //FIXME HANDLE card = true 
2591         
2592         // add this to the correct place in items.
2593         
2594         // remove Card from items.
2595         
2596        
2597         if (this.items.length) {
2598             var nitems = [];
2599             //Roo.log([info.items_n, info.position, this.items.length]);
2600             for (var i =0; i < this.items.length; i++) {
2601                 if (i == to_items_n && position == 'above') {
2602                     nitems.push(move_card);
2603                 }
2604                 nitems.push(this.items[i]);
2605                 if (i == to_items_n && position == 'below') {
2606                     nitems.push(move_card);
2607                 }
2608             }
2609             this.items = nitems;
2610             Roo.log(this.items);
2611         } else {
2612             this.items.push(move_card);
2613         }
2614         
2615         move_card.parentId = this.id;
2616         
2617         return true;
2618         
2619         
2620     },
2621     removeCard : function(c)
2622     {
2623         this.items = this.items.filter(function(e) { return e != c });
2624  
2625         var dom = c.el.dom;
2626         dom.parentNode.removeChild(dom);
2627         dom.style.width = ''; // clear with - which is set by drag.
2628         c.parentId = false;
2629         
2630     },
2631     
2632     /**    Decide whether to drop above or below a View node. */
2633     getDropPoint : function(e, n, dd)
2634     {
2635         if (dd) {
2636              return false;
2637         }
2638         if (n == this.containerEl.dom) {
2639             return "above";
2640         }
2641         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2642         var c = t + (b - t) / 2;
2643         var y = Roo.lib.Event.getPageY(e);
2644         if(y <= c) {
2645             return "above";
2646         }else{
2647             return "below";
2648         }
2649     },
2650     onToggleCollapse : function(e)
2651         {
2652         if (this.collapsed) {
2653             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2654             this.collapsableEl.addClass('show');
2655             this.collapsed = false;
2656             return;
2657         }
2658         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2659         this.collapsableEl.removeClass('show');
2660         this.collapsed = true;
2661         
2662     
2663     },
2664     
2665     onToggleRotate : function(e)
2666     {
2667         this.collapsableEl.removeClass('show');
2668         this.footerEl.removeClass('d-none');
2669         this.el.removeClass('roo-card-rotated');
2670         this.el.removeClass('d-none');
2671         if (this.rotated) {
2672             
2673             this.collapsableEl.addClass('show');
2674             this.rotated = false;
2675             this.fireEvent('rotate', this, this.rotated);
2676             return;
2677         }
2678         this.el.addClass('roo-card-rotated');
2679         this.footerEl.addClass('d-none');
2680         this.el.select('.roo-collapsable').removeClass('show');
2681         
2682         this.rotated = true;
2683         this.fireEvent('rotate', this, this.rotated);
2684     
2685     },
2686     
2687     dropPlaceHolder: function (action, info, data)
2688     {
2689         if (this.dropEl === false) {
2690             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2691             cls : 'd-none'
2692             },true);
2693         }
2694         this.dropEl.removeClass(['d-none', 'd-block']);        
2695         if (action == 'hide') {
2696             
2697             this.dropEl.addClass('d-none');
2698             return;
2699         }
2700         // FIXME - info.card == true!!!
2701         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702         
2703         if (info.card !== true) {
2704             var cardel = info.card.el.dom;
2705             
2706             if (info.position == 'above') {
2707                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2708             } else if (cardel.nextSibling) {
2709                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710             } else {
2711                 cardel.parentNode.append(this.dropEl.dom);
2712             }
2713         } else {
2714             // card container???
2715             this.containerEl.dom.append(this.dropEl.dom);
2716         }
2717         
2718         this.dropEl.addClass('d-block roo-card-dropzone');
2719         
2720         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2721         
2722         
2723     
2724     
2725     
2726     },
2727     setHeaderText: function(html)
2728     {
2729         this.header = html;
2730         if (this.headerContainerEl) {
2731             this.headerContainerEl.dom.innerHTML = html;
2732         }
2733     },
2734     onHeaderImageLoad : function(ev, he)
2735     {
2736         if (!this.header_image_fit_square) {
2737             return;
2738         }
2739         
2740         var hw = he.naturalHeight / he.naturalWidth;
2741         // wide image = < 0
2742         // tall image = > 1
2743         //var w = he.dom.naturalWidth;
2744         var ww = he.width;
2745         he.style.left =  0;
2746         he.style.position =  'relative';
2747         if (hw > 1) {
2748             var nw = (ww * (1/hw));
2749             Roo.get(he).setSize( ww * (1/hw),  ww);
2750             he.style.left =  ((ww - nw)/ 2) + 'px';
2751             he.style.position =  'relative';
2752         }
2753
2754     }
2755
2756     
2757 });
2758
2759 /*
2760  * - LGPL
2761  *
2762  * Card header - holder for the card header elements.
2763  * 
2764  */
2765
2766 /**
2767  * @class Roo.bootstrap.CardHeader
2768  * @extends Roo.bootstrap.Element
2769  * Bootstrap CardHeader class
2770  * @constructor
2771  * Create a new Card Header - that you can embed children into
2772  * @param {Object} config The config object
2773  */
2774
2775 Roo.bootstrap.CardHeader = function(config){
2776     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2777 };
2778
2779 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2780     
2781     
2782     container_method : 'getCardHeader' 
2783     
2784      
2785     
2786     
2787    
2788 });
2789
2790  
2791
2792  /*
2793  * - LGPL
2794  *
2795  * Card footer - holder for the card footer elements.
2796  * 
2797  */
2798
2799 /**
2800  * @class Roo.bootstrap.CardFooter
2801  * @extends Roo.bootstrap.Element
2802  * Bootstrap CardFooter class
2803  * @constructor
2804  * Create a new Card Footer - that you can embed children into
2805  * @param {Object} config The config object
2806  */
2807
2808 Roo.bootstrap.CardFooter = function(config){
2809     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2810 };
2811
2812 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2813     
2814     
2815     container_method : 'getCardFooter' 
2816     
2817      
2818     
2819     
2820    
2821 });
2822
2823  
2824
2825  /*
2826  * - LGPL
2827  *
2828  * Card header - holder for the card header elements.
2829  * 
2830  */
2831
2832 /**
2833  * @class Roo.bootstrap.CardImageTop
2834  * @extends Roo.bootstrap.Element
2835  * Bootstrap CardImageTop class
2836  * @constructor
2837  * Create a new Card Image Top container
2838  * @param {Object} config The config object
2839  */
2840
2841 Roo.bootstrap.CardImageTop = function(config){
2842     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2843 };
2844
2845 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2846     
2847    
2848     container_method : 'getCardImageTop' 
2849     
2850      
2851     
2852    
2853 });
2854
2855  
2856
2857  
2858 /*
2859 * Licence: LGPL
2860 */
2861
2862 /**
2863  * @class Roo.bootstrap.ButtonUploader
2864  * @extends Roo.bootstrap.Button
2865  * Bootstrap Button Uploader class - it's a button which when you add files to it
2866  *
2867  * 
2868  * @cfg {Number} errorTimeout default 3000
2869  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2870  * @cfg {Array}  html The button text.
2871  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2872  *
2873  * @constructor
2874  * Create a new CardUploader
2875  * @param {Object} config The config object
2876  */
2877
2878 Roo.bootstrap.ButtonUploader = function(config){
2879     
2880  
2881     
2882     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2883     
2884      
2885      this.addEvents({
2886          // raw events
2887         /**
2888          * @event beforeselect
2889          * When button is pressed, before show upload files dialog is shown
2890          * @param {Roo.bootstrap.UploaderButton} this
2891          *
2892          */
2893         'beforeselect' : true,
2894          /**
2895          * @event fired when files have been selected, 
2896          * When a the download link is clicked
2897          * @param {Roo.bootstrap.UploaderButton} this
2898          * @param {Array} Array of files that have been uploaded
2899          */
2900         'uploaded' : true
2901         
2902     });
2903 };
2904  
2905 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2906     
2907      
2908     errorTimeout : 3000,
2909      
2910     images : false,
2911    
2912     fileCollection : false,
2913     allowBlank : true,
2914     
2915     multiple : true,
2916     
2917     getAutoCreate : function()
2918     {
2919         var im = {
2920             tag: 'input',
2921             type : 'file',
2922             cls : 'd-none  roo-card-upload-selector' 
2923           
2924         };
2925         if (this.multiple) {
2926             im.multiple = 'multiple';
2927         }
2928         
2929         return  {
2930             cls :'div' ,
2931             cn : [
2932                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2933                 im
2934
2935             ]
2936         };
2937            
2938          
2939     },
2940      
2941    
2942     initEvents : function()
2943     {
2944         
2945         Roo.bootstrap.Button.prototype.initEvents.call(this);
2946         
2947         
2948         
2949         
2950         
2951         this.urlAPI = (window.createObjectURL && window) || 
2952                                 (window.URL && URL.revokeObjectURL && URL) || 
2953                                 (window.webkitURL && webkitURL);
2954                         
2955          
2956          
2957          
2958         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959         
2960         this.selectorEl.on('change', this.onFileSelected, this);
2961          
2962          
2963        
2964     },
2965     
2966    
2967     onClick : function(e)
2968     {
2969         e.preventDefault();
2970         
2971         if ( this.fireEvent('beforeselect', this) === false) {
2972             return;
2973         }
2974          
2975         this.selectorEl.dom.click();
2976          
2977     },
2978     
2979     onFileSelected : function(e)
2980     {
2981         e.preventDefault();
2982         
2983         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2984             return;
2985         }
2986         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2987         this.selectorEl.dom.value  = '';// hopefully reset..
2988         
2989         this.fireEvent('uploaded', this,  files );
2990         
2991     },
2992     
2993        
2994    
2995     
2996     /**
2997      * addCard - add an Attachment to the uploader
2998      * @param data - the data about the image to upload
2999      *
3000      * {
3001           id : 123
3002           title : "Title of file",
3003           is_uploaded : false,
3004           src : "http://.....",
3005           srcfile : { the File upload object },
3006           mimetype : file.type,
3007           preview : false,
3008           is_deleted : 0
3009           .. any other data...
3010         }
3011      *
3012      * 
3013     */
3014      
3015     reset: function()
3016     {
3017          
3018          this.selectorEl
3019     } 
3020     
3021     
3022     
3023     
3024 });
3025  /*
3026  * - LGPL
3027  *
3028  * image
3029  * 
3030  */
3031
3032
3033 /**
3034  * @class Roo.bootstrap.Img
3035  * @extends Roo.bootstrap.Component
3036  * Bootstrap Img class
3037  * @cfg {Boolean} imgResponsive false | true
3038  * @cfg {String} border rounded | circle | thumbnail
3039  * @cfg {String} src image source
3040  * @cfg {String} alt image alternative text
3041  * @cfg {String} href a tag href
3042  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3043  * @cfg {String} xsUrl xs image source
3044  * @cfg {String} smUrl sm image source
3045  * @cfg {String} mdUrl md image source
3046  * @cfg {String} lgUrl lg image source
3047  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3048  * 
3049  * @constructor
3050  * Create a new Input
3051  * @param {Object} config The config object
3052  */
3053
3054 Roo.bootstrap.Img = function(config){
3055     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3056     
3057     this.addEvents({
3058         // img events
3059         /**
3060          * @event click
3061          * The img click event for the img.
3062          * @param {Roo.EventObject} e
3063          */
3064         "click" : true,
3065         /**
3066          * @event load
3067          * The when any image loads
3068          * @param {Roo.EventObject} e
3069          */
3070         "load" : true
3071     });
3072 };
3073
3074 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3075     
3076     imgResponsive: true,
3077     border: '',
3078     src: 'about:blank',
3079     href: false,
3080     target: false,
3081     xsUrl: '',
3082     smUrl: '',
3083     mdUrl: '',
3084     lgUrl: '',
3085     backgroundContain : false,
3086
3087     getAutoCreate : function()
3088     {   
3089         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3090             return this.createSingleImg();
3091         }
3092         
3093         var cfg = {
3094             tag: 'div',
3095             cls: 'roo-image-responsive-group',
3096             cn: []
3097         };
3098         var _this = this;
3099         
3100         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101             
3102             if(!_this[size + 'Url']){
3103                 return;
3104             }
3105             
3106             var img = {
3107                 tag: 'img',
3108                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3109                 html: _this.html || cfg.html,
3110                 src: _this[size + 'Url']
3111             };
3112             
3113             img.cls += ' roo-image-responsive-' + size;
3114             
3115             var s = ['xs', 'sm', 'md', 'lg'];
3116             
3117             s.splice(s.indexOf(size), 1);
3118             
3119             Roo.each(s, function(ss){
3120                 img.cls += ' hidden-' + ss;
3121             });
3122             
3123             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3124                 cfg.cls += ' img-' + _this.border;
3125             }
3126             
3127             if(_this.alt){
3128                 cfg.alt = _this.alt;
3129             }
3130             
3131             if(_this.href){
3132                 var a = {
3133                     tag: 'a',
3134                     href: _this.href,
3135                     cn: [
3136                         img
3137                     ]
3138                 };
3139
3140                 if(this.target){
3141                     a.target = _this.target;
3142                 }
3143             }
3144             
3145             cfg.cn.push((_this.href) ? a : img);
3146             
3147         });
3148         
3149         return cfg;
3150     },
3151     
3152     createSingleImg : function()
3153     {
3154         var cfg = {
3155             tag: 'img',
3156             cls: (this.imgResponsive) ? 'img-responsive' : '',
3157             html : null,
3158             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3159         };
3160         
3161         if (this.backgroundContain) {
3162             cfg.cls += ' background-contain';
3163         }
3164         
3165         cfg.html = this.html || cfg.html;
3166         
3167         if (this.backgroundContain) {
3168             cfg.style="background-image: url(" + this.src + ')';
3169         } else {
3170             cfg.src = this.src || cfg.src;
3171         }
3172         
3173         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3174             cfg.cls += ' img-' + this.border;
3175         }
3176         
3177         if(this.alt){
3178             cfg.alt = this.alt;
3179         }
3180         
3181         if(this.href){
3182             var a = {
3183                 tag: 'a',
3184                 href: this.href,
3185                 cn: [
3186                     cfg
3187                 ]
3188             };
3189             
3190             if(this.target){
3191                 a.target = this.target;
3192             }
3193             
3194         }
3195         
3196         return (this.href) ? a : cfg;
3197     },
3198     
3199     initEvents: function() 
3200     {
3201         if(!this.href){
3202             this.el.on('click', this.onClick, this);
3203         }
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.on('load', this.onImageLoad, this);
3206         } else {
3207             // not sure if this works.. not tested
3208             this.el.select('img', true).on('load', this.onImageLoad, this);
3209         }
3210         
3211     },
3212     
3213     onClick : function(e)
3214     {
3215         Roo.log('img onclick');
3216         this.fireEvent('click', this, e);
3217     },
3218     onImageLoad: function(e)
3219     {
3220         Roo.log('img load');
3221         this.fireEvent('load', this, e);
3222     },
3223     
3224     /**
3225      * Sets the url of the image - used to update it
3226      * @param {String} url the url of the image
3227      */
3228     
3229     setSrc : function(url)
3230     {
3231         this.src =  url;
3232         
3233         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3234             if (this.backgroundContain) {
3235                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3236             } else {
3237                 this.el.dom.src =  url;
3238             }
3239             return;
3240         }
3241         
3242         this.el.select('img', true).first().dom.src =  url;
3243     }
3244     
3245     
3246    
3247 });
3248
3249  /*
3250  * - LGPL
3251  *
3252  * image
3253  * 
3254  */
3255
3256
3257 /**
3258  * @class Roo.bootstrap.Link
3259  * @extends Roo.bootstrap.Component
3260  * Bootstrap Link Class
3261  * @cfg {String} alt image alternative text
3262  * @cfg {String} href a tag href
3263  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3264  * @cfg {String} html the content of the link.
3265  * @cfg {String} anchor name for the anchor link
3266  * @cfg {String} fa - favicon
3267
3268  * @cfg {Boolean} preventDefault (true | false) default false
3269
3270  * 
3271  * @constructor
3272  * Create a new Input
3273  * @param {Object} config The config object
3274  */
3275
3276 Roo.bootstrap.Link = function(config){
3277     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3278     
3279     this.addEvents({
3280         // img events
3281         /**
3282          * @event click
3283          * The img click event for the img.
3284          * @param {Roo.EventObject} e
3285          */
3286         "click" : true
3287     });
3288 };
3289
3290 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3291     
3292     href: false,
3293     target: false,
3294     preventDefault: false,
3295     anchor : false,
3296     alt : false,
3297     fa: false,
3298
3299
3300     getAutoCreate : function()
3301     {
3302         var html = this.html || '';
3303         
3304         if (this.fa !== false) {
3305             html = '<i class="fa fa-' + this.fa + '"></i>';
3306         }
3307         var cfg = {
3308             tag: 'a'
3309         };
3310         // anchor's do not require html/href...
3311         if (this.anchor === false) {
3312             cfg.html = html;
3313             cfg.href = this.href || '#';
3314         } else {
3315             cfg.name = this.anchor;
3316             if (this.html !== false || this.fa !== false) {
3317                 cfg.html = html;
3318             }
3319             if (this.href !== false) {
3320                 cfg.href = this.href;
3321             }
3322         }
3323         
3324         if(this.alt !== false){
3325             cfg.alt = this.alt;
3326         }
3327         
3328         
3329         if(this.target !== false) {
3330             cfg.target = this.target;
3331         }
3332         
3333         return cfg;
3334     },
3335     
3336     initEvents: function() {
3337         
3338         if(!this.href || this.preventDefault){
3339             this.el.on('click', this.onClick, this);
3340         }
3341     },
3342     
3343     onClick : function(e)
3344     {
3345         if(this.preventDefault){
3346             e.preventDefault();
3347         }
3348         //Roo.log('img onclick');
3349         this.fireEvent('click', this, e);
3350     }
3351    
3352 });
3353
3354  /*
3355  * - LGPL
3356  *
3357  * header
3358  * 
3359  */
3360
3361 /**
3362  * @class Roo.bootstrap.Header
3363  * @extends Roo.bootstrap.Component
3364  * Bootstrap Header class
3365  * @cfg {String} html content of header
3366  * @cfg {Number} level (1|2|3|4|5|6) default 1
3367  * 
3368  * @constructor
3369  * Create a new Header
3370  * @param {Object} config The config object
3371  */
3372
3373
3374 Roo.bootstrap.Header  = function(config){
3375     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3376 };
3377
3378 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3379     
3380     //href : false,
3381     html : false,
3382     level : 1,
3383     
3384     
3385     
3386     getAutoCreate : function(){
3387         
3388         
3389         
3390         var cfg = {
3391             tag: 'h' + (1 *this.level),
3392             html: this.html || ''
3393         } ;
3394         
3395         return cfg;
3396     }
3397    
3398 });
3399
3400  
3401
3402  /*
3403  * Based on:
3404  * Ext JS Library 1.1.1
3405  * Copyright(c) 2006-2007, Ext JS, LLC.
3406  *
3407  * Originally Released Under LGPL - original licence link has changed is not relivant.
3408  *
3409  * Fork - LGPL
3410  * <script type="text/javascript">
3411  */
3412  
3413 /**
3414  * @class Roo.bootstrap.MenuMgr
3415  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3416  * @singleton
3417  */
3418 Roo.bootstrap.MenuMgr = function(){
3419    var menus, active, groups = {}, attached = false, lastShow = new Date();
3420
3421    // private - called when first menu is created
3422    function init(){
3423        menus = {};
3424        active = new Roo.util.MixedCollection();
3425        Roo.get(document).addKeyListener(27, function(){
3426            if(active.length > 0){
3427                hideAll();
3428            }
3429        });
3430    }
3431
3432    // private
3433    function hideAll(){
3434        if(active && active.length > 0){
3435            var c = active.clone();
3436            c.each(function(m){
3437                m.hide();
3438            });
3439        }
3440    }
3441
3442    // private
3443    function onHide(m){
3444        active.remove(m);
3445        if(active.length < 1){
3446            Roo.get(document).un("mouseup", onMouseDown);
3447             
3448            attached = false;
3449        }
3450    }
3451
3452    // private
3453    function onShow(m){
3454        var last = active.last();
3455        lastShow = new Date();
3456        active.add(m);
3457        if(!attached){
3458           Roo.get(document).on("mouseup", onMouseDown);
3459            
3460            attached = true;
3461        }
3462        if(m.parentMenu){
3463           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3464           m.parentMenu.activeChild = m;
3465        }else if(last && last.isVisible()){
3466           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3467        }
3468    }
3469
3470    // private
3471    function onBeforeHide(m){
3472        if(m.activeChild){
3473            m.activeChild.hide();
3474        }
3475        if(m.autoHideTimer){
3476            clearTimeout(m.autoHideTimer);
3477            delete m.autoHideTimer;
3478        }
3479    }
3480
3481    // private
3482    function onBeforeShow(m){
3483        var pm = m.parentMenu;
3484        if(!pm && !m.allowOtherMenus){
3485            hideAll();
3486        }else if(pm && pm.activeChild && active != m){
3487            pm.activeChild.hide();
3488        }
3489    }
3490
3491    // private this should really trigger on mouseup..
3492    function onMouseDown(e){
3493         Roo.log("on Mouse Up");
3494         
3495         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3496             Roo.log("MenuManager hideAll");
3497             hideAll();
3498             e.stopEvent();
3499         }
3500         
3501         
3502    }
3503
3504    // private
3505    function onBeforeCheck(mi, state){
3506        if(state){
3507            var g = groups[mi.group];
3508            for(var i = 0, l = g.length; i < l; i++){
3509                if(g[i] != mi){
3510                    g[i].setChecked(false);
3511                }
3512            }
3513        }
3514    }
3515
3516    return {
3517
3518        /**
3519         * Hides all menus that are currently visible
3520         */
3521        hideAll : function(){
3522             hideAll();  
3523        },
3524
3525        // private
3526        register : function(menu){
3527            if(!menus){
3528                init();
3529            }
3530            menus[menu.id] = menu;
3531            menu.on("beforehide", onBeforeHide);
3532            menu.on("hide", onHide);
3533            menu.on("beforeshow", onBeforeShow);
3534            menu.on("show", onShow);
3535            var g = menu.group;
3536            if(g && menu.events["checkchange"]){
3537                if(!groups[g]){
3538                    groups[g] = [];
3539                }
3540                groups[g].push(menu);
3541                menu.on("checkchange", onCheck);
3542            }
3543        },
3544
3545         /**
3546          * Returns a {@link Roo.menu.Menu} object
3547          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3548          * be used to generate and return a new Menu instance.
3549          */
3550        get : function(menu){
3551            if(typeof menu == "string"){ // menu id
3552                return menus[menu];
3553            }else if(menu.events){  // menu instance
3554                return menu;
3555            }
3556            /*else if(typeof menu.length == 'number'){ // array of menu items?
3557                return new Roo.bootstrap.Menu({items:menu});
3558            }else{ // otherwise, must be a config
3559                return new Roo.bootstrap.Menu(menu);
3560            }
3561            */
3562            return false;
3563        },
3564
3565        // private
3566        unregister : function(menu){
3567            delete menus[menu.id];
3568            menu.un("beforehide", onBeforeHide);
3569            menu.un("hide", onHide);
3570            menu.un("beforeshow", onBeforeShow);
3571            menu.un("show", onShow);
3572            var g = menu.group;
3573            if(g && menu.events["checkchange"]){
3574                groups[g].remove(menu);
3575                menu.un("checkchange", onCheck);
3576            }
3577        },
3578
3579        // private
3580        registerCheckable : function(menuItem){
3581            var g = menuItem.group;
3582            if(g){
3583                if(!groups[g]){
3584                    groups[g] = [];
3585                }
3586                groups[g].push(menuItem);
3587                menuItem.on("beforecheckchange", onBeforeCheck);
3588            }
3589        },
3590
3591        // private
3592        unregisterCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                groups[g].remove(menuItem);
3596                menuItem.un("beforecheckchange", onBeforeCheck);
3597            }
3598        }
3599    };
3600 }();/*
3601  * - LGPL
3602  *
3603  * menu
3604  * 
3605  */
3606
3607 /**
3608  * @class Roo.bootstrap.Menu
3609  * @extends Roo.bootstrap.Component
3610  * Bootstrap Menu class - container for MenuItems
3611  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3612  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3613  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3614  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3615   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3616   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3617  
3618  * @constructor
3619  * Create a new Menu
3620  * @param {Object} config The config object
3621  */
3622
3623
3624 Roo.bootstrap.Menu = function(config){
3625     
3626     if (config.type == 'treeview') {
3627         // normally menu's are drawn attached to the document to handle layering etc..
3628         // however treeview (used by the docs menu is drawn into the parent element)
3629         this.container_method = 'getChildContainer'; 
3630     }
3631     
3632     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3633     if (this.registerMenu && this.type != 'treeview')  {
3634         Roo.bootstrap.MenuMgr.register(this);
3635     }
3636     
3637     
3638     this.addEvents({
3639         /**
3640          * @event beforeshow
3641          * Fires before this menu is displayed (return false to block)
3642          * @param {Roo.menu.Menu} this
3643          */
3644         beforeshow : true,
3645         /**
3646          * @event beforehide
3647          * Fires before this menu is hidden (return false to block)
3648          * @param {Roo.menu.Menu} this
3649          */
3650         beforehide : true,
3651         /**
3652          * @event show
3653          * Fires after this menu is displayed
3654          * @param {Roo.menu.Menu} this
3655          */
3656         show : true,
3657         /**
3658          * @event hide
3659          * Fires after this menu is hidden
3660          * @param {Roo.menu.Menu} this
3661          */
3662         hide : true,
3663         /**
3664          * @event click
3665          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3666          * @param {Roo.menu.Menu} this
3667          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3668          * @param {Roo.EventObject} e
3669          */
3670         click : true,
3671         /**
3672          * @event mouseover
3673          * Fires when the mouse is hovering over this menu
3674          * @param {Roo.menu.Menu} this
3675          * @param {Roo.EventObject} e
3676          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3677          */
3678         mouseover : true,
3679         /**
3680          * @event mouseout
3681          * Fires when the mouse exits this menu
3682          * @param {Roo.menu.Menu} this
3683          * @param {Roo.EventObject} e
3684          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3685          */
3686         mouseout : true,
3687         /**
3688          * @event itemclick
3689          * Fires when a menu item contained in this menu is clicked
3690          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3691          * @param {Roo.EventObject} e
3692          */
3693         itemclick: true
3694     });
3695     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3696 };
3697
3698 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3699     
3700    /// html : false,
3701    
3702     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3703     type: false,
3704     /**
3705      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706      */
3707     registerMenu : true,
3708     
3709     menuItems :false, // stores the menu items..
3710     
3711     hidden:true,
3712         
3713     parentMenu : false,
3714     
3715     stopEvent : true,
3716     
3717     isLink : false,
3718     
3719     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720     
3721     hideTrigger : false,
3722     
3723     align : 'tl-bl?',
3724     
3725     
3726     getChildContainer : function() {
3727         return this.el;  
3728     },
3729     
3730     getAutoCreate : function(){
3731          
3732         //if (['right'].indexOf(this.align)!==-1) {
3733         //    cfg.cn[1].cls += ' pull-right'
3734         //}
3735          
3736         var cfg = {
3737             tag : 'ul',
3738             cls : 'dropdown-menu shadow' ,
3739             style : 'z-index:1000'
3740             
3741         };
3742         
3743         if (this.type === 'submenu') {
3744             cfg.cls = 'submenu active';
3745         }
3746         if (this.type === 'treeview') {
3747             cfg.cls = 'treeview-menu';
3748         }
3749         
3750         return cfg;
3751     },
3752     initEvents : function() {
3753         
3754        // Roo.log("ADD event");
3755        // Roo.log(this.triggerEl.dom);
3756         if (this.triggerEl) {
3757             
3758             this.triggerEl.on('click', this.onTriggerClick, this);
3759             
3760             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761             
3762             if (!this.hideTrigger) {
3763                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3764                     // dropdown toggle on the 'a' in BS4?
3765                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766                 } else {
3767                     this.triggerEl.addClass('dropdown-toggle');
3768                 }
3769             }
3770         }
3771         
3772         if (Roo.isTouch) {
3773             this.el.on('touchstart'  , this.onTouch, this);
3774         }
3775         this.el.on('click' , this.onClick, this);
3776
3777         this.el.on("mouseover", this.onMouseOver, this);
3778         this.el.on("mouseout", this.onMouseOut, this);
3779         
3780     },
3781     
3782     findTargetItem : function(e)
3783     {
3784         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3785         if(!t){
3786             return false;
3787         }
3788         //Roo.log(t);         Roo.log(t.id);
3789         if(t && t.id){
3790             //Roo.log(this.menuitems);
3791             return this.menuitems.get(t.id);
3792             
3793             //return this.items.get(t.menuItemId);
3794         }
3795         
3796         return false;
3797     },
3798     
3799     onTouch : function(e) 
3800     {
3801         Roo.log("menu.onTouch");
3802         //e.stopEvent(); this make the user popdown broken
3803         this.onClick(e);
3804     },
3805     
3806     onClick : function(e)
3807     {
3808         Roo.log("menu.onClick");
3809         
3810         var t = this.findTargetItem(e);
3811         if(!t || t.isContainer){
3812             return;
3813         }
3814         Roo.log(e);
3815         /*
3816         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3817             if(t == this.activeItem && t.shouldDeactivate(e)){
3818                 this.activeItem.deactivate();
3819                 delete this.activeItem;
3820                 return;
3821             }
3822             if(t.canActivate){
3823                 this.setActiveItem(t, true);
3824             }
3825             return;
3826             
3827             
3828         }
3829         */
3830        
3831         Roo.log('pass click event');
3832         
3833         t.onClick(e);
3834         
3835         this.fireEvent("click", this, t, e);
3836         
3837         var _this = this;
3838         
3839         if(!t.href.length || t.href == '#'){
3840             (function() { _this.hide(); }).defer(100);
3841         }
3842         
3843     },
3844     
3845     onMouseOver : function(e){
3846         var t  = this.findTargetItem(e);
3847         //Roo.log(t);
3848         //if(t){
3849         //    if(t.canActivate && !t.disabled){
3850         //        this.setActiveItem(t, true);
3851         //    }
3852         //}
3853         
3854         this.fireEvent("mouseover", this, e, t);
3855     },
3856     isVisible : function(){
3857         return !this.hidden;
3858     },
3859     onMouseOut : function(e){
3860         var t  = this.findTargetItem(e);
3861         
3862         //if(t ){
3863         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3864         //        this.activeItem.deactivate();
3865         //        delete this.activeItem;
3866         //    }
3867         //}
3868         this.fireEvent("mouseout", this, e, t);
3869     },
3870     
3871     
3872     /**
3873      * Displays this menu relative to another element
3874      * @param {String/HTMLElement/Roo.Element} element The element to align to
3875      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3876      * the element (defaults to this.defaultAlign)
3877      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878      */
3879     show : function(el, pos, parentMenu)
3880     {
3881         if (false === this.fireEvent("beforeshow", this)) {
3882             Roo.log("show canceled");
3883             return;
3884         }
3885         this.parentMenu = parentMenu;
3886         if(!this.el){
3887             this.render();
3888         }
3889         this.el.addClass('show'); // show otherwise we do not know how big we are..
3890          
3891         var xy = this.el.getAlignToXY(el, pos);
3892         
3893         // bl-tl << left align  below
3894         // tl-bl << left align 
3895         
3896         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3897             // if it goes to far to the right.. -> align left.
3898             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3899         }
3900         if(xy[0] < 0){
3901             // was left align - go right?
3902             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3903         }
3904         
3905         // goes down the bottom
3906         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907            xy[1]  < 0 ){
3908             var a = this.align.replace('?', '').split('-');
3909             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3910             
3911         }
3912         
3913         this.showAt(  xy , parentMenu, false);
3914     },
3915      /**
3916      * Displays this menu at a specific xy position
3917      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3918      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919      */
3920     showAt : function(xy, parentMenu, /* private: */_e){
3921         this.parentMenu = parentMenu;
3922         if(!this.el){
3923             this.render();
3924         }
3925         if(_e !== false){
3926             this.fireEvent("beforeshow", this);
3927             //xy = this.el.adjustForConstraints(xy);
3928         }
3929         
3930         //this.el.show();
3931         this.hideMenuItems();
3932         this.hidden = false;
3933         if (this.triggerEl) {
3934             this.triggerEl.addClass('open');
3935         }
3936         
3937         this.el.addClass('show');
3938         
3939         
3940         
3941         // reassign x when hitting right
3942         
3943         // reassign y when hitting bottom
3944         
3945         // but the list may align on trigger left or trigger top... should it be a properity?
3946         
3947         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3948             this.el.setXY(xy);
3949         }
3950         
3951         this.focus();
3952         this.fireEvent("show", this);
3953     },
3954     
3955     focus : function(){
3956         return;
3957         if(!this.hidden){
3958             this.doFocus.defer(50, this);
3959         }
3960     },
3961
3962     doFocus : function(){
3963         if(!this.hidden){
3964             this.focusEl.focus();
3965         }
3966     },
3967
3968     /**
3969      * Hides this menu and optionally all parent menus
3970      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971      */
3972     hide : function(deep)
3973     {
3974         if (false === this.fireEvent("beforehide", this)) {
3975             Roo.log("hide canceled");
3976             return;
3977         }
3978         this.hideMenuItems();
3979         if(this.el && this.isVisible()){
3980            
3981             if(this.activeItem){
3982                 this.activeItem.deactivate();
3983                 this.activeItem = null;
3984             }
3985             if (this.triggerEl) {
3986                 this.triggerEl.removeClass('open');
3987             }
3988             
3989             this.el.removeClass('show');
3990             this.hidden = true;
3991             this.fireEvent("hide", this);
3992         }
3993         if(deep === true && this.parentMenu){
3994             this.parentMenu.hide(true);
3995         }
3996     },
3997     
3998     onTriggerClick : function(e)
3999     {
4000         Roo.log('trigger click');
4001         
4002         var target = e.getTarget();
4003         
4004         Roo.log(target.nodeName.toLowerCase());
4005         
4006         if(target.nodeName.toLowerCase() === 'i'){
4007             e.preventDefault();
4008         }
4009         
4010     },
4011     
4012     onTriggerPress  : function(e)
4013     {
4014         Roo.log('trigger press');
4015         //Roo.log(e.getTarget());
4016        // Roo.log(this.triggerEl.dom);
4017        
4018         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4019         var pel = Roo.get(e.getTarget());
4020         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4021             Roo.log('is treeview or dropdown?');
4022             return;
4023         }
4024         
4025         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4026             return;
4027         }
4028         
4029         if (this.isVisible()) {
4030             Roo.log('hide');
4031             this.hide();
4032         } else {
4033             Roo.log('show');
4034             
4035             this.show(this.triggerEl, this.align, false);
4036         }
4037         
4038         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4039             e.stopEvent();
4040         }
4041         
4042     },
4043        
4044     
4045     hideMenuItems : function()
4046     {
4047         Roo.log("hide Menu Items");
4048         if (!this.el) { 
4049             return;
4050         }
4051         
4052         this.el.select('.open',true).each(function(aa) {
4053             
4054             aa.removeClass('open');
4055          
4056         });
4057     },
4058     addxtypeChild : function (tree, cntr) {
4059         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060           
4061         this.menuitems.add(comp);
4062         return comp;
4063
4064     },
4065     getEl : function()
4066     {
4067         Roo.log(this.el);
4068         return this.el;
4069     },
4070     
4071     clear : function()
4072     {
4073         this.getEl().dom.innerHTML = '';
4074         this.menuitems.clear();
4075     }
4076 });
4077
4078  
4079  /*
4080  * - LGPL
4081  *
4082  * menu item
4083  * 
4084  */
4085
4086
4087 /**
4088  * @class Roo.bootstrap.MenuItem
4089  * @extends Roo.bootstrap.Component
4090  * Bootstrap MenuItem class
4091  * @cfg {String} html the menu label
4092  * @cfg {String} href the link
4093  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4094  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4095  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4096  * @cfg {String} fa favicon to show on left of menu item.
4097  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4098  * 
4099  * 
4100  * @constructor
4101  * Create a new MenuItem
4102  * @param {Object} config The config object
4103  */
4104
4105
4106 Roo.bootstrap.MenuItem = function(config){
4107     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4108     this.addEvents({
4109         // raw events
4110         /**
4111          * @event click
4112          * The raw click event for the entire grid.
4113          * @param {Roo.bootstrap.MenuItem} this
4114          * @param {Roo.EventObject} e
4115          */
4116         "click" : true
4117     });
4118 };
4119
4120 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4121     
4122     href : false,
4123     html : false,
4124     preventDefault: false,
4125     isContainer : false,
4126     active : false,
4127     fa: false,
4128     
4129     getAutoCreate : function(){
4130         
4131         if(this.isContainer){
4132             return {
4133                 tag: 'li',
4134                 cls: 'dropdown-menu-item '
4135             };
4136         }
4137         var ctag = {
4138             tag: 'span',
4139             html: 'Link'
4140         };
4141         
4142         var anc = {
4143             tag : 'a',
4144             cls : 'dropdown-item',
4145             href : '#',
4146             cn : [  ]
4147         };
4148         
4149         if (this.fa !== false) {
4150             anc.cn.push({
4151                 tag : 'i',
4152                 cls : 'fa fa-' + this.fa
4153             });
4154         }
4155         
4156         anc.cn.push(ctag);
4157         
4158         
4159         var cfg= {
4160             tag: 'li',
4161             cls: 'dropdown-menu-item',
4162             cn: [ anc ]
4163         };
4164         if (this.parent().type == 'treeview') {
4165             cfg.cls = 'treeview-menu';
4166         }
4167         if (this.active) {
4168             cfg.cls += ' active';
4169         }
4170         
4171         
4172         
4173         anc.href = this.href || cfg.cn[0].href ;
4174         ctag.html = this.html || cfg.cn[0].html ;
4175         return cfg;
4176     },
4177     
4178     initEvents: function()
4179     {
4180         if (this.parent().type == 'treeview') {
4181             this.el.select('a').on('click', this.onClick, this);
4182         }
4183         
4184         if (this.menu) {
4185             this.menu.parentType = this.xtype;
4186             this.menu.triggerEl = this.el;
4187             this.menu = this.addxtype(Roo.apply({}, this.menu));
4188         }
4189         
4190     },
4191     onClick : function(e)
4192     {
4193         Roo.log('item on click ');
4194         
4195         if(this.preventDefault){
4196             e.preventDefault();
4197         }
4198         //this.parent().hideMenuItems();
4199         
4200         this.fireEvent('click', this, e);
4201     },
4202     getEl : function()
4203     {
4204         return this.el;
4205     } 
4206 });
4207
4208  
4209
4210  /*
4211  * - LGPL
4212  *
4213  * menu separator
4214  * 
4215  */
4216
4217
4218 /**
4219  * @class Roo.bootstrap.MenuSeparator
4220  * @extends Roo.bootstrap.Component
4221  * Bootstrap MenuSeparator class
4222  * 
4223  * @constructor
4224  * Create a new MenuItem
4225  * @param {Object} config The config object
4226  */
4227
4228
4229 Roo.bootstrap.MenuSeparator = function(config){
4230     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4231 };
4232
4233 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4234     
4235     getAutoCreate : function(){
4236         var cfg = {
4237             cls: 'divider',
4238             tag : 'li'
4239         };
4240         
4241         return cfg;
4242     }
4243    
4244 });
4245
4246  
4247
4248  
4249 /*
4250 * Licence: LGPL
4251 */
4252
4253 /**
4254  * @class Roo.bootstrap.Modal
4255  * @extends Roo.bootstrap.Component
4256  * Bootstrap Modal class
4257  * @cfg {String} title Title of dialog
4258  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4259  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4260  * @cfg {Boolean} specificTitle default false
4261  * @cfg {Array} buttons Array of buttons or standard button set..
4262  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4263  * @cfg {Boolean} animate default true
4264  * @cfg {Boolean} allow_close default true
4265  * @cfg {Boolean} fitwindow default false
4266  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4267  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4268  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4269  * @cfg {String} size (sm|lg|xl) default empty
4270  * @cfg {Number} max_width set the max width of modal
4271  * @cfg {Boolean} editableTitle can the title be edited
4272
4273  *
4274  *
4275  * @constructor
4276  * Create a new Modal Dialog
4277  * @param {Object} config The config object
4278  */
4279
4280 Roo.bootstrap.Modal = function(config){
4281     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4282     this.addEvents({
4283         // raw events
4284         /**
4285          * @event btnclick
4286          * The raw btnclick event for the button
4287          * @param {Roo.EventObject} e
4288          */
4289         "btnclick" : true,
4290         /**
4291          * @event resize
4292          * Fire when dialog resize
4293          * @param {Roo.bootstrap.Modal} this
4294          * @param {Roo.EventObject} e
4295          */
4296         "resize" : true,
4297         /**
4298          * @event titlechanged
4299          * Fire when the editable title has been changed
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} value
4302          */
4303         "titlechanged" : true 
4304         
4305     });
4306     this.buttons = this.buttons || [];
4307
4308     if (this.tmpl) {
4309         this.tmpl = Roo.factory(this.tmpl);
4310     }
4311
4312 };
4313
4314 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4315
4316     title : 'test dialog',
4317
4318     buttons : false,
4319
4320     // set on load...
4321
4322     html: false,
4323
4324     tmp: false,
4325
4326     specificTitle: false,
4327
4328     buttonPosition: 'right',
4329
4330     allow_close : true,
4331
4332     animate : true,
4333
4334     fitwindow: false,
4335     
4336      // private
4337     dialogEl: false,
4338     bodyEl:  false,
4339     footerEl:  false,
4340     titleEl:  false,
4341     closeEl:  false,
4342
4343     size: '',
4344     
4345     max_width: 0,
4346     
4347     max_height: 0,
4348     
4349     fit_content: false,
4350     editableTitle  : false,
4351
4352     onRender : function(ct, position)
4353     {
4354         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4355
4356         if(!this.el){
4357             var cfg = Roo.apply({},  this.getAutoCreate());
4358             cfg.id = Roo.id();
4359             //if(!cfg.name){
4360             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361             //}
4362             //if (!cfg.name.length) {
4363             //    delete cfg.name;
4364            // }
4365             if (this.cls) {
4366                 cfg.cls += ' ' + this.cls;
4367             }
4368             if (this.style) {
4369                 cfg.style = this.style;
4370             }
4371             this.el = Roo.get(document.body).createChild(cfg, position);
4372         }
4373         //var type = this.el.dom.type;
4374
4375
4376         if(this.tabIndex !== undefined){
4377             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4378         }
4379
4380         this.dialogEl = this.el.select('.modal-dialog',true).first();
4381         this.bodyEl = this.el.select('.modal-body',true).first();
4382         this.closeEl = this.el.select('.modal-header .close', true).first();
4383         this.headerEl = this.el.select('.modal-header',true).first();
4384         this.titleEl = this.el.select('.modal-title',true).first();
4385         this.footerEl = this.el.select('.modal-footer',true).first();
4386
4387         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388         
4389         //this.el.addClass("x-dlg-modal");
4390
4391         if (this.buttons.length) {
4392             Roo.each(this.buttons, function(bb) {
4393                 var b = Roo.apply({}, bb);
4394                 b.xns = b.xns || Roo.bootstrap;
4395                 b.xtype = b.xtype || 'Button';
4396                 if (typeof(b.listeners) == 'undefined') {
4397                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4398                 }
4399
4400                 var btn = Roo.factory(b);
4401
4402                 btn.render(this.getButtonContainer());
4403
4404             },this);
4405         }
4406         // render the children.
4407         var nitems = [];
4408
4409         if(typeof(this.items) != 'undefined'){
4410             var items = this.items;
4411             delete this.items;
4412
4413             for(var i =0;i < items.length;i++) {
4414                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4415             }
4416         }
4417
4418         this.items = nitems;
4419
4420         // where are these used - they used to be body/close/footer
4421
4422
4423         this.initEvents();
4424         //this.el.addClass([this.fieldClass, this.cls]);
4425
4426     },
4427
4428     getAutoCreate : function()
4429     {
4430         // we will default to modal-body-overflow - might need to remove or make optional later.
4431         var bdy = {
4432                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4433                 html : this.html || ''
4434         };
4435
4436         var title = {
4437             tag: 'h5',
4438             cls : 'modal-title',
4439             html : this.title
4440         };
4441
4442         if(this.specificTitle){ // WTF is this?
4443             title = this.title;
4444         }
4445
4446         var header = [];
4447         if (this.allow_close && Roo.bootstrap.version == 3) {
4448             header.push({
4449                 tag: 'button',
4450                 cls : 'close',
4451                 html : '&times'
4452             });
4453         }
4454
4455         header.push(title);
4456
4457         if (this.editableTitle) {
4458             header.push({
4459                 cls: 'form-control roo-editable-title d-none',
4460                 tag: 'input',
4461                 type: 'text'
4462             });
4463         }
4464         
4465         if (this.allow_close && Roo.bootstrap.version == 4) {
4466             header.push({
4467                 tag: 'button',
4468                 cls : 'close',
4469                 html : '&times'
4470             });
4471         }
4472         
4473         var size = '';
4474
4475         if(this.size.length){
4476             size = 'modal-' + this.size;
4477         }
4478         
4479         var footer = Roo.bootstrap.version == 3 ?
4480             {
4481                 cls : 'modal-footer',
4482                 cn : [
4483                     {
4484                         tag: 'div',
4485                         cls: 'btn-' + this.buttonPosition
4486                     }
4487                 ]
4488
4489             } :
4490             {  // BS4 uses mr-auto on left buttons....
4491                 cls : 'modal-footer'
4492             };
4493
4494             
4495
4496         
4497         
4498         var modal = {
4499             cls: "modal",
4500              cn : [
4501                 {
4502                     cls: "modal-dialog " + size,
4503                     cn : [
4504                         {
4505                             cls : "modal-content",
4506                             cn : [
4507                                 {
4508                                     cls : 'modal-header',
4509                                     cn : header
4510                                 },
4511                                 bdy,
4512                                 footer
4513                             ]
4514
4515                         }
4516                     ]
4517
4518                 }
4519             ]
4520         };
4521
4522         if(this.animate){
4523             modal.cls += ' fade';
4524         }
4525
4526         return modal;
4527
4528     },
4529     getChildContainer : function() {
4530
4531          return this.bodyEl;
4532
4533     },
4534     getButtonContainer : function() {
4535         
4536          return Roo.bootstrap.version == 4 ?
4537             this.el.select('.modal-footer',true).first()
4538             : this.el.select('.modal-footer div',true).first();
4539
4540     },
4541     initEvents : function()
4542     {
4543         if (this.allow_close) {
4544             this.closeEl.on('click', this.hide, this);
4545         }
4546         Roo.EventManager.onWindowResize(this.resize, this, true);
4547         if (this.editableTitle) {
4548             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4549             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4550             this.headerEditEl.on('keyup', function(e) {
4551                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4552                         this.toggleHeaderInput(false)
4553                     }
4554                 }, this);
4555             this.headerEditEl.on('blur', function(e) {
4556                 this.toggleHeaderInput(false)
4557             },this);
4558         }
4559
4560     },
4561   
4562
4563     resize : function()
4564     {
4565         this.maskEl.setSize(
4566             Roo.lib.Dom.getViewWidth(true),
4567             Roo.lib.Dom.getViewHeight(true)
4568         );
4569         
4570         if (this.fitwindow) {
4571             
4572            this.dialogEl.setStyle( { 'max-width' : '100%' });
4573             this.setSize(
4574                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4575                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4576             );
4577             return;
4578         }
4579         
4580         if(this.max_width !== 0) {
4581             
4582             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4583             
4584             if(this.height) {
4585                 this.setSize(w, this.height);
4586                 return;
4587             }
4588             
4589             if(this.max_height) {
4590                 this.setSize(w,Math.min(
4591                     this.max_height,
4592                     Roo.lib.Dom.getViewportHeight(true) - 60
4593                 ));
4594                 
4595                 return;
4596             }
4597             
4598             if(!this.fit_content) {
4599                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4600                 return;
4601             }
4602             
4603             this.setSize(w, Math.min(
4604                 60 +
4605                 this.headerEl.getHeight() + 
4606                 this.footerEl.getHeight() + 
4607                 this.getChildHeight(this.bodyEl.dom.childNodes),
4608                 Roo.lib.Dom.getViewportHeight(true) - 60)
4609             );
4610         }
4611         
4612     },
4613
4614     setSize : function(w,h)
4615     {
4616         if (!w && !h) {
4617             return;
4618         }
4619         
4620         this.resizeTo(w,h);
4621     },
4622
4623     show : function() {
4624
4625         if (!this.rendered) {
4626             this.render();
4627         }
4628         this.toggleHeaderInput(false);
4629         //this.el.setStyle('display', 'block');
4630         this.el.removeClass('hideing');
4631         this.el.dom.style.display='block';
4632         
4633         Roo.get(document.body).addClass('modal-open');
4634  
4635         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4636             
4637             (function(){
4638                 this.el.addClass('show');
4639                 this.el.addClass('in');
4640             }).defer(50, this);
4641         }else{
4642             this.el.addClass('show');
4643             this.el.addClass('in');
4644         }
4645
4646         // not sure how we can show data in here..
4647         //if (this.tmpl) {
4648         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4649         //}
4650
4651         Roo.get(document.body).addClass("x-body-masked");
4652         
4653         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4654         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4655         this.maskEl.dom.style.display = 'block';
4656         this.maskEl.addClass('show');
4657         
4658         
4659         this.resize();
4660         
4661         this.fireEvent('show', this);
4662
4663         // set zindex here - otherwise it appears to be ignored...
4664         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4665
4666         (function () {
4667             this.items.forEach( function(e) {
4668                 e.layout ? e.layout() : false;
4669
4670             });
4671         }).defer(100,this);
4672
4673     },
4674     hide : function()
4675     {
4676         if(this.fireEvent("beforehide", this) !== false){
4677             
4678             this.maskEl.removeClass('show');
4679             
4680             this.maskEl.dom.style.display = '';
4681             Roo.get(document.body).removeClass("x-body-masked");
4682             this.el.removeClass('in');
4683             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684
4685             if(this.animate){ // why
4686                 this.el.addClass('hideing');
4687                 this.el.removeClass('show');
4688                 (function(){
4689                     if (!this.el.hasClass('hideing')) {
4690                         return; // it's been shown again...
4691                     }
4692                     
4693                     this.el.dom.style.display='';
4694
4695                     Roo.get(document.body).removeClass('modal-open');
4696                     this.el.removeClass('hideing');
4697                 }).defer(150,this);
4698                 
4699             }else{
4700                 this.el.removeClass('show');
4701                 this.el.dom.style.display='';
4702                 Roo.get(document.body).removeClass('modal-open');
4703
4704             }
4705             this.fireEvent('hide', this);
4706         }
4707     },
4708     isVisible : function()
4709     {
4710         
4711         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4712         
4713     },
4714
4715     addButton : function(str, cb)
4716     {
4717
4718
4719         var b = Roo.apply({}, { html : str } );
4720         b.xns = b.xns || Roo.bootstrap;
4721         b.xtype = b.xtype || 'Button';
4722         if (typeof(b.listeners) == 'undefined') {
4723             b.listeners = { click : cb.createDelegate(this)  };
4724         }
4725
4726         var btn = Roo.factory(b);
4727
4728         btn.render(this.getButtonContainer());
4729
4730         return btn;
4731
4732     },
4733
4734     setDefaultButton : function(btn)
4735     {
4736         //this.el.select('.modal-footer').()
4737     },
4738
4739     resizeTo: function(w,h)
4740     {
4741         this.dialogEl.setWidth(w);
4742         
4743         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4744
4745         this.bodyEl.setHeight(h - diff);
4746         
4747         this.fireEvent('resize', this);
4748     },
4749     
4750     setContentSize  : function(w, h)
4751     {
4752
4753     },
4754     onButtonClick: function(btn,e)
4755     {
4756         //Roo.log([a,b,c]);
4757         this.fireEvent('btnclick', btn.name, e);
4758     },
4759      /**
4760      * Set the title of the Dialog
4761      * @param {String} str new Title
4762      */
4763     setTitle: function(str) {
4764         this.titleEl.dom.innerHTML = str;
4765         this.title = str;
4766     },
4767     /**
4768      * Set the body of the Dialog
4769      * @param {String} str new Title
4770      */
4771     setBody: function(str) {
4772         this.bodyEl.dom.innerHTML = str;
4773     },
4774     /**
4775      * Set the body of the Dialog using the template
4776      * @param {Obj} data - apply this data to the template and replace the body contents.
4777      */
4778     applyBody: function(obj)
4779     {
4780         if (!this.tmpl) {
4781             Roo.log("Error - using apply Body without a template");
4782             //code
4783         }
4784         this.tmpl.overwrite(this.bodyEl, obj);
4785     },
4786     
4787     getChildHeight : function(child_nodes)
4788     {
4789         if(
4790             !child_nodes ||
4791             child_nodes.length == 0
4792         ) {
4793             return 0;
4794         }
4795         
4796         var child_height = 0;
4797         
4798         for(var i = 0; i < child_nodes.length; i++) {
4799             
4800             /*
4801             * for modal with tabs...
4802             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803                 
4804                 var layout_childs = child_nodes[i].childNodes;
4805                 
4806                 for(var j = 0; j < layout_childs.length; j++) {
4807                     
4808                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809                         
4810                         var layout_body_childs = layout_childs[j].childNodes;
4811                         
4812                         for(var k = 0; k < layout_body_childs.length; k++) {
4813                             
4814                             if(layout_body_childs[k].classList.contains('navbar')) {
4815                                 child_height += layout_body_childs[k].offsetHeight;
4816                                 continue;
4817                             }
4818                             
4819                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820                                 
4821                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822                                 
4823                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824                                     
4825                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4826                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4827                                         continue;
4828                                     }
4829                                     
4830                                 }
4831                                 
4832                             }
4833                             
4834                         }
4835                     }
4836                 }
4837                 continue;
4838             }
4839             */
4840             
4841             child_height += child_nodes[i].offsetHeight;
4842             // Roo.log(child_nodes[i].offsetHeight);
4843         }
4844         
4845         return child_height;
4846     },
4847     toggleHeaderInput : function(is_edit)
4848     {
4849         if (!this.editableTitle) {
4850             return; // not editable.
4851         }
4852         if (is_edit && this.is_header_editing) {
4853             return; // already editing..
4854         }
4855         if (is_edit) {
4856     
4857             this.headerEditEl.dom.value = this.title;
4858             this.headerEditEl.removeClass('d-none');
4859             this.headerEditEl.dom.focus();
4860             this.titleEl.addClass('d-none');
4861             
4862             this.is_header_editing = true;
4863             return
4864         }
4865         // flip back to not editing.
4866         this.title = this.headerEditEl.dom.value;
4867         this.headerEditEl.addClass('d-none');
4868         this.titleEl.removeClass('d-none');
4869         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4870         this.is_header_editing = false;
4871         this.fireEvent('titlechanged', this, this.title);
4872     
4873             
4874         
4875     }
4876
4877 });
4878
4879
4880 Roo.apply(Roo.bootstrap.Modal,  {
4881     /**
4882          * Button config that displays a single OK button
4883          * @type Object
4884          */
4885         OK :  [{
4886             name : 'ok',
4887             weight : 'primary',
4888             html : 'OK'
4889         }],
4890         /**
4891          * Button config that displays Yes and No buttons
4892          * @type Object
4893          */
4894         YESNO : [
4895             {
4896                 name  : 'no',
4897                 html : 'No'
4898             },
4899             {
4900                 name  :'yes',
4901                 weight : 'primary',
4902                 html : 'Yes'
4903             }
4904         ],
4905
4906         /**
4907          * Button config that displays OK and Cancel buttons
4908          * @type Object
4909          */
4910         OKCANCEL : [
4911             {
4912                name : 'cancel',
4913                 html : 'Cancel'
4914             },
4915             {
4916                 name : 'ok',
4917                 weight : 'primary',
4918                 html : 'OK'
4919             }
4920         ],
4921         /**
4922          * Button config that displays Yes, No and Cancel buttons
4923          * @type Object
4924          */
4925         YESNOCANCEL : [
4926             {
4927                 name : 'yes',
4928                 weight : 'primary',
4929                 html : 'Yes'
4930             },
4931             {
4932                 name : 'no',
4933                 html : 'No'
4934             },
4935             {
4936                 name : 'cancel',
4937                 html : 'Cancel'
4938             }
4939         ],
4940         
4941         zIndex : 10001
4942 });
4943
4944 /*
4945  * - LGPL
4946  *
4947  * messagebox - can be used as a replace
4948  * 
4949  */
4950 /**
4951  * @class Roo.MessageBox
4952  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4953  * Example usage:
4954  *<pre><code>
4955 // Basic alert:
4956 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957
4958 // Prompt for user data:
4959 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960     if (btn == 'ok'){
4961         // process text value...
4962     }
4963 });
4964
4965 // Show a dialog using config options:
4966 Roo.Msg.show({
4967    title:'Save Changes?',
4968    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4969    buttons: Roo.Msg.YESNOCANCEL,
4970    fn: processResult,
4971    animEl: 'elId'
4972 });
4973 </code></pre>
4974  * @singleton
4975  */
4976 Roo.bootstrap.MessageBox = function(){
4977     var dlg, opt, mask, waitTimer;
4978     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4979     var buttons, activeTextEl, bwidth;
4980
4981     
4982     // private
4983     var handleButton = function(button){
4984         dlg.hide();
4985         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4986     };
4987
4988     // private
4989     var handleHide = function(){
4990         if(opt && opt.cls){
4991             dlg.el.removeClass(opt.cls);
4992         }
4993         //if(waitTimer){
4994         //    Roo.TaskMgr.stop(waitTimer);
4995         //    waitTimer = null;
4996         //}
4997     };
4998
4999     // private
5000     var updateButtons = function(b){
5001         var width = 0;
5002         if(!b){
5003             buttons["ok"].hide();
5004             buttons["cancel"].hide();
5005             buttons["yes"].hide();
5006             buttons["no"].hide();
5007             dlg.footerEl.hide();
5008             
5009             return width;
5010         }
5011         dlg.footerEl.show();
5012         for(var k in buttons){
5013             if(typeof buttons[k] != "function"){
5014                 if(b[k]){
5015                     buttons[k].show();
5016                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5017                     width += buttons[k].el.getWidth()+15;
5018                 }else{
5019                     buttons[k].hide();
5020                 }
5021             }
5022         }
5023         return width;
5024     };
5025
5026     // private
5027     var handleEsc = function(d, k, e){
5028         if(opt && opt.closable !== false){
5029             dlg.hide();
5030         }
5031         if(e){
5032             e.stopEvent();
5033         }
5034     };
5035
5036     return {
5037         /**
5038          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5039          * @return {Roo.BasicDialog} The BasicDialog element
5040          */
5041         getDialog : function(){
5042            if(!dlg){
5043                 dlg = new Roo.bootstrap.Modal( {
5044                     //draggable: true,
5045                     //resizable:false,
5046                     //constraintoviewport:false,
5047                     //fixedcenter:true,
5048                     //collapsible : false,
5049                     //shim:true,
5050                     //modal: true,
5051                 //    width: 'auto',
5052                   //  height:100,
5053                     //buttonAlign:"center",
5054                     closeClick : function(){
5055                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5056                             handleButton("no");
5057                         }else{
5058                             handleButton("cancel");
5059                         }
5060                     }
5061                 });
5062                 dlg.render();
5063                 dlg.on("hide", handleHide);
5064                 mask = dlg.mask;
5065                 //dlg.addKeyListener(27, handleEsc);
5066                 buttons = {};
5067                 this.buttons = buttons;
5068                 var bt = this.buttonText;
5069                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5070                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5071                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5072                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073                 //Roo.log(buttons);
5074                 bodyEl = dlg.bodyEl.createChild({
5075
5076                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5077                         '<textarea class="roo-mb-textarea"></textarea>' +
5078                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5079                 });
5080                 msgEl = bodyEl.dom.firstChild;
5081                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5082                 textboxEl.enableDisplayMode();
5083                 textboxEl.addKeyListener([10,13], function(){
5084                     if(dlg.isVisible() && opt && opt.buttons){
5085                         if(opt.buttons.ok){
5086                             handleButton("ok");
5087                         }else if(opt.buttons.yes){
5088                             handleButton("yes");
5089                         }
5090                     }
5091                 });
5092                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5093                 textareaEl.enableDisplayMode();
5094                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5095                 progressEl.enableDisplayMode();
5096                 
5097                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5098                 var pf = progressEl.dom.firstChild;
5099                 if (pf) {
5100                     pp = Roo.get(pf.firstChild);
5101                     pp.setHeight(pf.offsetHeight);
5102                 }
5103                 
5104             }
5105             return dlg;
5106         },
5107
5108         /**
5109          * Updates the message box body text
5110          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5111          * the XHTML-compliant non-breaking space character '&amp;#160;')
5112          * @return {Roo.MessageBox} This message box
5113          */
5114         updateText : function(text)
5115         {
5116             if(!dlg.isVisible() && !opt.width){
5117                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5118                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119             }
5120             msgEl.innerHTML = text || '&#160;';
5121       
5122             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5123             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124             var w = Math.max(
5125                     Math.min(opt.width || cw , this.maxWidth), 
5126                     Math.max(opt.minWidth || this.minWidth, bwidth)
5127             );
5128             if(opt.prompt){
5129                 activeTextEl.setWidth(w);
5130             }
5131             if(dlg.isVisible()){
5132                 dlg.fixedcenter = false;
5133             }
5134             // to big, make it scroll. = But as usual stupid IE does not support
5135             // !important..
5136             
5137             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5138                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5139                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140             } else {
5141                 bodyEl.dom.style.height = '';
5142                 bodyEl.dom.style.overflowY = '';
5143             }
5144             if (cw > w) {
5145                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146             } else {
5147                 bodyEl.dom.style.overflowX = '';
5148             }
5149             
5150             dlg.setContentSize(w, bodyEl.getHeight());
5151             if(dlg.isVisible()){
5152                 dlg.fixedcenter = true;
5153             }
5154             return this;
5155         },
5156
5157         /**
5158          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5159          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5160          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5161          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5162          * @return {Roo.MessageBox} This message box
5163          */
5164         updateProgress : function(value, text){
5165             if(text){
5166                 this.updateText(text);
5167             }
5168             
5169             if (pp) { // weird bug on my firefox - for some reason this is not defined
5170                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5171                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5172             }
5173             return this;
5174         },        
5175
5176         /**
5177          * Returns true if the message box is currently displayed
5178          * @return {Boolean} True if the message box is visible, else false
5179          */
5180         isVisible : function(){
5181             return dlg && dlg.isVisible();  
5182         },
5183
5184         /**
5185          * Hides the message box if it is displayed
5186          */
5187         hide : function(){
5188             if(this.isVisible()){
5189                 dlg.hide();
5190             }  
5191         },
5192
5193         /**
5194          * Displays a new message box, or reinitializes an existing message box, based on the config options
5195          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5196          * The following config object properties are supported:
5197          * <pre>
5198 Property    Type             Description
5199 ----------  ---------------  ------------------------------------------------------------------------------------
5200 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5201                                    closes (defaults to undefined)
5202 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5203                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5204 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5205                                    progress and wait dialogs will ignore this property and always hide the
5206                                    close button as they can only be closed programmatically.
5207 cls               String           A custom CSS class to apply to the message box element
5208 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5209                                    displayed (defaults to 75)
5210 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5211                                    function will be btn (the name of the button that was clicked, if applicable,
5212                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5213                                    Progress and wait dialogs will ignore this option since they do not respond to
5214                                    user actions and can only be closed programmatically, so any required function
5215                                    should be called by the same code after it closes the dialog.
5216 icon              String           A CSS class that provides a background image to be used as an icon for
5217                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5218 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5219 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5220 modal             Boolean          False to allow user interaction with the page while the message box is
5221                                    displayed (defaults to true)
5222 msg               String           A string that will replace the existing message box body text (defaults
5223                                    to the XHTML-compliant non-breaking space character '&#160;')
5224 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5225 progress          Boolean          True to display a progress bar (defaults to false)
5226 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5227 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5228 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5229 title             String           The title text
5230 value             String           The string value to set into the active textbox element if displayed
5231 wait              Boolean          True to display a progress bar (defaults to false)
5232 width             Number           The width of the dialog in pixels
5233 </pre>
5234          *
5235          * Example usage:
5236          * <pre><code>
5237 Roo.Msg.show({
5238    title: 'Address',
5239    msg: 'Please enter your address:',
5240    width: 300,
5241    buttons: Roo.MessageBox.OKCANCEL,
5242    multiline: true,
5243    fn: saveAddress,
5244    animEl: 'addAddressBtn'
5245 });
5246 </code></pre>
5247          * @param {Object} config Configuration options
5248          * @return {Roo.MessageBox} This message box
5249          */
5250         show : function(options)
5251         {
5252             
5253             // this causes nightmares if you show one dialog after another
5254             // especially on callbacks..
5255              
5256             if(this.isVisible()){
5257                 
5258                 this.hide();
5259                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5260                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5261                 Roo.log("New Dialog Message:" +  options.msg )
5262                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5263                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5264                 
5265             }
5266             var d = this.getDialog();
5267             opt = options;
5268             d.setTitle(opt.title || "&#160;");
5269             d.closeEl.setDisplayed(opt.closable !== false);
5270             activeTextEl = textboxEl;
5271             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5272             if(opt.prompt){
5273                 if(opt.multiline){
5274                     textboxEl.hide();
5275                     textareaEl.show();
5276                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5277                         opt.multiline : this.defaultTextHeight);
5278                     activeTextEl = textareaEl;
5279                 }else{
5280                     textboxEl.show();
5281                     textareaEl.hide();
5282                 }
5283             }else{
5284                 textboxEl.hide();
5285                 textareaEl.hide();
5286             }
5287             progressEl.setDisplayed(opt.progress === true);
5288             if (opt.progress) {
5289                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290             }
5291             this.updateProgress(0);
5292             activeTextEl.dom.value = opt.value || "";
5293             if(opt.prompt){
5294                 dlg.setDefaultButton(activeTextEl);
5295             }else{
5296                 var bs = opt.buttons;
5297                 var db = null;
5298                 if(bs && bs.ok){
5299                     db = buttons["ok"];
5300                 }else if(bs && bs.yes){
5301                     db = buttons["yes"];
5302                 }
5303                 dlg.setDefaultButton(db);
5304             }
5305             bwidth = updateButtons(opt.buttons);
5306             this.updateText(opt.msg);
5307             if(opt.cls){
5308                 d.el.addClass(opt.cls);
5309             }
5310             d.proxyDrag = opt.proxyDrag === true;
5311             d.modal = opt.modal !== false;
5312             d.mask = opt.modal !== false ? mask : false;
5313             if(!d.isVisible()){
5314                 // force it to the end of the z-index stack so it gets a cursor in FF
5315                 document.body.appendChild(dlg.el.dom);
5316                 d.animateTarget = null;
5317                 d.show(options.animEl);
5318             }
5319             return this;
5320         },
5321
5322         /**
5323          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5324          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5325          * and closing the message box when the process is complete.
5326          * @param {String} title The title bar text
5327          * @param {String} msg The message box body text
5328          * @return {Roo.MessageBox} This message box
5329          */
5330         progress : function(title, msg){
5331             this.show({
5332                 title : title,
5333                 msg : msg,
5334                 buttons: false,
5335                 progress:true,
5336                 closable:false,
5337                 minWidth: this.minProgressWidth,
5338                 modal : true
5339             });
5340             return this;
5341         },
5342
5343         /**
5344          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5345          * If a callback function is passed it will be called after the user clicks the button, and the
5346          * id of the button that was clicked will be passed as the only parameter to the callback
5347          * (could also be the top-right close button).
5348          * @param {String} title The title bar text
5349          * @param {String} msg The message box body text
5350          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5351          * @param {Object} scope (optional) The scope of the callback function
5352          * @return {Roo.MessageBox} This message box
5353          */
5354         alert : function(title, msg, fn, scope)
5355         {
5356             this.show({
5357                 title : title,
5358                 msg : msg,
5359                 buttons: this.OK,
5360                 fn: fn,
5361                 closable : false,
5362                 scope : scope,
5363                 modal : true
5364             });
5365             return this;
5366         },
5367
5368         /**
5369          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5370          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5371          * You are responsible for closing the message box when the process is complete.
5372          * @param {String} msg The message box body text
5373          * @param {String} title (optional) The title bar text
5374          * @return {Roo.MessageBox} This message box
5375          */
5376         wait : function(msg, title){
5377             this.show({
5378                 title : title,
5379                 msg : msg,
5380                 buttons: false,
5381                 closable:false,
5382                 progress:true,
5383                 modal:true,
5384                 width:300,
5385                 wait:true
5386             });
5387             waitTimer = Roo.TaskMgr.start({
5388                 run: function(i){
5389                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5390                 },
5391                 interval: 1000
5392             });
5393             return this;
5394         },
5395
5396         /**
5397          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5398          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5399          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5400          * @param {String} title The title bar text
5401          * @param {String} msg The message box body text
5402          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5403          * @param {Object} scope (optional) The scope of the callback function
5404          * @return {Roo.MessageBox} This message box
5405          */
5406         confirm : function(title, msg, fn, scope){
5407             this.show({
5408                 title : title,
5409                 msg : msg,
5410                 buttons: this.YESNO,
5411                 fn: fn,
5412                 scope : scope,
5413                 modal : true
5414             });
5415             return this;
5416         },
5417
5418         /**
5419          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5420          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5421          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5422          * (could also be the top-right close button) and the text that was entered will be passed as the two
5423          * parameters to the callback.
5424          * @param {String} title The title bar text
5425          * @param {String} msg The message box body text
5426          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5427          * @param {Object} scope (optional) The scope of the callback function
5428          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5429          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5430          * @return {Roo.MessageBox} This message box
5431          */
5432         prompt : function(title, msg, fn, scope, multiline){
5433             this.show({
5434                 title : title,
5435                 msg : msg,
5436                 buttons: this.OKCANCEL,
5437                 fn: fn,
5438                 minWidth:250,
5439                 scope : scope,
5440                 prompt:true,
5441                 multiline: multiline,
5442                 modal : true
5443             });
5444             return this;
5445         },
5446
5447         /**
5448          * Button config that displays a single OK button
5449          * @type Object
5450          */
5451         OK : {ok:true},
5452         /**
5453          * Button config that displays Yes and No buttons
5454          * @type Object
5455          */
5456         YESNO : {yes:true, no:true},
5457         /**
5458          * Button config that displays OK and Cancel buttons
5459          * @type Object
5460          */
5461         OKCANCEL : {ok:true, cancel:true},
5462         /**
5463          * Button config that displays Yes, No and Cancel buttons
5464          * @type Object
5465          */
5466         YESNOCANCEL : {yes:true, no:true, cancel:true},
5467
5468         /**
5469          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5470          * @type Number
5471          */
5472         defaultTextHeight : 75,
5473         /**
5474          * The maximum width in pixels of the message box (defaults to 600)
5475          * @type Number
5476          */
5477         maxWidth : 600,
5478         /**
5479          * The minimum width in pixels of the message box (defaults to 100)
5480          * @type Number
5481          */
5482         minWidth : 100,
5483         /**
5484          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5485          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5486          * @type Number
5487          */
5488         minProgressWidth : 250,
5489         /**
5490          * An object containing the default button text strings that can be overriden for localized language support.
5491          * Supported properties are: ok, cancel, yes and no.
5492          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5493          * @type Object
5494          */
5495         buttonText : {
5496             ok : "OK",
5497             cancel : "Cancel",
5498             yes : "Yes",
5499             no : "No"
5500         }
5501     };
5502 }();
5503
5504 /**
5505  * Shorthand for {@link Roo.MessageBox}
5506  */
5507 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5508 Roo.Msg = Roo.Msg || Roo.MessageBox;
5509 /*
5510  * - LGPL
5511  *
5512  * navbar
5513  * 
5514  */
5515
5516 /**
5517  * @class Roo.bootstrap.Navbar
5518  * @extends Roo.bootstrap.Component
5519  * Bootstrap Navbar class
5520
5521  * @constructor
5522  * Create a new Navbar
5523  * @param {Object} config The config object
5524  */
5525
5526
5527 Roo.bootstrap.Navbar = function(config){
5528     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5529     this.addEvents({
5530         // raw events
5531         /**
5532          * @event beforetoggle
5533          * Fire before toggle the menu
5534          * @param {Roo.EventObject} e
5535          */
5536         "beforetoggle" : true
5537     });
5538 };
5539
5540 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5541     
5542     
5543    
5544     // private
5545     navItems : false,
5546     loadMask : false,
5547     
5548     
5549     getAutoCreate : function(){
5550         
5551         
5552         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5553         
5554     },
5555     
5556     initEvents :function ()
5557     {
5558         //Roo.log(this.el.select('.navbar-toggle',true));
5559         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5560         
5561         var mark = {
5562             tag: "div",
5563             cls:"x-dlg-mask"
5564         };
5565         
5566         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567         
5568         var size = this.el.getSize();
5569         this.maskEl.setSize(size.width, size.height);
5570         this.maskEl.enableDisplayMode("block");
5571         this.maskEl.hide();
5572         
5573         if(this.loadMask){
5574             this.maskEl.show();
5575         }
5576     },
5577     
5578     
5579     getChildContainer : function()
5580     {
5581         if (this.el && this.el.select('.collapse').getCount()) {
5582             return this.el.select('.collapse',true).first();
5583         }
5584         
5585         return this.el;
5586     },
5587     
5588     mask : function()
5589     {
5590         this.maskEl.show();
5591     },
5592     
5593     unmask : function()
5594     {
5595         this.maskEl.hide();
5596     },
5597     onToggle : function()
5598     {
5599         
5600         if(this.fireEvent('beforetoggle', this) === false){
5601             return;
5602         }
5603         var ce = this.el.select('.navbar-collapse',true).first();
5604       
5605         if (!ce.hasClass('show')) {
5606            this.expand();
5607         } else {
5608             this.collapse();
5609         }
5610         
5611         
5612     
5613     },
5614     /**
5615      * Expand the navbar pulldown 
5616      */
5617     expand : function ()
5618     {
5619        
5620         var ce = this.el.select('.navbar-collapse',true).first();
5621         if (ce.hasClass('collapsing')) {
5622             return;
5623         }
5624         ce.dom.style.height = '';
5625                // show it...
5626         ce.addClass('in'); // old...
5627         ce.removeClass('collapse');
5628         ce.addClass('show');
5629         var h = ce.getHeight();
5630         Roo.log(h);
5631         ce.removeClass('show');
5632         // at this point we should be able to see it..
5633         ce.addClass('collapsing');
5634         
5635         ce.setHeight(0); // resize it ...
5636         ce.on('transitionend', function() {
5637             //Roo.log('done transition');
5638             ce.removeClass('collapsing');
5639             ce.addClass('show');
5640             ce.removeClass('collapse');
5641
5642             ce.dom.style.height = '';
5643         }, this, { single: true} );
5644         ce.setHeight(h);
5645         ce.dom.scrollTop = 0;
5646     },
5647     /**
5648      * Collapse the navbar pulldown 
5649      */
5650     collapse : function()
5651     {
5652          var ce = this.el.select('.navbar-collapse',true).first();
5653        
5654         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5655             // it's collapsed or collapsing..
5656             return;
5657         }
5658         ce.removeClass('in'); // old...
5659         ce.setHeight(ce.getHeight());
5660         ce.removeClass('show');
5661         ce.addClass('collapsing');
5662         
5663         ce.on('transitionend', function() {
5664             ce.dom.style.height = '';
5665             ce.removeClass('collapsing');
5666             ce.addClass('collapse');
5667         }, this, { single: true} );
5668         ce.setHeight(0);
5669     }
5670     
5671     
5672     
5673 });
5674
5675
5676
5677  
5678
5679  /*
5680  * - LGPL
5681  *
5682  * navbar
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.NavSimplebar
5688  * @extends Roo.bootstrap.Navbar
5689  * Bootstrap Sidebar class
5690  *
5691  * @cfg {Boolean} inverse is inverted color
5692  * 
5693  * @cfg {String} type (nav | pills | tabs)
5694  * @cfg {Boolean} arrangement stacked | justified
5695  * @cfg {String} align (left | right) alignment
5696  * 
5697  * @cfg {Boolean} main (true|false) main nav bar? default false
5698  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699  * 
5700  * @cfg {String} tag (header|footer|nav|div) default is nav 
5701
5702  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5703  * 
5704  * 
5705  * @constructor
5706  * Create a new Sidebar
5707  * @param {Object} config The config object
5708  */
5709
5710
5711 Roo.bootstrap.NavSimplebar = function(config){
5712     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5713 };
5714
5715 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5716     
5717     inverse: false,
5718     
5719     type: false,
5720     arrangement: '',
5721     align : false,
5722     
5723     weight : 'light',
5724     
5725     main : false,
5726     
5727     
5728     tag : false,
5729     
5730     
5731     getAutoCreate : function(){
5732         
5733         
5734         var cfg = {
5735             tag : this.tag || 'div',
5736             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737         };
5738         if (['light','white'].indexOf(this.weight) > -1) {
5739             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740         }
5741         cfg.cls += ' bg-' + this.weight;
5742         
5743         if (this.inverse) {
5744             cfg.cls += ' navbar-inverse';
5745             
5746         }
5747         
5748         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749         
5750         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5751             return cfg;
5752         }
5753         
5754         
5755     
5756         
5757         cfg.cn = [
5758             {
5759                 cls: 'nav nav-' + this.xtype,
5760                 tag : 'ul'
5761             }
5762         ];
5763         
5764          
5765         this.type = this.type || 'nav';
5766         if (['tabs','pills'].indexOf(this.type) != -1) {
5767             cfg.cn[0].cls += ' nav-' + this.type
5768         
5769         
5770         } else {
5771             if (this.type!=='nav') {
5772                 Roo.log('nav type must be nav/tabs/pills')
5773             }
5774             cfg.cn[0].cls += ' navbar-nav'
5775         }
5776         
5777         
5778         
5779         
5780         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5781             cfg.cn[0].cls += ' nav-' + this.arrangement;
5782         }
5783         
5784         
5785         if (this.align === 'right') {
5786             cfg.cn[0].cls += ' navbar-right';
5787         }
5788         
5789         
5790         
5791         
5792         return cfg;
5793     
5794         
5795     }
5796     
5797     
5798     
5799 });
5800
5801
5802
5803  
5804
5805  
5806        /*
5807  * - LGPL
5808  *
5809  * navbar
5810  * navbar-fixed-top
5811  * navbar-expand-md  fixed-top 
5812  */
5813
5814 /**
5815  * @class Roo.bootstrap.NavHeaderbar
5816  * @extends Roo.bootstrap.NavSimplebar
5817  * Bootstrap Sidebar class
5818  *
5819  * @cfg {String} brand what is brand
5820  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5821  * @cfg {String} brand_href href of the brand
5822  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5823  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5824  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5825  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5826  * 
5827  * @constructor
5828  * Create a new Sidebar
5829  * @param {Object} config The config object
5830  */
5831
5832
5833 Roo.bootstrap.NavHeaderbar = function(config){
5834     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5835       
5836 };
5837
5838 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5839     
5840     position: '',
5841     brand: '',
5842     brand_href: false,
5843     srButton : true,
5844     autohide : false,
5845     desktopCenter : false,
5846    
5847     
5848     getAutoCreate : function(){
5849         
5850         var   cfg = {
5851             tag: this.nav || 'nav',
5852             cls: 'navbar navbar-expand-md',
5853             role: 'navigation',
5854             cn: []
5855         };
5856         
5857         var cn = cfg.cn;
5858         if (this.desktopCenter) {
5859             cn.push({cls : 'container', cn : []});
5860             cn = cn[0].cn;
5861         }
5862         
5863         if(this.srButton){
5864             var btn = {
5865                 tag: 'button',
5866                 type: 'button',
5867                 cls: 'navbar-toggle navbar-toggler',
5868                 'data-toggle': 'collapse',
5869                 cn: [
5870                     {
5871                         tag: 'span',
5872                         cls: 'sr-only',
5873                         html: 'Toggle navigation'
5874                     },
5875                     {
5876                         tag: 'span',
5877                         cls: 'icon-bar navbar-toggler-icon'
5878                     },
5879                     {
5880                         tag: 'span',
5881                         cls: 'icon-bar'
5882                     },
5883                     {
5884                         tag: 'span',
5885                         cls: 'icon-bar'
5886                     }
5887                 ]
5888             };
5889             
5890             cn.push( Roo.bootstrap.version == 4 ? btn : {
5891                 tag: 'div',
5892                 cls: 'navbar-header',
5893                 cn: [
5894                     btn
5895                 ]
5896             });
5897         }
5898         
5899         cn.push({
5900             tag: 'div',
5901             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5902             cn : []
5903         });
5904         
5905         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906         
5907         if (['light','white'].indexOf(this.weight) > -1) {
5908             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909         }
5910         cfg.cls += ' bg-' + this.weight;
5911         
5912         
5913         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5914             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915             
5916             // tag can override this..
5917             
5918             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5919         }
5920         
5921         if (this.brand !== '') {
5922             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5923             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924                 tag: 'a',
5925                 href: this.brand_href ? this.brand_href : '#',
5926                 cls: 'navbar-brand',
5927                 cn: [
5928                 this.brand
5929                 ]
5930             });
5931         }
5932         
5933         if(this.main){
5934             cfg.cls += ' main-nav';
5935         }
5936         
5937         
5938         return cfg;
5939
5940         
5941     },
5942     getHeaderChildContainer : function()
5943     {
5944         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5945             return this.el.select('.navbar-header',true).first();
5946         }
5947         
5948         return this.getChildContainer();
5949     },
5950     
5951     getChildContainer : function()
5952     {
5953          
5954         return this.el.select('.roo-navbar-collapse',true).first();
5955          
5956         
5957     },
5958     
5959     initEvents : function()
5960     {
5961         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962         
5963         if (this.autohide) {
5964             
5965             var prevScroll = 0;
5966             var ft = this.el;
5967             
5968             Roo.get(document).on('scroll',function(e) {
5969                 var ns = Roo.get(document).getScroll().top;
5970                 var os = prevScroll;
5971                 prevScroll = ns;
5972                 
5973                 if(ns > os){
5974                     ft.removeClass('slideDown');
5975                     ft.addClass('slideUp');
5976                     return;
5977                 }
5978                 ft.removeClass('slideUp');
5979                 ft.addClass('slideDown');
5980                  
5981               
5982           },this);
5983         }
5984     }    
5985     
5986 });
5987
5988
5989
5990  
5991
5992  /*
5993  * - LGPL
5994  *
5995  * navbar
5996  * 
5997  */
5998
5999 /**
6000  * @class Roo.bootstrap.NavSidebar
6001  * @extends Roo.bootstrap.Navbar
6002  * Bootstrap Sidebar class
6003  * 
6004  * @constructor
6005  * Create a new Sidebar
6006  * @param {Object} config The config object
6007  */
6008
6009
6010 Roo.bootstrap.NavSidebar = function(config){
6011     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6012 };
6013
6014 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6015     
6016     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017     
6018     getAutoCreate : function(){
6019         
6020         
6021         return  {
6022             tag: 'div',
6023             cls: 'sidebar sidebar-nav'
6024         };
6025     
6026         
6027     }
6028     
6029     
6030     
6031 });
6032
6033
6034
6035  
6036
6037  /*
6038  * - LGPL
6039  *
6040  * nav group
6041  * 
6042  */
6043
6044 /**
6045  * @class Roo.bootstrap.NavGroup
6046  * @extends Roo.bootstrap.Component
6047  * Bootstrap NavGroup class
6048  * @cfg {String} align (left|right)
6049  * @cfg {Boolean} inverse
6050  * @cfg {String} type (nav|pills|tab) default nav
6051  * @cfg {String} navId - reference Id for navbar.
6052  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6053  * 
6054  * @constructor
6055  * Create a new nav group
6056  * @param {Object} config The config object
6057  */
6058
6059 Roo.bootstrap.NavGroup = function(config){
6060     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6061     this.navItems = [];
6062    
6063     Roo.bootstrap.NavGroup.register(this);
6064      this.addEvents({
6065         /**
6066              * @event changed
6067              * Fires when the active item changes
6068              * @param {Roo.bootstrap.NavGroup} this
6069              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6070              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6071          */
6072         'changed': true
6073      });
6074     
6075 };
6076
6077 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6078     
6079     align: '',
6080     inverse: false,
6081     form: false,
6082     type: 'nav',
6083     navId : '',
6084     // private
6085     pilltype : true,
6086     
6087     navItems : false, 
6088     
6089     getAutoCreate : function()
6090     {
6091         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6092         
6093         cfg = {
6094             tag : 'ul',
6095             cls: 'nav' 
6096         };
6097         if (Roo.bootstrap.version == 4) {
6098             if (['tabs','pills'].indexOf(this.type) != -1) {
6099                 cfg.cls += ' nav-' + this.type; 
6100             } else {
6101                 // trying to remove so header bar can right align top?
6102                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6103                     // do not use on header bar... 
6104                     cfg.cls += ' navbar-nav';
6105                 }
6106             }
6107             
6108         } else {
6109             if (['tabs','pills'].indexOf(this.type) != -1) {
6110                 cfg.cls += ' nav-' + this.type
6111             } else {
6112                 if (this.type !== 'nav') {
6113                     Roo.log('nav type must be nav/tabs/pills')
6114                 }
6115                 cfg.cls += ' navbar-nav'
6116             }
6117         }
6118         
6119         if (this.parent() && this.parent().sidebar) {
6120             cfg = {
6121                 tag: 'ul',
6122                 cls: 'dashboard-menu sidebar-menu'
6123             };
6124             
6125             return cfg;
6126         }
6127         
6128         if (this.form === true) {
6129             cfg = {
6130                 tag: 'form',
6131                 cls: 'navbar-form form-inline'
6132             };
6133             //nav navbar-right ml-md-auto
6134             if (this.align === 'right') {
6135                 cfg.cls += ' navbar-right ml-md-auto';
6136             } else {
6137                 cfg.cls += ' navbar-left';
6138             }
6139         }
6140         
6141         if (this.align === 'right') {
6142             cfg.cls += ' navbar-right ml-md-auto';
6143         } else {
6144             cfg.cls += ' mr-auto';
6145         }
6146         
6147         if (this.inverse) {
6148             cfg.cls += ' navbar-inverse';
6149             
6150         }
6151         
6152         
6153         return cfg;
6154     },
6155     /**
6156     * sets the active Navigation item
6157     * @param {Roo.bootstrap.NavItem} the new current navitem
6158     */
6159     setActiveItem : function(item)
6160     {
6161         var prev = false;
6162         Roo.each(this.navItems, function(v){
6163             if (v == item) {
6164                 return ;
6165             }
6166             if (v.isActive()) {
6167                 v.setActive(false, true);
6168                 prev = v;
6169                 
6170             }
6171             
6172         });
6173
6174         item.setActive(true, true);
6175         this.fireEvent('changed', this, item, prev);
6176         
6177         
6178     },
6179     /**
6180     * gets the active Navigation item
6181     * @return {Roo.bootstrap.NavItem} the current navitem
6182     */
6183     getActive : function()
6184     {
6185         
6186         var prev = false;
6187         Roo.each(this.navItems, function(v){
6188             
6189             if (v.isActive()) {
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195         return prev;
6196     },
6197     
6198     indexOfNav : function()
6199     {
6200         
6201         var prev = false;
6202         Roo.each(this.navItems, function(v,i){
6203             
6204             if (v.isActive()) {
6205                 prev = i;
6206                 
6207             }
6208             
6209         });
6210         return prev;
6211     },
6212     /**
6213     * adds a Navigation item
6214     * @param {Roo.bootstrap.NavItem} the navitem to add
6215     */
6216     addItem : function(cfg)
6217     {
6218         if (this.form && Roo.bootstrap.version == 4) {
6219             cfg.tag = 'div';
6220         }
6221         var cn = new Roo.bootstrap.NavItem(cfg);
6222         this.register(cn);
6223         cn.parentId = this.id;
6224         cn.onRender(this.el, null);
6225         return cn;
6226     },
6227     /**
6228     * register a Navigation item
6229     * @param {Roo.bootstrap.NavItem} the navitem to add
6230     */
6231     register : function(item)
6232     {
6233         this.navItems.push( item);
6234         item.navId = this.navId;
6235     
6236     },
6237     
6238     /**
6239     * clear all the Navigation item
6240     */
6241    
6242     clearAll : function()
6243     {
6244         this.navItems = [];
6245         this.el.dom.innerHTML = '';
6246     },
6247     
6248     getNavItem: function(tabId)
6249     {
6250         var ret = false;
6251         Roo.each(this.navItems, function(e) {
6252             if (e.tabId == tabId) {
6253                ret =  e;
6254                return false;
6255             }
6256             return true;
6257             
6258         });
6259         return ret;
6260     },
6261     
6262     setActiveNext : function()
6263     {
6264         var i = this.indexOfNav(this.getActive());
6265         if (i > this.navItems.length) {
6266             return;
6267         }
6268         this.setActiveItem(this.navItems[i+1]);
6269     },
6270     setActivePrev : function()
6271     {
6272         var i = this.indexOfNav(this.getActive());
6273         if (i  < 1) {
6274             return;
6275         }
6276         this.setActiveItem(this.navItems[i-1]);
6277     },
6278     clearWasActive : function(except) {
6279         Roo.each(this.navItems, function(e) {
6280             if (e.tabId != except.tabId && e.was_active) {
6281                e.was_active = false;
6282                return false;
6283             }
6284             return true;
6285             
6286         });
6287     },
6288     getWasActive : function ()
6289     {
6290         var r = false;
6291         Roo.each(this.navItems, function(e) {
6292             if (e.was_active) {
6293                r = e;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299         return r;
6300     }
6301     
6302     
6303 });
6304
6305  
6306 Roo.apply(Roo.bootstrap.NavGroup, {
6307     
6308     groups: {},
6309      /**
6310     * register a Navigation Group
6311     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312     */
6313     register : function(navgrp)
6314     {
6315         this.groups[navgrp.navId] = navgrp;
6316         
6317     },
6318     /**
6319     * fetch a Navigation Group based on the navigation ID
6320     * @param {string} the navgroup to add
6321     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6322     */
6323     get: function(navId) {
6324         if (typeof(this.groups[navId]) == 'undefined') {
6325             return false;
6326             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327         }
6328         return this.groups[navId] ;
6329     }
6330     
6331     
6332     
6333 });
6334
6335  /*
6336  * - LGPL
6337  *
6338  * row
6339  * 
6340  */
6341
6342 /**
6343  * @class Roo.bootstrap.NavItem
6344  * @extends Roo.bootstrap.Component
6345  * Bootstrap Navbar.NavItem class
6346  * @cfg {String} href  link to
6347  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6348  * @cfg {Boolean} button_outline show and outlined button
6349  * @cfg {String} html content of button
6350  * @cfg {String} badge text inside badge
6351  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6352  * @cfg {String} glyphicon DEPRICATED - use fa
6353  * @cfg {String} icon DEPRICATED - use fa
6354  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6355  * @cfg {Boolean} active Is item active
6356  * @cfg {Boolean} disabled Is item disabled
6357  * @cfg {String} linkcls  Link Class
6358  * @cfg {Boolean} preventDefault (true | false) default false
6359  * @cfg {String} tabId the tab that this item activates.
6360  * @cfg {String} tagtype (a|span) render as a href or span?
6361  * @cfg {Boolean} animateRef (true|false) link to element default false  
6362   
6363  * @constructor
6364  * Create a new Navbar Item
6365  * @param {Object} config The config object
6366  */
6367 Roo.bootstrap.NavItem = function(config){
6368     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6369     this.addEvents({
6370         // raw events
6371         /**
6372          * @event click
6373          * The raw click event for the entire grid.
6374          * @param {Roo.EventObject} e
6375          */
6376         "click" : true,
6377          /**
6378             * @event changed
6379             * Fires when the active item active state changes
6380             * @param {Roo.bootstrap.NavItem} this
6381             * @param {boolean} state the new state
6382              
6383          */
6384         'changed': true,
6385         /**
6386             * @event scrollto
6387             * Fires when scroll to element
6388             * @param {Roo.bootstrap.NavItem} this
6389             * @param {Object} options
6390             * @param {Roo.EventObject} e
6391              
6392          */
6393         'scrollto': true
6394     });
6395    
6396 };
6397
6398 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6399     
6400     href: false,
6401     html: '',
6402     badge: '',
6403     icon: false,
6404     fa : false,
6405     glyphicon: false,
6406     active: false,
6407     preventDefault : false,
6408     tabId : false,
6409     tagtype : 'a',
6410     tag: 'li',
6411     disabled : false,
6412     animateRef : false,
6413     was_active : false,
6414     button_weight : '',
6415     button_outline : false,
6416     linkcls : '',
6417     navLink: false,
6418     
6419     getAutoCreate : function(){
6420          
6421         var cfg = {
6422             tag: this.tag,
6423             cls: 'nav-item'
6424         };
6425         
6426         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6427         
6428         if (this.active) {
6429             cfg.cls +=  ' active' ;
6430         }
6431         if (this.disabled) {
6432             cfg.cls += ' disabled';
6433         }
6434         
6435         // BS4 only?
6436         if (this.button_weight.length) {
6437             cfg.tag = this.href ? 'a' : 'button';
6438             cfg.html = this.html || '';
6439             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440             if (this.href) {
6441                 cfg.href = this.href;
6442             }
6443             if (this.fa) {
6444                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445             } else {
6446                 cfg.cls += " nav-html";
6447             }
6448             
6449             // menu .. should add dropdown-menu class - so no need for carat..
6450             
6451             if (this.badge !== '') {
6452                  
6453                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6454             }
6455             return cfg;
6456         }
6457         
6458         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6459             cfg.cn = [
6460                 {
6461                     tag: this.tagtype,
6462                     href : this.href || "#",
6463                     html: this.html || '',
6464                     cls : ''
6465                 }
6466             ];
6467             if (this.tagtype == 'a') {
6468                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6469         
6470             }
6471             if (this.icon) {
6472                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6473             } else  if (this.fa) {
6474                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6475             } else if(this.glyphicon) {
6476                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6477             } else {
6478                 cfg.cn[0].cls += " nav-html";
6479             }
6480             
6481             if (this.menu) {
6482                 cfg.cn[0].html += " <span class='caret'></span>";
6483              
6484             }
6485             
6486             if (this.badge !== '') {
6487                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6488             }
6489         }
6490         
6491         
6492         
6493         return cfg;
6494     },
6495     onRender : function(ct, position)
6496     {
6497        // Roo.log("Call onRender: " + this.xtype);
6498         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6499             this.tag = 'div';
6500         }
6501         
6502         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6503         this.navLink = this.el.select('.nav-link',true).first();
6504         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6505         return ret;
6506     },
6507       
6508     
6509     initEvents: function() 
6510     {
6511         if (typeof (this.menu) != 'undefined') {
6512             this.menu.parentType = this.xtype;
6513             this.menu.triggerEl = this.el;
6514             this.menu = this.addxtype(Roo.apply({}, this.menu));
6515         }
6516         
6517         this.el.on('click', this.onClick, this);
6518         
6519         //if(this.tagtype == 'span'){
6520         //    this.el.select('span',true).on('click', this.onClick, this);
6521         //}
6522        
6523         // at this point parent should be available..
6524         this.parent().register(this);
6525     },
6526     
6527     onClick : function(e)
6528     {
6529         if (e.getTarget('.dropdown-menu-item')) {
6530             // did you click on a menu itemm.... - then don't trigger onclick..
6531             return;
6532         }
6533         
6534         if(
6535                 this.preventDefault || 
6536                 this.href == '#' 
6537         ){
6538             Roo.log("NavItem - prevent Default?");
6539             e.preventDefault();
6540         }
6541         
6542         if (this.disabled) {
6543             return;
6544         }
6545         
6546         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6547         if (tg && tg.transition) {
6548             Roo.log("waiting for the transitionend");
6549             return;
6550         }
6551         
6552         
6553         
6554         //Roo.log("fire event clicked");
6555         if(this.fireEvent('click', this, e) === false){
6556             return;
6557         };
6558         
6559         if(this.tagtype == 'span'){
6560             return;
6561         }
6562         
6563         //Roo.log(this.href);
6564         var ael = this.el.select('a',true).first();
6565         //Roo.log(ael);
6566         
6567         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6568             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6569             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6570                 return; // ignore... - it's a 'hash' to another page.
6571             }
6572             Roo.log("NavItem - prevent Default?");
6573             e.preventDefault();
6574             this.scrollToElement(e);
6575         }
6576         
6577         
6578         var p =  this.parent();
6579    
6580         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6581             if (typeof(p.setActiveItem) !== 'undefined') {
6582                 p.setActiveItem(this);
6583             }
6584         }
6585         
6586         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6587         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6588             // remove the collapsed menu expand...
6589             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6590         }
6591     },
6592     
6593     isActive: function () {
6594         return this.active
6595     },
6596     setActive : function(state, fire, is_was_active)
6597     {
6598         if (this.active && !state && this.navId) {
6599             this.was_active = true;
6600             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601             if (nv) {
6602                 nv.clearWasActive(this);
6603             }
6604             
6605         }
6606         this.active = state;
6607         
6608         if (!state ) {
6609             this.el.removeClass('active');
6610             this.navLink ? this.navLink.removeClass('active') : false;
6611         } else if (!this.el.hasClass('active')) {
6612             
6613             this.el.addClass('active');
6614             if (Roo.bootstrap.version == 4 && this.navLink ) {
6615                 this.navLink.addClass('active');
6616             }
6617             
6618         }
6619         if (fire) {
6620             this.fireEvent('changed', this, state);
6621         }
6622         
6623         // show a panel if it's registered and related..
6624         
6625         if (!this.navId || !this.tabId || !state || is_was_active) {
6626             return;
6627         }
6628         
6629         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6630         if (!tg) {
6631             return;
6632         }
6633         var pan = tg.getPanelByName(this.tabId);
6634         if (!pan) {
6635             return;
6636         }
6637         // if we can not flip to new panel - go back to old nav highlight..
6638         if (false == tg.showPanel(pan)) {
6639             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640             if (nv) {
6641                 var onav = nv.getWasActive();
6642                 if (onav) {
6643                     onav.setActive(true, false, true);
6644                 }
6645             }
6646             
6647         }
6648         
6649         
6650         
6651     },
6652      // this should not be here...
6653     setDisabled : function(state)
6654     {
6655         this.disabled = state;
6656         if (!state ) {
6657             this.el.removeClass('disabled');
6658         } else if (!this.el.hasClass('disabled')) {
6659             this.el.addClass('disabled');
6660         }
6661         
6662     },
6663     
6664     /**
6665      * Fetch the element to display the tooltip on.
6666      * @return {Roo.Element} defaults to this.el
6667      */
6668     tooltipEl : function()
6669     {
6670         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6671     },
6672     
6673     scrollToElement : function(e)
6674     {
6675         var c = document.body;
6676         
6677         /*
6678          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679          */
6680         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6681             c = document.documentElement;
6682         }
6683         
6684         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6685         
6686         if(!target){
6687             return;
6688         }
6689
6690         var o = target.calcOffsetsTo(c);
6691         
6692         var options = {
6693             target : target,
6694             value : o[1]
6695         };
6696         
6697         this.fireEvent('scrollto', this, options, e);
6698         
6699         Roo.get(c).scrollTo('top', options.value, true);
6700         
6701         return;
6702     },
6703     /**
6704      * Set the HTML (text content) of the item
6705      * @param {string} html  content for the nav item
6706      */
6707     setHtml : function(html)
6708     {
6709         this.html = html;
6710         this.htmlEl.dom.innerHTML = html;
6711         
6712     } 
6713 });
6714  
6715
6716  /*
6717  * - LGPL
6718  *
6719  * sidebar item
6720  *
6721  *  li
6722  *    <span> icon </span>
6723  *    <span> text </span>
6724  *    <span>badge </span>
6725  */
6726
6727 /**
6728  * @class Roo.bootstrap.NavSidebarItem
6729  * @extends Roo.bootstrap.NavItem
6730  * Bootstrap Navbar.NavSidebarItem class
6731  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6732  * {Boolean} open is the menu open
6733  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6734  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6735  * {String} buttonSize (sm|md|lg)the extra classes for the button
6736  * {Boolean} showArrow show arrow next to the text (default true)
6737  * @constructor
6738  * Create a new Navbar Button
6739  * @param {Object} config The config object
6740  */
6741 Roo.bootstrap.NavSidebarItem = function(config){
6742     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6743     this.addEvents({
6744         // raw events
6745         /**
6746          * @event click
6747          * The raw click event for the entire grid.
6748          * @param {Roo.EventObject} e
6749          */
6750         "click" : true,
6751          /**
6752             * @event changed
6753             * Fires when the active item active state changes
6754             * @param {Roo.bootstrap.NavSidebarItem} this
6755             * @param {boolean} state the new state
6756              
6757          */
6758         'changed': true
6759     });
6760    
6761 };
6762
6763 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6764     
6765     badgeWeight : 'default',
6766     
6767     open: false,
6768     
6769     buttonView : false,
6770     
6771     buttonWeight : 'default',
6772     
6773     buttonSize : 'md',
6774     
6775     showArrow : true,
6776     
6777     getAutoCreate : function(){
6778         
6779         
6780         var a = {
6781                 tag: 'a',
6782                 href : this.href || '#',
6783                 cls: '',
6784                 html : '',
6785                 cn : []
6786         };
6787         
6788         if(this.buttonView){
6789             a = {
6790                 tag: 'button',
6791                 href : this.href || '#',
6792                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6793                 html : this.html,
6794                 cn : []
6795             };
6796         }
6797         
6798         var cfg = {
6799             tag: 'li',
6800             cls: '',
6801             cn: [ a ]
6802         };
6803         
6804         if (this.active) {
6805             cfg.cls += ' active';
6806         }
6807         
6808         if (this.disabled) {
6809             cfg.cls += ' disabled';
6810         }
6811         if (this.open) {
6812             cfg.cls += ' open x-open';
6813         }
6814         // left icon..
6815         if (this.glyphicon || this.icon) {
6816             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6817             a.cn.push({ tag : 'i', cls : c }) ;
6818         }
6819         
6820         if(!this.buttonView){
6821             var span = {
6822                 tag: 'span',
6823                 html : this.html || ''
6824             };
6825
6826             a.cn.push(span);
6827             
6828         }
6829         
6830         if (this.badge !== '') {
6831             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6832         }
6833         
6834         if (this.menu) {
6835             
6836             if(this.showArrow){
6837                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6838             }
6839             
6840             a.cls += ' dropdown-toggle treeview' ;
6841         }
6842         
6843         return cfg;
6844     },
6845     
6846     initEvents : function()
6847     { 
6848         if (typeof (this.menu) != 'undefined') {
6849             this.menu.parentType = this.xtype;
6850             this.menu.triggerEl = this.el;
6851             this.menu = this.addxtype(Roo.apply({}, this.menu));
6852         }
6853         
6854         this.el.on('click', this.onClick, this);
6855         
6856         if(this.badge !== ''){
6857             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6858         }
6859         
6860     },
6861     
6862     onClick : function(e)
6863     {
6864         if(this.disabled){
6865             e.preventDefault();
6866             return;
6867         }
6868         
6869         if(this.preventDefault){
6870             e.preventDefault();
6871         }
6872         
6873         this.fireEvent('click', this, e);
6874     },
6875     
6876     disable : function()
6877     {
6878         this.setDisabled(true);
6879     },
6880     
6881     enable : function()
6882     {
6883         this.setDisabled(false);
6884     },
6885     
6886     setDisabled : function(state)
6887     {
6888         if(this.disabled == state){
6889             return;
6890         }
6891         
6892         this.disabled = state;
6893         
6894         if (state) {
6895             this.el.addClass('disabled');
6896             return;
6897         }
6898         
6899         this.el.removeClass('disabled');
6900         
6901         return;
6902     },
6903     
6904     setActive : function(state)
6905     {
6906         if(this.active == state){
6907             return;
6908         }
6909         
6910         this.active = state;
6911         
6912         if (state) {
6913             this.el.addClass('active');
6914             return;
6915         }
6916         
6917         this.el.removeClass('active');
6918         
6919         return;
6920     },
6921     
6922     isActive: function () 
6923     {
6924         return this.active;
6925     },
6926     
6927     setBadge : function(str)
6928     {
6929         if(!this.badgeEl){
6930             return;
6931         }
6932         
6933         this.badgeEl.dom.innerHTML = str;
6934     }
6935     
6936    
6937      
6938  
6939 });
6940  
6941
6942  /*
6943  * - LGPL
6944  *
6945  *  Breadcrumb Nav
6946  * 
6947  */
6948 Roo.namespace('Roo.bootstrap.breadcrumb');
6949
6950
6951 /**
6952  * @class Roo.bootstrap.breadcrumb.Nav
6953  * @extends Roo.bootstrap.Component
6954  * Bootstrap Breadcrumb Nav Class
6955  *  
6956  * @children Roo.bootstrap.breadcrumb.Item
6957  * 
6958  * @constructor
6959  * Create a new breadcrumb.Nav
6960  * @param {Object} config The config object
6961  */
6962
6963
6964 Roo.bootstrap.breadcrumb.Nav = function(config){
6965     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6966     
6967     
6968 };
6969
6970 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6971     
6972     getAutoCreate : function()
6973     {
6974
6975         var cfg = {
6976             tag: 'nav',
6977             cn : [
6978                 {
6979                     tag : 'ol',
6980                     cls : 'breadcrumb'
6981                 }
6982             ]
6983             
6984         };
6985           
6986         return cfg;
6987     },
6988     
6989     initEvents: function()
6990     {
6991         this.olEl = this.el.select('ol',true).first();    
6992     },
6993     getChildContainer : function()
6994     {
6995         return this.olEl;  
6996     }
6997     
6998 });
6999
7000  /*
7001  * - LGPL
7002  *
7003  *  Breadcrumb Item
7004  * 
7005  */
7006
7007
7008 /**
7009  * @class Roo.bootstrap.breadcrumb.Nav
7010  * @extends Roo.bootstrap.Component
7011  * Bootstrap Breadcrumb Nav Class
7012  *  
7013  * @children Roo.bootstrap.breadcrumb.Component
7014  * @cfg {String} html the content of the link.
7015  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7016  * @cfg {Boolean} active is it active
7017
7018  * 
7019  * @constructor
7020  * Create a new breadcrumb.Nav
7021  * @param {Object} config The config object
7022  */
7023
7024 Roo.bootstrap.breadcrumb.Item = function(config){
7025     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7026     this.addEvents({
7027         // img events
7028         /**
7029          * @event click
7030          * The img click event for the img.
7031          * @param {Roo.EventObject} e
7032          */
7033         "click" : true
7034     });
7035     
7036 };
7037
7038 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7039     
7040     href: false,
7041     html : '',
7042     
7043     getAutoCreate : function()
7044     {
7045
7046         var cfg = {
7047             tag: 'li',
7048             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049         };
7050         if (this.href !== false) {
7051             cfg.cn = [{
7052                 tag : 'a',
7053                 href : this.href,
7054                 html : this.html
7055             }];
7056         } else {
7057             cfg.html = this.html;
7058         }
7059         
7060         return cfg;
7061     },
7062     
7063     initEvents: function()
7064     {
7065         if (this.href) {
7066             this.el.select('a', true).first().on('click',this.onClick, this)
7067         }
7068         
7069     },
7070     onClick : function(e)
7071     {
7072         e.preventDefault();
7073         this.fireEvent('click',this,  e);
7074     }
7075     
7076 });
7077
7078  /*
7079  * - LGPL
7080  *
7081  * row
7082  * 
7083  */
7084
7085 /**
7086  * @class Roo.bootstrap.Row
7087  * @extends Roo.bootstrap.Component
7088  * Bootstrap Row class (contains columns...)
7089  * 
7090  * @constructor
7091  * Create a new Row
7092  * @param {Object} config The config object
7093  */
7094
7095 Roo.bootstrap.Row = function(config){
7096     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7097 };
7098
7099 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7100     
7101     getAutoCreate : function(){
7102        return {
7103             cls: 'row clearfix'
7104        };
7105     }
7106     
7107     
7108 });
7109
7110  
7111
7112  /*
7113  * - LGPL
7114  *
7115  * pagination
7116  * 
7117  */
7118
7119 /**
7120  * @class Roo.bootstrap.Pagination
7121  * @extends Roo.bootstrap.Component
7122  * Bootstrap Pagination class
7123  * @cfg {String} size xs | sm | md | lg
7124  * @cfg {Boolean} inverse false | true
7125  * 
7126  * @constructor
7127  * Create a new Pagination
7128  * @param {Object} config The config object
7129  */
7130
7131 Roo.bootstrap.Pagination = function(config){
7132     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7133 };
7134
7135 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7136     
7137     cls: false,
7138     size: false,
7139     inverse: false,
7140     
7141     getAutoCreate : function(){
7142         var cfg = {
7143             tag: 'ul',
7144                 cls: 'pagination'
7145         };
7146         if (this.inverse) {
7147             cfg.cls += ' inverse';
7148         }
7149         if (this.html) {
7150             cfg.html=this.html;
7151         }
7152         if (this.cls) {
7153             cfg.cls += " " + this.cls;
7154         }
7155         return cfg;
7156     }
7157    
7158 });
7159
7160  
7161
7162  /*
7163  * - LGPL
7164  *
7165  * Pagination item
7166  * 
7167  */
7168
7169
7170 /**
7171  * @class Roo.bootstrap.PaginationItem
7172  * @extends Roo.bootstrap.Component
7173  * Bootstrap PaginationItem class
7174  * @cfg {String} html text
7175  * @cfg {String} href the link
7176  * @cfg {Boolean} preventDefault (true | false) default true
7177  * @cfg {Boolean} active (true | false) default false
7178  * @cfg {Boolean} disabled default false
7179  * 
7180  * 
7181  * @constructor
7182  * Create a new PaginationItem
7183  * @param {Object} config The config object
7184  */
7185
7186
7187 Roo.bootstrap.PaginationItem = function(config){
7188     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7189     this.addEvents({
7190         // raw events
7191         /**
7192          * @event click
7193          * The raw click event for the entire grid.
7194          * @param {Roo.EventObject} e
7195          */
7196         "click" : true
7197     });
7198 };
7199
7200 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7201     
7202     href : false,
7203     html : false,
7204     preventDefault: true,
7205     active : false,
7206     cls : false,
7207     disabled: false,
7208     
7209     getAutoCreate : function(){
7210         var cfg= {
7211             tag: 'li',
7212             cn: [
7213                 {
7214                     tag : 'a',
7215                     href : this.href ? this.href : '#',
7216                     html : this.html ? this.html : ''
7217                 }
7218             ]
7219         };
7220         
7221         if(this.cls){
7222             cfg.cls = this.cls;
7223         }
7224         
7225         if(this.disabled){
7226             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7227         }
7228         
7229         if(this.active){
7230             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7231         }
7232         
7233         return cfg;
7234     },
7235     
7236     initEvents: function() {
7237         
7238         this.el.on('click', this.onClick, this);
7239         
7240     },
7241     onClick : function(e)
7242     {
7243         Roo.log('PaginationItem on click ');
7244         if(this.preventDefault){
7245             e.preventDefault();
7246         }
7247         
7248         if(this.disabled){
7249             return;
7250         }
7251         
7252         this.fireEvent('click', this, e);
7253     }
7254    
7255 });
7256
7257  
7258
7259  /*
7260  * - LGPL
7261  *
7262  * slider
7263  * 
7264  */
7265
7266
7267 /**
7268  * @class Roo.bootstrap.Slider
7269  * @extends Roo.bootstrap.Component
7270  * Bootstrap Slider class
7271  *    
7272  * @constructor
7273  * Create a new Slider
7274  * @param {Object} config The config object
7275  */
7276
7277 Roo.bootstrap.Slider = function(config){
7278     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7279 };
7280
7281 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7282     
7283     getAutoCreate : function(){
7284         
7285         var cfg = {
7286             tag: 'div',
7287             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7288             cn: [
7289                 {
7290                     tag: 'a',
7291                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7292                 }
7293             ]
7294         };
7295         
7296         return cfg;
7297     }
7298    
7299 });
7300
7301  /*
7302  * Based on:
7303  * Ext JS Library 1.1.1
7304  * Copyright(c) 2006-2007, Ext JS, LLC.
7305  *
7306  * Originally Released Under LGPL - original licence link has changed is not relivant.
7307  *
7308  * Fork - LGPL
7309  * <script type="text/javascript">
7310  */
7311  /**
7312  * @extends Roo.dd.DDProxy
7313  * @class Roo.grid.SplitDragZone
7314  * Support for Column Header resizing
7315  * @constructor
7316  * @param {Object} config
7317  */
7318 // private
7319 // This is a support class used internally by the Grid components
7320 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7321     this.grid = grid;
7322     this.view = grid.getView();
7323     this.proxy = this.view.resizeProxy;
7324     Roo.grid.SplitDragZone.superclass.constructor.call(
7325         this,
7326         hd, // ID
7327         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7328         {  // CONFIG
7329             dragElId : Roo.id(this.proxy.dom),
7330             resizeFrame:false
7331         }
7332     );
7333     
7334     this.setHandleElId(Roo.id(hd));
7335     if (hd2 !== false) {
7336         this.setOuterHandleElId(Roo.id(hd2));
7337     }
7338     
7339     this.scroll = false;
7340 };
7341 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7342     fly: Roo.Element.fly,
7343
7344     b4StartDrag : function(x, y){
7345         this.view.headersDisabled = true;
7346         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7347                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7348         );
7349         this.proxy.setHeight(h);
7350         
7351         // for old system colWidth really stored the actual width?
7352         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7353         // which in reality did not work.. - it worked only for fixed sizes
7354         // for resizable we need to use actual sizes.
7355         var w = this.cm.getColumnWidth(this.cellIndex);
7356         if (!this.view.mainWrap) {
7357             // bootstrap.
7358             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7359         }
7360         
7361         
7362         
7363         // this was w-this.grid.minColumnWidth;
7364         // doesnt really make sense? - w = thie curren width or the rendered one?
7365         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7366         this.resetConstraints();
7367         this.setXConstraint(minw, 1000);
7368         this.setYConstraint(0, 0);
7369         this.minX = x - minw;
7370         this.maxX = x + 1000;
7371         this.startPos = x;
7372         if (!this.view.mainWrap) { // this is Bootstrap code..
7373             this.getDragEl().style.display='block';
7374         }
7375         
7376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7377     },
7378
7379
7380     handleMouseDown : function(e){
7381         ev = Roo.EventObject.setEvent(e);
7382         var t = this.fly(ev.getTarget());
7383         if(t.hasClass("x-grid-split")){
7384             this.cellIndex = this.view.getCellIndex(t.dom);
7385             this.split = t.dom;
7386             this.cm = this.grid.colModel;
7387             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7388                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7389             }
7390         }
7391     },
7392
7393     endDrag : function(e){
7394         this.view.headersDisabled = false;
7395         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7396         var diff = endX - this.startPos;
7397         // 
7398         var w = this.cm.getColumnWidth(this.cellIndex);
7399         if (!this.view.mainWrap) {
7400             w = 0;
7401         }
7402         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7403     },
7404
7405     autoOffset : function(){
7406         this.setDelta(0,0);
7407     }
7408 });/*
7409  * Based on:
7410  * Ext JS Library 1.1.1
7411  * Copyright(c) 2006-2007, Ext JS, LLC.
7412  *
7413  * Originally Released Under LGPL - original licence link has changed is not relivant.
7414  *
7415  * Fork - LGPL
7416  * <script type="text/javascript">
7417  */
7418
7419 /**
7420  * @class Roo.grid.AbstractSelectionModel
7421  * @extends Roo.util.Observable
7422  * @abstract
7423  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7424  * implemented by descendant classes.  This class should not be directly instantiated.
7425  * @constructor
7426  */
7427 Roo.grid.AbstractSelectionModel = function(){
7428     this.locked = false;
7429     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7430 };
7431
7432 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7433     /** @ignore Called by the grid automatically. Do not call directly. */
7434     init : function(grid){
7435         this.grid = grid;
7436         this.initEvents();
7437     },
7438
7439     /**
7440      * Locks the selections.
7441      */
7442     lock : function(){
7443         this.locked = true;
7444     },
7445
7446     /**
7447      * Unlocks the selections.
7448      */
7449     unlock : function(){
7450         this.locked = false;
7451     },
7452
7453     /**
7454      * Returns true if the selections are locked.
7455      * @return {Boolean}
7456      */
7457     isLocked : function(){
7458         return this.locked;
7459     }
7460 });/*
7461  * Based on:
7462  * Ext JS Library 1.1.1
7463  * Copyright(c) 2006-2007, Ext JS, LLC.
7464  *
7465  * Originally Released Under LGPL - original licence link has changed is not relivant.
7466  *
7467  * Fork - LGPL
7468  * <script type="text/javascript">
7469  */
7470 /**
7471  * @extends Roo.grid.AbstractSelectionModel
7472  * @class Roo.grid.RowSelectionModel
7473  * The default SelectionModel used by {@link Roo.grid.Grid}.
7474  * It supports multiple selections and keyboard selection/navigation. 
7475  * @constructor
7476  * @param {Object} config
7477  */
7478 Roo.grid.RowSelectionModel = function(config){
7479     Roo.apply(this, config);
7480     this.selections = new Roo.util.MixedCollection(false, function(o){
7481         return o.id;
7482     });
7483
7484     this.last = false;
7485     this.lastActive = false;
7486
7487     this.addEvents({
7488         /**
7489         * @event selectionchange
7490         * Fires when the selection changes
7491         * @param {SelectionModel} this
7492         */
7493        "selectionchange" : true,
7494        /**
7495         * @event afterselectionchange
7496         * Fires after the selection changes (eg. by key press or clicking)
7497         * @param {SelectionModel} this
7498         */
7499        "afterselectionchange" : true,
7500        /**
7501         * @event beforerowselect
7502         * Fires when a row is selected being selected, return false to cancel.
7503         * @param {SelectionModel} this
7504         * @param {Number} rowIndex The selected index
7505         * @param {Boolean} keepExisting False if other selections will be cleared
7506         */
7507        "beforerowselect" : true,
7508        /**
7509         * @event rowselect
7510         * Fires when a row is selected.
7511         * @param {SelectionModel} this
7512         * @param {Number} rowIndex The selected index
7513         * @param {Roo.data.Record} r The record
7514         */
7515        "rowselect" : true,
7516        /**
7517         * @event rowdeselect
7518         * Fires when a row is deselected.
7519         * @param {SelectionModel} this
7520         * @param {Number} rowIndex The selected index
7521         */
7522         "rowdeselect" : true
7523     });
7524     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7525     this.locked = false;
7526 };
7527
7528 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7529     /**
7530      * @cfg {Boolean} singleSelect
7531      * True to allow selection of only one row at a time (defaults to false)
7532      */
7533     singleSelect : false,
7534
7535     // private
7536     initEvents : function(){
7537
7538         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7539             this.grid.on("mousedown", this.handleMouseDown, this);
7540         }else{ // allow click to work like normal
7541             this.grid.on("rowclick", this.handleDragableRowClick, this);
7542         }
7543         // bootstrap does not have a view..
7544         var view = this.grid.view ? this.grid.view : this.grid;
7545         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7546             "up" : function(e){
7547                 if(!e.shiftKey){
7548                     this.selectPrevious(e.shiftKey);
7549                 }else if(this.last !== false && this.lastActive !== false){
7550                     var last = this.last;
7551                     this.selectRange(this.last,  this.lastActive-1);
7552                     view.focusRow(this.lastActive);
7553                     if(last !== false){
7554                         this.last = last;
7555                     }
7556                 }else{
7557                     this.selectFirstRow();
7558                 }
7559                 this.fireEvent("afterselectionchange", this);
7560             },
7561             "down" : function(e){
7562                 if(!e.shiftKey){
7563                     this.selectNext(e.shiftKey);
7564                 }else if(this.last !== false && this.lastActive !== false){
7565                     var last = this.last;
7566                     this.selectRange(this.last,  this.lastActive+1);
7567                     view.focusRow(this.lastActive);
7568                     if(last !== false){
7569                         this.last = last;
7570                     }
7571                 }else{
7572                     this.selectFirstRow();
7573                 }
7574                 this.fireEvent("afterselectionchange", this);
7575             },
7576             scope: this
7577         });
7578
7579          
7580         view.on("refresh", this.onRefresh, this);
7581         view.on("rowupdated", this.onRowUpdated, this);
7582         view.on("rowremoved", this.onRemove, this);
7583     },
7584
7585     // private
7586     onRefresh : function(){
7587         var ds = this.grid.ds, i, v = this.grid.view;
7588         var s = this.selections;
7589         s.each(function(r){
7590             if((i = ds.indexOfId(r.id)) != -1){
7591                 v.onRowSelect(i);
7592                 s.add(ds.getAt(i)); // updating the selection relate data
7593             }else{
7594                 s.remove(r);
7595             }
7596         });
7597     },
7598
7599     // private
7600     onRemove : function(v, index, r){
7601         this.selections.remove(r);
7602     },
7603
7604     // private
7605     onRowUpdated : function(v, index, r){
7606         if(this.isSelected(r)){
7607             v.onRowSelect(index);
7608         }
7609     },
7610
7611     /**
7612      * Select records.
7613      * @param {Array} records The records to select
7614      * @param {Boolean} keepExisting (optional) True to keep existing selections
7615      */
7616     selectRecords : function(records, keepExisting){
7617         if(!keepExisting){
7618             this.clearSelections();
7619         }
7620         var ds = this.grid.ds;
7621         for(var i = 0, len = records.length; i < len; i++){
7622             this.selectRow(ds.indexOf(records[i]), true);
7623         }
7624     },
7625
7626     /**
7627      * Gets the number of selected rows.
7628      * @return {Number}
7629      */
7630     getCount : function(){
7631         return this.selections.length;
7632     },
7633
7634     /**
7635      * Selects the first row in the grid.
7636      */
7637     selectFirstRow : function(){
7638         this.selectRow(0);
7639     },
7640
7641     /**
7642      * Select the last row.
7643      * @param {Boolean} keepExisting (optional) True to keep existing selections
7644      */
7645     selectLastRow : function(keepExisting){
7646         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7647     },
7648
7649     /**
7650      * Selects the row immediately following the last selected row.
7651      * @param {Boolean} keepExisting (optional) True to keep existing selections
7652      */
7653     selectNext : function(keepExisting){
7654         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7655             this.selectRow(this.last+1, keepExisting);
7656             var view = this.grid.view ? this.grid.view : this.grid;
7657             view.focusRow(this.last);
7658         }
7659     },
7660
7661     /**
7662      * Selects the row that precedes the last selected row.
7663      * @param {Boolean} keepExisting (optional) True to keep existing selections
7664      */
7665     selectPrevious : function(keepExisting){
7666         if(this.last){
7667             this.selectRow(this.last-1, keepExisting);
7668             var view = this.grid.view ? this.grid.view : this.grid;
7669             view.focusRow(this.last);
7670         }
7671     },
7672
7673     /**
7674      * Returns the selected records
7675      * @return {Array} Array of selected records
7676      */
7677     getSelections : function(){
7678         return [].concat(this.selections.items);
7679     },
7680
7681     /**
7682      * Returns the first selected record.
7683      * @return {Record}
7684      */
7685     getSelected : function(){
7686         return this.selections.itemAt(0);
7687     },
7688
7689
7690     /**
7691      * Clears all selections.
7692      */
7693     clearSelections : function(fast){
7694         if(this.locked) {
7695             return;
7696         }
7697         if(fast !== true){
7698             var ds = this.grid.ds;
7699             var s = this.selections;
7700             s.each(function(r){
7701                 this.deselectRow(ds.indexOfId(r.id));
7702             }, this);
7703             s.clear();
7704         }else{
7705             this.selections.clear();
7706         }
7707         this.last = false;
7708     },
7709
7710
7711     /**
7712      * Selects all rows.
7713      */
7714     selectAll : function(){
7715         if(this.locked) {
7716             return;
7717         }
7718         this.selections.clear();
7719         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7720             this.selectRow(i, true);
7721         }
7722     },
7723
7724     /**
7725      * Returns True if there is a selection.
7726      * @return {Boolean}
7727      */
7728     hasSelection : function(){
7729         return this.selections.length > 0;
7730     },
7731
7732     /**
7733      * Returns True if the specified row is selected.
7734      * @param {Number/Record} record The record or index of the record to check
7735      * @return {Boolean}
7736      */
7737     isSelected : function(index){
7738         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7739         return (r && this.selections.key(r.id) ? true : false);
7740     },
7741
7742     /**
7743      * Returns True if the specified record id is selected.
7744      * @param {String} id The id of record to check
7745      * @return {Boolean}
7746      */
7747     isIdSelected : function(id){
7748         return (this.selections.key(id) ? true : false);
7749     },
7750
7751     // private
7752     handleMouseDown : function(e, t)
7753     {
7754         var view = this.grid.view ? this.grid.view : this.grid;
7755         var rowIndex;
7756         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7757             return;
7758         };
7759         if(e.shiftKey && this.last !== false){
7760             var last = this.last;
7761             this.selectRange(last, rowIndex, e.ctrlKey);
7762             this.last = last; // reset the last
7763             view.focusRow(rowIndex);
7764         }else{
7765             var isSelected = this.isSelected(rowIndex);
7766             if(e.button !== 0 && isSelected){
7767                 view.focusRow(rowIndex);
7768             }else if(e.ctrlKey && isSelected){
7769                 this.deselectRow(rowIndex);
7770             }else if(!isSelected){
7771                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7772                 view.focusRow(rowIndex);
7773             }
7774         }
7775         this.fireEvent("afterselectionchange", this);
7776     },
7777     // private
7778     handleDragableRowClick :  function(grid, rowIndex, e) 
7779     {
7780         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7781             this.selectRow(rowIndex, false);
7782             var view = this.grid.view ? this.grid.view : this.grid;
7783             view.focusRow(rowIndex);
7784              this.fireEvent("afterselectionchange", this);
7785         }
7786     },
7787     
7788     /**
7789      * Selects multiple rows.
7790      * @param {Array} rows Array of the indexes of the row to select
7791      * @param {Boolean} keepExisting (optional) True to keep existing selections
7792      */
7793     selectRows : function(rows, keepExisting){
7794         if(!keepExisting){
7795             this.clearSelections();
7796         }
7797         for(var i = 0, len = rows.length; i < len; i++){
7798             this.selectRow(rows[i], true);
7799         }
7800     },
7801
7802     /**
7803      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7804      * @param {Number} startRow The index of the first row in the range
7805      * @param {Number} endRow The index of the last row in the range
7806      * @param {Boolean} keepExisting (optional) True to retain existing selections
7807      */
7808     selectRange : function(startRow, endRow, keepExisting){
7809         if(this.locked) {
7810             return;
7811         }
7812         if(!keepExisting){
7813             this.clearSelections();
7814         }
7815         if(startRow <= endRow){
7816             for(var i = startRow; i <= endRow; i++){
7817                 this.selectRow(i, true);
7818             }
7819         }else{
7820             for(var i = startRow; i >= endRow; i--){
7821                 this.selectRow(i, true);
7822             }
7823         }
7824     },
7825
7826     /**
7827      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7828      * @param {Number} startRow The index of the first row in the range
7829      * @param {Number} endRow The index of the last row in the range
7830      */
7831     deselectRange : function(startRow, endRow, preventViewNotify){
7832         if(this.locked) {
7833             return;
7834         }
7835         for(var i = startRow; i <= endRow; i++){
7836             this.deselectRow(i, preventViewNotify);
7837         }
7838     },
7839
7840     /**
7841      * Selects a row.
7842      * @param {Number} row The index of the row to select
7843      * @param {Boolean} keepExisting (optional) True to keep existing selections
7844      */
7845     selectRow : function(index, keepExisting, preventViewNotify){
7846         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7847             return;
7848         }
7849         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7850             if(!keepExisting || this.singleSelect){
7851                 this.clearSelections();
7852             }
7853             var r = this.grid.ds.getAt(index);
7854             this.selections.add(r);
7855             this.last = this.lastActive = index;
7856             if(!preventViewNotify){
7857                 var view = this.grid.view ? this.grid.view : this.grid;
7858                 view.onRowSelect(index);
7859             }
7860             this.fireEvent("rowselect", this, index, r);
7861             this.fireEvent("selectionchange", this);
7862         }
7863     },
7864
7865     /**
7866      * Deselects a row.
7867      * @param {Number} row The index of the row to deselect
7868      */
7869     deselectRow : function(index, preventViewNotify){
7870         if(this.locked) {
7871             return;
7872         }
7873         if(this.last == index){
7874             this.last = false;
7875         }
7876         if(this.lastActive == index){
7877             this.lastActive = false;
7878         }
7879         var r = this.grid.ds.getAt(index);
7880         this.selections.remove(r);
7881         if(!preventViewNotify){
7882             var view = this.grid.view ? this.grid.view : this.grid;
7883             view.onRowDeselect(index);
7884         }
7885         this.fireEvent("rowdeselect", this, index);
7886         this.fireEvent("selectionchange", this);
7887     },
7888
7889     // private
7890     restoreLast : function(){
7891         if(this._last){
7892             this.last = this._last;
7893         }
7894     },
7895
7896     // private
7897     acceptsNav : function(row, col, cm){
7898         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7899     },
7900
7901     // private
7902     onEditorKey : function(field, e){
7903         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7904         if(k == e.TAB){
7905             e.stopEvent();
7906             ed.completeEdit();
7907             if(e.shiftKey){
7908                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7909             }else{
7910                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7911             }
7912         }else if(k == e.ENTER && !e.ctrlKey){
7913             e.stopEvent();
7914             ed.completeEdit();
7915             if(e.shiftKey){
7916                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7917             }else{
7918                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7919             }
7920         }else if(k == e.ESC){
7921             ed.cancelEdit();
7922         }
7923         if(newCell){
7924             g.startEditing(newCell[0], newCell[1]);
7925         }
7926     }
7927 });/*
7928  * Based on:
7929  * Ext JS Library 1.1.1
7930  * Copyright(c) 2006-2007, Ext JS, LLC.
7931  *
7932  * Originally Released Under LGPL - original licence link has changed is not relivant.
7933  *
7934  * Fork - LGPL
7935  * <script type="text/javascript">
7936  */
7937  
7938
7939 /**
7940  * @class Roo.grid.ColumnModel
7941  * @extends Roo.util.Observable
7942  * This is the default implementation of a ColumnModel used by the Grid. It defines
7943  * the columns in the grid.
7944  * <br>Usage:<br>
7945  <pre><code>
7946  var colModel = new Roo.grid.ColumnModel([
7947         {header: "Ticker", width: 60, sortable: true, locked: true},
7948         {header: "Company Name", width: 150, sortable: true},
7949         {header: "Market Cap.", width: 100, sortable: true},
7950         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7951         {header: "Employees", width: 100, sortable: true, resizable: false}
7952  ]);
7953  </code></pre>
7954  * <p>
7955  
7956  * The config options listed for this class are options which may appear in each
7957  * individual column definition.
7958  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7959  * @constructor
7960  * @param {Object} config An Array of column config objects. See this class's
7961  * config objects for details.
7962 */
7963 Roo.grid.ColumnModel = function(config){
7964         /**
7965      * The config passed into the constructor
7966      */
7967     this.config = []; //config;
7968     this.lookup = {};
7969
7970     // if no id, create one
7971     // if the column does not have a dataIndex mapping,
7972     // map it to the order it is in the config
7973     for(var i = 0, len = config.length; i < len; i++){
7974         this.addColumn(config[i]);
7975         
7976     }
7977
7978     /**
7979      * The width of columns which have no width specified (defaults to 100)
7980      * @type Number
7981      */
7982     this.defaultWidth = 100;
7983
7984     /**
7985      * Default sortable of columns which have no sortable specified (defaults to false)
7986      * @type Boolean
7987      */
7988     this.defaultSortable = false;
7989
7990     this.addEvents({
7991         /**
7992              * @event widthchange
7993              * Fires when the width of a column changes.
7994              * @param {ColumnModel} this
7995              * @param {Number} columnIndex The column index
7996              * @param {Number} newWidth The new width
7997              */
7998             "widthchange": true,
7999         /**
8000              * @event headerchange
8001              * Fires when the text of a header changes.
8002              * @param {ColumnModel} this
8003              * @param {Number} columnIndex The column index
8004              * @param {Number} newText The new header text
8005              */
8006             "headerchange": true,
8007         /**
8008              * @event hiddenchange
8009              * Fires when a column is hidden or "unhidden".
8010              * @param {ColumnModel} this
8011              * @param {Number} columnIndex The column index
8012              * @param {Boolean} hidden true if hidden, false otherwise
8013              */
8014             "hiddenchange": true,
8015             /**
8016          * @event columnmoved
8017          * Fires when a column is moved.
8018          * @param {ColumnModel} this
8019          * @param {Number} oldIndex
8020          * @param {Number} newIndex
8021          */
8022         "columnmoved" : true,
8023         /**
8024          * @event columlockchange
8025          * Fires when a column's locked state is changed
8026          * @param {ColumnModel} this
8027          * @param {Number} colIndex
8028          * @param {Boolean} locked true if locked
8029          */
8030         "columnlockchange" : true
8031     });
8032     Roo.grid.ColumnModel.superclass.constructor.call(this);
8033 };
8034 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8035     /**
8036      * @cfg {String} header The header text to display in the Grid view.
8037      */
8038         /**
8039      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8040      */
8041         /**
8042      * @cfg {String} smHeader Header at Bootsrap Small width
8043      */
8044         /**
8045      * @cfg {String} mdHeader Header at Bootsrap Medium width
8046      */
8047         /**
8048      * @cfg {String} lgHeader Header at Bootsrap Large width
8049      */
8050         /**
8051      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8052      */
8053     /**
8054      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8055      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8056      * specified, the column's index is used as an index into the Record's data Array.
8057      */
8058     /**
8059      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8060      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8061      */
8062     /**
8063      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8064      * Defaults to the value of the {@link #defaultSortable} property.
8065      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8066      */
8067     /**
8068      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8069      */
8070     /**
8071      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8072      */
8073     /**
8074      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8075      */
8076     /**
8077      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8078      */
8079     /**
8080      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8081      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8082      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8083      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8084      */
8085        /**
8086      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8087      */
8088     /**
8089      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8090      */
8091     /**
8092      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8093      */
8094     /**
8095      * @cfg {String} cursor (Optional)
8096      */
8097     /**
8098      * @cfg {String} tooltip (Optional)
8099      */
8100     /**
8101      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8102      */
8103     /**
8104      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8105      */
8106     /**
8107      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8108      */
8109     /**
8110      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8111      */
8112         /**
8113      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8114      */
8115     /**
8116      * Returns the id of the column at the specified index.
8117      * @param {Number} index The column index
8118      * @return {String} the id
8119      */
8120     getColumnId : function(index){
8121         return this.config[index].id;
8122     },
8123
8124     /**
8125      * Returns the column for a specified id.
8126      * @param {String} id The column id
8127      * @return {Object} the column
8128      */
8129     getColumnById : function(id){
8130         return this.lookup[id];
8131     },
8132
8133     
8134     /**
8135      * Returns the column Object for a specified dataIndex.
8136      * @param {String} dataIndex The column dataIndex
8137      * @return {Object|Boolean} the column or false if not found
8138      */
8139     getColumnByDataIndex: function(dataIndex){
8140         var index = this.findColumnIndex(dataIndex);
8141         return index > -1 ? this.config[index] : false;
8142     },
8143     
8144     /**
8145      * Returns the index for a specified column id.
8146      * @param {String} id The column id
8147      * @return {Number} the index, or -1 if not found
8148      */
8149     getIndexById : function(id){
8150         for(var i = 0, len = this.config.length; i < len; i++){
8151             if(this.config[i].id == id){
8152                 return i;
8153             }
8154         }
8155         return -1;
8156     },
8157     
8158     /**
8159      * Returns the index for a specified column dataIndex.
8160      * @param {String} dataIndex The column dataIndex
8161      * @return {Number} the index, or -1 if not found
8162      */
8163     
8164     findColumnIndex : function(dataIndex){
8165         for(var i = 0, len = this.config.length; i < len; i++){
8166             if(this.config[i].dataIndex == dataIndex){
8167                 return i;
8168             }
8169         }
8170         return -1;
8171     },
8172     
8173     
8174     moveColumn : function(oldIndex, newIndex){
8175         var c = this.config[oldIndex];
8176         this.config.splice(oldIndex, 1);
8177         this.config.splice(newIndex, 0, c);
8178         this.dataMap = null;
8179         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8180     },
8181
8182     isLocked : function(colIndex){
8183         return this.config[colIndex].locked === true;
8184     },
8185
8186     setLocked : function(colIndex, value, suppressEvent){
8187         if(this.isLocked(colIndex) == value){
8188             return;
8189         }
8190         this.config[colIndex].locked = value;
8191         if(!suppressEvent){
8192             this.fireEvent("columnlockchange", this, colIndex, value);
8193         }
8194     },
8195
8196     getTotalLockedWidth : function(){
8197         var totalWidth = 0;
8198         for(var i = 0; i < this.config.length; i++){
8199             if(this.isLocked(i) && !this.isHidden(i)){
8200                 this.totalWidth += this.getColumnWidth(i);
8201             }
8202         }
8203         return totalWidth;
8204     },
8205
8206     getLockedCount : function(){
8207         for(var i = 0, len = this.config.length; i < len; i++){
8208             if(!this.isLocked(i)){
8209                 return i;
8210             }
8211         }
8212         
8213         return this.config.length;
8214     },
8215
8216     /**
8217      * Returns the number of columns.
8218      * @return {Number}
8219      */
8220     getColumnCount : function(visibleOnly){
8221         if(visibleOnly === true){
8222             var c = 0;
8223             for(var i = 0, len = this.config.length; i < len; i++){
8224                 if(!this.isHidden(i)){
8225                     c++;
8226                 }
8227             }
8228             return c;
8229         }
8230         return this.config.length;
8231     },
8232
8233     /**
8234      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8235      * @param {Function} fn
8236      * @param {Object} scope (optional)
8237      * @return {Array} result
8238      */
8239     getColumnsBy : function(fn, scope){
8240         var r = [];
8241         for(var i = 0, len = this.config.length; i < len; i++){
8242             var c = this.config[i];
8243             if(fn.call(scope||this, c, i) === true){
8244                 r[r.length] = c;
8245             }
8246         }
8247         return r;
8248     },
8249
8250     /**
8251      * Returns true if the specified column is sortable.
8252      * @param {Number} col The column index
8253      * @return {Boolean}
8254      */
8255     isSortable : function(col){
8256         if(typeof this.config[col].sortable == "undefined"){
8257             return this.defaultSortable;
8258         }
8259         return this.config[col].sortable;
8260     },
8261
8262     /**
8263      * Returns the rendering (formatting) function defined for the column.
8264      * @param {Number} col The column index.
8265      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8266      */
8267     getRenderer : function(col){
8268         if(!this.config[col].renderer){
8269             return Roo.grid.ColumnModel.defaultRenderer;
8270         }
8271         return this.config[col].renderer;
8272     },
8273
8274     /**
8275      * Sets the rendering (formatting) function for a column.
8276      * @param {Number} col The column index
8277      * @param {Function} fn The function to use to process the cell's raw data
8278      * to return HTML markup for the grid view. The render function is called with
8279      * the following parameters:<ul>
8280      * <li>Data value.</li>
8281      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8282      * <li>css A CSS style string to apply to the table cell.</li>
8283      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8284      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8285      * <li>Row index</li>
8286      * <li>Column index</li>
8287      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8288      */
8289     setRenderer : function(col, fn){
8290         this.config[col].renderer = fn;
8291     },
8292
8293     /**
8294      * Returns the width for the specified column.
8295      * @param {Number} col The column index
8296      * @param (optional) {String} gridSize bootstrap width size.
8297      * @return {Number}
8298      */
8299     getColumnWidth : function(col, gridSize)
8300         {
8301                 var cfg = this.config[col];
8302                 
8303                 if (typeof(gridSize) == 'undefined') {
8304                         return cfg.width * 1 || this.defaultWidth;
8305                 }
8306                 if (gridSize === false) { // if we set it..
8307                         return cfg.width || false;
8308                 }
8309                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8310                 
8311                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8312                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8313                                 continue;
8314                         }
8315                         return cfg[ sizes[i] ];
8316                 }
8317                 return 1;
8318                 
8319     },
8320
8321     /**
8322      * Sets the width for a column.
8323      * @param {Number} col The column index
8324      * @param {Number} width The new width
8325      */
8326     setColumnWidth : function(col, width, suppressEvent){
8327         this.config[col].width = width;
8328         this.totalWidth = null;
8329         if(!suppressEvent){
8330              this.fireEvent("widthchange", this, col, width);
8331         }
8332     },
8333
8334     /**
8335      * Returns the total width of all columns.
8336      * @param {Boolean} includeHidden True to include hidden column widths
8337      * @return {Number}
8338      */
8339     getTotalWidth : function(includeHidden){
8340         if(!this.totalWidth){
8341             this.totalWidth = 0;
8342             for(var i = 0, len = this.config.length; i < len; i++){
8343                 if(includeHidden || !this.isHidden(i)){
8344                     this.totalWidth += this.getColumnWidth(i);
8345                 }
8346             }
8347         }
8348         return this.totalWidth;
8349     },
8350
8351     /**
8352      * Returns the header for the specified column.
8353      * @param {Number} col The column index
8354      * @return {String}
8355      */
8356     getColumnHeader : function(col){
8357         return this.config[col].header;
8358     },
8359
8360     /**
8361      * Sets the header for a column.
8362      * @param {Number} col The column index
8363      * @param {String} header The new header
8364      */
8365     setColumnHeader : function(col, header){
8366         this.config[col].header = header;
8367         this.fireEvent("headerchange", this, col, header);
8368     },
8369
8370     /**
8371      * Returns the tooltip for the specified column.
8372      * @param {Number} col The column index
8373      * @return {String}
8374      */
8375     getColumnTooltip : function(col){
8376             return this.config[col].tooltip;
8377     },
8378     /**
8379      * Sets the tooltip for a column.
8380      * @param {Number} col The column index
8381      * @param {String} tooltip The new tooltip
8382      */
8383     setColumnTooltip : function(col, tooltip){
8384             this.config[col].tooltip = tooltip;
8385     },
8386
8387     /**
8388      * Returns the dataIndex for the specified column.
8389      * @param {Number} col The column index
8390      * @return {Number}
8391      */
8392     getDataIndex : function(col){
8393         return this.config[col].dataIndex;
8394     },
8395
8396     /**
8397      * Sets the dataIndex for a column.
8398      * @param {Number} col The column index
8399      * @param {Number} dataIndex The new dataIndex
8400      */
8401     setDataIndex : function(col, dataIndex){
8402         this.config[col].dataIndex = dataIndex;
8403     },
8404
8405     
8406     
8407     /**
8408      * Returns true if the cell is editable.
8409      * @param {Number} colIndex The column index
8410      * @param {Number} rowIndex The row index - this is nto actually used..?
8411      * @return {Boolean}
8412      */
8413     isCellEditable : function(colIndex, rowIndex){
8414         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8415     },
8416
8417     /**
8418      * Returns the editor defined for the cell/column.
8419      * return false or null to disable editing.
8420      * @param {Number} colIndex The column index
8421      * @param {Number} rowIndex The row index
8422      * @return {Object}
8423      */
8424     getCellEditor : function(colIndex, rowIndex){
8425         return this.config[colIndex].editor;
8426     },
8427
8428     /**
8429      * Sets if a column is editable.
8430      * @param {Number} col The column index
8431      * @param {Boolean} editable True if the column is editable
8432      */
8433     setEditable : function(col, editable){
8434         this.config[col].editable = editable;
8435     },
8436
8437
8438     /**
8439      * Returns true if the column is hidden.
8440      * @param {Number} colIndex The column index
8441      * @return {Boolean}
8442      */
8443     isHidden : function(colIndex){
8444         return this.config[colIndex].hidden;
8445     },
8446
8447
8448     /**
8449      * Returns true if the column width cannot be changed
8450      */
8451     isFixed : function(colIndex){
8452         return this.config[colIndex].fixed;
8453     },
8454
8455     /**
8456      * Returns true if the column can be resized
8457      * @return {Boolean}
8458      */
8459     isResizable : function(colIndex){
8460         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8461     },
8462     /**
8463      * Sets if a column is hidden.
8464      * @param {Number} colIndex The column index
8465      * @param {Boolean} hidden True if the column is hidden
8466      */
8467     setHidden : function(colIndex, hidden){
8468         this.config[colIndex].hidden = hidden;
8469         this.totalWidth = null;
8470         this.fireEvent("hiddenchange", this, colIndex, hidden);
8471     },
8472
8473     /**
8474      * Sets the editor for a column.
8475      * @param {Number} col The column index
8476      * @param {Object} editor The editor object
8477      */
8478     setEditor : function(col, editor){
8479         this.config[col].editor = editor;
8480     },
8481     /**
8482      * Add a column (experimental...) - defaults to adding to the end..
8483      * @param {Object} config 
8484     */
8485     addColumn : function(c)
8486     {
8487     
8488         var i = this.config.length;
8489         this.config[i] = c;
8490         
8491         if(typeof c.dataIndex == "undefined"){
8492             c.dataIndex = i;
8493         }
8494         if(typeof c.renderer == "string"){
8495             c.renderer = Roo.util.Format[c.renderer];
8496         }
8497         if(typeof c.id == "undefined"){
8498             c.id = Roo.id();
8499         }
8500         if(c.editor && c.editor.xtype){
8501             c.editor  = Roo.factory(c.editor, Roo.grid);
8502         }
8503         if(c.editor && c.editor.isFormField){
8504             c.editor = new Roo.grid.GridEditor(c.editor);
8505         }
8506         this.lookup[c.id] = c;
8507     }
8508     
8509 });
8510
8511 Roo.grid.ColumnModel.defaultRenderer = function(value)
8512 {
8513     if(typeof value == "object") {
8514         return value;
8515     }
8516         if(typeof value == "string" && value.length < 1){
8517             return "&#160;";
8518         }
8519     
8520         return String.format("{0}", value);
8521 };
8522
8523 // Alias for backwards compatibility
8524 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8525 /*
8526  * Based on:
8527  * Ext JS Library 1.1.1
8528  * Copyright(c) 2006-2007, Ext JS, LLC.
8529  *
8530  * Originally Released Under LGPL - original licence link has changed is not relivant.
8531  *
8532  * Fork - LGPL
8533  * <script type="text/javascript">
8534  */
8535  
8536 /**
8537  * @class Roo.LoadMask
8538  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8539  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8540  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8541  * element's UpdateManager load indicator and will be destroyed after the initial load.
8542  * @constructor
8543  * Create a new LoadMask
8544  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8545  * @param {Object} config The config object
8546  */
8547 Roo.LoadMask = function(el, config){
8548     this.el = Roo.get(el);
8549     Roo.apply(this, config);
8550     if(this.store){
8551         this.store.on('beforeload', this.onBeforeLoad, this);
8552         this.store.on('load', this.onLoad, this);
8553         this.store.on('loadexception', this.onLoadException, this);
8554         this.removeMask = false;
8555     }else{
8556         var um = this.el.getUpdateManager();
8557         um.showLoadIndicator = false; // disable the default indicator
8558         um.on('beforeupdate', this.onBeforeLoad, this);
8559         um.on('update', this.onLoad, this);
8560         um.on('failure', this.onLoad, this);
8561         this.removeMask = true;
8562     }
8563 };
8564
8565 Roo.LoadMask.prototype = {
8566     /**
8567      * @cfg {Boolean} removeMask
8568      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8569      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8570      */
8571     removeMask : false,
8572     /**
8573      * @cfg {String} msg
8574      * The text to display in a centered loading message box (defaults to 'Loading...')
8575      */
8576     msg : 'Loading...',
8577     /**
8578      * @cfg {String} msgCls
8579      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8580      */
8581     msgCls : 'x-mask-loading',
8582
8583     /**
8584      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8585      * @type Boolean
8586      */
8587     disabled: false,
8588
8589     /**
8590      * Disables the mask to prevent it from being displayed
8591      */
8592     disable : function(){
8593        this.disabled = true;
8594     },
8595
8596     /**
8597      * Enables the mask so that it can be displayed
8598      */
8599     enable : function(){
8600         this.disabled = false;
8601     },
8602     
8603     onLoadException : function()
8604     {
8605         Roo.log(arguments);
8606         
8607         if (typeof(arguments[3]) != 'undefined') {
8608             Roo.MessageBox.alert("Error loading",arguments[3]);
8609         } 
8610         /*
8611         try {
8612             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8613                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8614             }   
8615         } catch(e) {
8616             
8617         }
8618         */
8619     
8620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8621     },
8622     // private
8623     onLoad : function()
8624     {
8625         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8626     },
8627
8628     // private
8629     onBeforeLoad : function(){
8630         if(!this.disabled){
8631             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8632         }
8633     },
8634
8635     // private
8636     destroy : function(){
8637         if(this.store){
8638             this.store.un('beforeload', this.onBeforeLoad, this);
8639             this.store.un('load', this.onLoad, this);
8640             this.store.un('loadexception', this.onLoadException, this);
8641         }else{
8642             var um = this.el.getUpdateManager();
8643             um.un('beforeupdate', this.onBeforeLoad, this);
8644             um.un('update', this.onLoad, this);
8645             um.un('failure', this.onLoad, this);
8646         }
8647     }
8648 };/**
8649  * @class Roo.bootstrap.Table
8650  * @licence LGBL
8651  * @extends Roo.bootstrap.Component
8652  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8653  * Similar to Roo.grid.Grid
8654  * <pre><code>
8655  var table = Roo.factory({
8656     xtype : 'Table',
8657     xns : Roo.bootstrap,
8658     autoSizeColumns: true,
8659     
8660     
8661     store : {
8662         xtype : 'Store',
8663         xns : Roo.data,
8664         remoteSort : true,
8665         sortInfo : { direction : 'ASC', field: 'name' },
8666         proxy : {
8667            xtype : 'HttpProxy',
8668            xns : Roo.data,
8669            method : 'GET',
8670            url : 'https://example.com/some.data.url.json'
8671         },
8672         reader : {
8673            xtype : 'JsonReader',
8674            xns : Roo.data,
8675            fields : [ 'id', 'name', whatever' ],
8676            id : 'id',
8677            root : 'data'
8678         }
8679     },
8680     cm : [
8681         {
8682             xtype : 'ColumnModel',
8683             xns : Roo.grid,
8684             align : 'center',
8685             cursor : 'pointer',
8686             dataIndex : 'is_in_group',
8687             header : "Name",
8688             sortable : true,
8689             renderer : function(v, x , r) {  
8690             
8691                 return String.format("{0}", v)
8692             }
8693             width : 3
8694         } // more columns..
8695     ],
8696     selModel : {
8697         xtype : 'RowSelectionModel',
8698         xns : Roo.bootstrap.Table
8699         // you can add listeners to catch selection change here....
8700     }
8701      
8702
8703  });
8704  // set any options
8705  grid.render(Roo.get("some-div"));
8706 </code></pre>
8707
8708 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8709
8710
8711
8712  *
8713  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8714  * @cfg {Roo.data.Store} store The data store to use
8715  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8716  * 
8717  * @cfg {String} cls table class
8718  *
8719  * 
8720  * @cfg {boolean} striped Should the rows be alternative striped
8721  * @cfg {boolean} bordered Add borders to the table
8722  * @cfg {boolean} hover Add hover highlighting
8723  * @cfg {boolean} condensed Format condensed
8724  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
8725  *                also adds table-responsive (see bootstrap docs for details)
8726  * @cfg {Boolean} loadMask (true|false) default false
8727  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8728  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8729  * @cfg {Boolean} rowSelection (true|false) default false
8730  * @cfg {Boolean} cellSelection (true|false) default false
8731  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8732  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8733  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8734  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8735  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8736  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8737  * 
8738  * @constructor
8739  * Create a new Table
8740  * @param {Object} config The config object
8741  */
8742
8743 Roo.bootstrap.Table = function(config)
8744 {
8745     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8746      
8747     // BC...
8748     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8749     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8750     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8751     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8752     
8753     this.view = this; // compat with grid.
8754     
8755     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8756     if (this.sm) {
8757         this.sm.grid = this;
8758         this.selModel = Roo.factory(this.sm, Roo.grid);
8759         this.sm = this.selModel;
8760         this.sm.xmodule = this.xmodule || false;
8761     }
8762     
8763     if (this.cm && typeof(this.cm.config) == 'undefined') {
8764         this.colModel = new Roo.grid.ColumnModel(this.cm);
8765         this.cm = this.colModel;
8766         this.cm.xmodule = this.xmodule || false;
8767     }
8768     if (this.store) {
8769         this.store= Roo.factory(this.store, Roo.data);
8770         this.ds = this.store;
8771         this.ds.xmodule = this.xmodule || false;
8772          
8773     }
8774     if (this.footer && this.store) {
8775         this.footer.dataSource = this.ds;
8776         this.footer = Roo.factory(this.footer);
8777     }
8778     
8779     /** @private */
8780     this.addEvents({
8781         /**
8782          * @event cellclick
8783          * Fires when a cell is clicked
8784          * @param {Roo.bootstrap.Table} this
8785          * @param {Roo.Element} el
8786          * @param {Number} rowIndex
8787          * @param {Number} columnIndex
8788          * @param {Roo.EventObject} e
8789          */
8790         "cellclick" : true,
8791         /**
8792          * @event celldblclick
8793          * Fires when a cell is double clicked
8794          * @param {Roo.bootstrap.Table} this
8795          * @param {Roo.Element} el
8796          * @param {Number} rowIndex
8797          * @param {Number} columnIndex
8798          * @param {Roo.EventObject} e
8799          */
8800         "celldblclick" : true,
8801         /**
8802          * @event rowclick
8803          * Fires when a row is clicked
8804          * @param {Roo.bootstrap.Table} this
8805          * @param {Roo.Element} el
8806          * @param {Number} rowIndex
8807          * @param {Roo.EventObject} e
8808          */
8809         "rowclick" : true,
8810         /**
8811          * @event rowdblclick
8812          * Fires when a row is double clicked
8813          * @param {Roo.bootstrap.Table} this
8814          * @param {Roo.Element} el
8815          * @param {Number} rowIndex
8816          * @param {Roo.EventObject} e
8817          */
8818         "rowdblclick" : true,
8819         /**
8820          * @event mouseover
8821          * Fires when a mouseover occur
8822          * @param {Roo.bootstrap.Table} this
8823          * @param {Roo.Element} el
8824          * @param {Number} rowIndex
8825          * @param {Number} columnIndex
8826          * @param {Roo.EventObject} e
8827          */
8828         "mouseover" : true,
8829         /**
8830          * @event mouseout
8831          * Fires when a mouseout occur
8832          * @param {Roo.bootstrap.Table} this
8833          * @param {Roo.Element} el
8834          * @param {Number} rowIndex
8835          * @param {Number} columnIndex
8836          * @param {Roo.EventObject} e
8837          */
8838         "mouseout" : true,
8839         /**
8840          * @event rowclass
8841          * Fires when a row is rendered, so you can change add a style to it.
8842          * @param {Roo.bootstrap.Table} this
8843          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8844          */
8845         'rowclass' : true,
8846           /**
8847          * @event rowsrendered
8848          * Fires when all the  rows have been rendered
8849          * @param {Roo.bootstrap.Table} this
8850          */
8851         'rowsrendered' : true,
8852         /**
8853          * @event contextmenu
8854          * The raw contextmenu event for the entire grid.
8855          * @param {Roo.EventObject} e
8856          */
8857         "contextmenu" : true,
8858         /**
8859          * @event rowcontextmenu
8860          * Fires when a row is right clicked
8861          * @param {Roo.bootstrap.Table} this
8862          * @param {Number} rowIndex
8863          * @param {Roo.EventObject} e
8864          */
8865         "rowcontextmenu" : true,
8866         /**
8867          * @event cellcontextmenu
8868          * Fires when a cell is right clicked
8869          * @param {Roo.bootstrap.Table} this
8870          * @param {Number} rowIndex
8871          * @param {Number} cellIndex
8872          * @param {Roo.EventObject} e
8873          */
8874          "cellcontextmenu" : true,
8875          /**
8876          * @event headercontextmenu
8877          * Fires when a header is right clicked
8878          * @param {Roo.bootstrap.Table} this
8879          * @param {Number} columnIndex
8880          * @param {Roo.EventObject} e
8881          */
8882         "headercontextmenu" : true,
8883         /**
8884          * @event mousedown
8885          * The raw mousedown event for the entire grid.
8886          * @param {Roo.EventObject} e
8887          */
8888         "mousedown" : true
8889         
8890     });
8891 };
8892
8893 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8894     
8895     cls: false,
8896     
8897     striped : false,
8898     scrollBody : false,
8899     bordered: false,
8900     hover:  false,
8901     condensed : false,
8902     responsive : false,
8903     sm : false,
8904     cm : false,
8905     store : false,
8906     loadMask : false,
8907     footerShow : true,
8908     headerShow : true,
8909     enableColumnResize: true,
8910   
8911     rowSelection : false,
8912     cellSelection : false,
8913     layout : false,
8914
8915     minColumnWidth : 50,
8916     
8917     // Roo.Element - the tbody
8918     bodyEl: false,  // <tbody> Roo.Element - thead element    
8919     headEl: false,  // <thead> Roo.Element - thead element
8920     resizeProxy : false, // proxy element for dragging?
8921
8922
8923     
8924     container: false, // used by gridpanel...
8925     
8926     lazyLoad : false,
8927     
8928     CSS : Roo.util.CSS,
8929     
8930     auto_hide_footer : false,
8931     
8932     view: false, // actually points to this..
8933     
8934     getAutoCreate : function()
8935     {
8936         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8937         
8938         cfg = {
8939             tag: 'table',
8940             cls : 'table', 
8941             cn : []
8942         };
8943         // this get's auto added by panel.Grid
8944         if (this.scrollBody) {
8945             cfg.cls += ' table-body-fixed';
8946         }    
8947         if (this.striped) {
8948             cfg.cls += ' table-striped';
8949         }
8950         
8951         if (this.hover) {
8952             cfg.cls += ' table-hover';
8953         }
8954         if (this.bordered) {
8955             cfg.cls += ' table-bordered';
8956         }
8957         if (this.condensed) {
8958             cfg.cls += ' table-condensed';
8959         }
8960         
8961         if (this.responsive) {
8962             cfg.cls += ' table-responsive';
8963         }
8964         
8965         if (this.cls) {
8966             cfg.cls+=  ' ' +this.cls;
8967         }
8968         
8969         
8970         
8971         if (this.layout) {
8972             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8973         }
8974         
8975         if(this.store || this.cm){
8976             if(this.headerShow){
8977                 cfg.cn.push(this.renderHeader());
8978             }
8979             
8980             cfg.cn.push(this.renderBody());
8981             
8982             if(this.footerShow){
8983                 cfg.cn.push(this.renderFooter());
8984             }
8985             // where does this come from?
8986             //cfg.cls+=  ' TableGrid';
8987         }
8988         
8989         return { cn : [ cfg ] };
8990     },
8991     
8992     initEvents : function()
8993     {   
8994         if(!this.store || !this.cm){
8995             return;
8996         }
8997         if (this.selModel) {
8998             this.selModel.initEvents();
8999         }
9000         
9001         
9002         //Roo.log('initEvents with ds!!!!');
9003         
9004         this.bodyEl = this.el.select('tbody', true).first();
9005         this.headEl = this.el.select('thead', true).first();
9006         this.mainFoot = this.el.select('tfoot', true).first();
9007         
9008         
9009         
9010         
9011         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9012             e.on('click', this.sort, this);
9013         }, this);
9014         
9015         
9016         // why is this done????? = it breaks dialogs??
9017         //this.parent().el.setStyle('position', 'relative');
9018         
9019         
9020         if (this.footer) {
9021             this.footer.parentId = this.id;
9022             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9023             
9024             if(this.lazyLoad){
9025                 this.el.select('tfoot tr td').first().addClass('hide');
9026             }
9027         } 
9028         
9029         if(this.loadMask) {
9030             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9031         }
9032         
9033         this.store.on('load', this.onLoad, this);
9034         this.store.on('beforeload', this.onBeforeLoad, this);
9035         this.store.on('update', this.onUpdate, this);
9036         this.store.on('add', this.onAdd, this);
9037         this.store.on("clear", this.clear, this);
9038         
9039         this.el.on("contextmenu", this.onContextMenu, this);
9040         
9041         
9042         this.cm.on("headerchange", this.onHeaderChange, this);
9043         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9044
9045  //?? does bodyEl get replaced on render?
9046         this.bodyEl.on("click", this.onClick, this);
9047         this.bodyEl.on("dblclick", this.onDblClick, this);        
9048         this.bodyEl.on('scroll', this.onBodyScroll, this);
9049
9050         // guessing mainbody will work - this relays usually caught by selmodel at present.
9051         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9052   
9053   
9054         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9055         
9056   
9057         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9058             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9059         }
9060         
9061         this.initCSS();
9062     },
9063     // Compatibility with grid - we implement all the view features at present.
9064     getView : function()
9065     {
9066         return this;
9067     },
9068     
9069     initCSS : function()
9070     {
9071         
9072         
9073         var cm = this.cm, styles = [];
9074         this.CSS.removeStyleSheet(this.id + '-cssrules');
9075         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9076         // we can honour xs/sm/md/xl  as widths...
9077         // we first have to decide what widht we are currently at...
9078         var sz = Roo.getGridSize();
9079         
9080         var total = 0;
9081         var last = -1;
9082         var cols = []; // visable cols.
9083         var total_abs = 0;
9084         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9085             var w = cm.getColumnWidth(i, false);
9086             if(cm.isHidden(i)){
9087                 cols.push( { rel : false, abs : 0 });
9088                 continue;
9089             }
9090             if (w !== false) {
9091                 cols.push( { rel : false, abs : w });
9092                 total_abs += w;
9093                 last = i; // not really..
9094                 continue;
9095             }
9096             var w = cm.getColumnWidth(i, sz);
9097             if (w > 0) {
9098                 last = i
9099             }
9100             total += w;
9101             cols.push( { rel : w, abs : false });
9102         }
9103         
9104         var avail = this.bodyEl.dom.clientWidth - total_abs;
9105         
9106         var unitWidth = Math.floor(avail / total);
9107         var rem = avail - (unitWidth * total);
9108         
9109         var hidden, width, pos = 0 , splithide , left;
9110         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9111             
9112             hidden = 'display:none;';
9113             left = '';
9114             width  = 'width:0px;';
9115             splithide = '';
9116             if(!cm.isHidden(i)){
9117                 hidden = '';
9118                 
9119                 
9120                 // we can honour xs/sm/md/xl ?
9121                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9122                 if (w===0) {
9123                     hidden = 'display:none;';
9124                 }
9125                 // width should return a small number...
9126                 if (i == last) {
9127                     w+=rem; // add the remaining with..
9128                 }
9129                 pos += w;
9130                 left = "left:" + (pos -4) + "px;";
9131                 width = "width:" + w+ "px;";
9132                 
9133             }
9134             if (this.responsive) {
9135                 width = '';
9136                 left = '';
9137                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9138                 splithide = 'display: none;';
9139             }
9140             
9141             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9142             if (this.headEl) {
9143                 if (i == last) {
9144                     splithide = 'display:none;';
9145                 }
9146                 
9147                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9148                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9149                 );
9150             }
9151             
9152         }
9153         //Roo.log(styles.join(''));
9154         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9155         
9156     },
9157     
9158     
9159     
9160     onContextMenu : function(e, t)
9161     {
9162         this.processEvent("contextmenu", e);
9163     },
9164     
9165     processEvent : function(name, e)
9166     {
9167         if (name != 'touchstart' ) {
9168             this.fireEvent(name, e);    
9169         }
9170         
9171         var t = e.getTarget();
9172         
9173         var cell = Roo.get(t);
9174         
9175         if(!cell){
9176             return;
9177         }
9178         
9179         if(cell.findParent('tfoot', false, true)){
9180             return;
9181         }
9182         
9183         if(cell.findParent('thead', false, true)){
9184             
9185             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9186                 cell = Roo.get(t).findParent('th', false, true);
9187                 if (!cell) {
9188                     Roo.log("failed to find th in thead?");
9189                     Roo.log(e.getTarget());
9190                     return;
9191                 }
9192             }
9193             
9194             var cellIndex = cell.dom.cellIndex;
9195             
9196             var ename = name == 'touchstart' ? 'click' : name;
9197             this.fireEvent("header" + ename, this, cellIndex, e);
9198             
9199             return;
9200         }
9201         
9202         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9203             cell = Roo.get(t).findParent('td', false, true);
9204             if (!cell) {
9205                 Roo.log("failed to find th in tbody?");
9206                 Roo.log(e.getTarget());
9207                 return;
9208             }
9209         }
9210         
9211         var row = cell.findParent('tr', false, true);
9212         var cellIndex = cell.dom.cellIndex;
9213         var rowIndex = row.dom.rowIndex - 1;
9214         
9215         if(row !== false){
9216             
9217             this.fireEvent("row" + name, this, rowIndex, e);
9218             
9219             if(cell !== false){
9220             
9221                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9222             }
9223         }
9224         
9225     },
9226     
9227     onMouseover : function(e, el)
9228     {
9229         var cell = Roo.get(el);
9230         
9231         if(!cell){
9232             return;
9233         }
9234         
9235         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9236             cell = cell.findParent('td', false, true);
9237         }
9238         
9239         var row = cell.findParent('tr', false, true);
9240         var cellIndex = cell.dom.cellIndex;
9241         var rowIndex = row.dom.rowIndex - 1; // start from 0
9242         
9243         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9244         
9245     },
9246     
9247     onMouseout : function(e, el)
9248     {
9249         var cell = Roo.get(el);
9250         
9251         if(!cell){
9252             return;
9253         }
9254         
9255         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9256             cell = cell.findParent('td', false, true);
9257         }
9258         
9259         var row = cell.findParent('tr', false, true);
9260         var cellIndex = cell.dom.cellIndex;
9261         var rowIndex = row.dom.rowIndex - 1; // start from 0
9262         
9263         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9264         
9265     },
9266     
9267     onClick : function(e, el)
9268     {
9269         var cell = Roo.get(el);
9270         
9271         if(!cell || (!this.cellSelection && !this.rowSelection)){
9272             return;
9273         }
9274         
9275         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9276             cell = cell.findParent('td', false, true);
9277         }
9278         
9279         if(!cell || typeof(cell) == 'undefined'){
9280             return;
9281         }
9282         
9283         var row = cell.findParent('tr', false, true);
9284         
9285         if(!row || typeof(row) == 'undefined'){
9286             return;
9287         }
9288         
9289         var cellIndex = cell.dom.cellIndex;
9290         var rowIndex = this.getRowIndex(row);
9291         
9292         // why??? - should these not be based on SelectionModel?
9293         //if(this.cellSelection){
9294             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9295         //}
9296         
9297         //if(this.rowSelection){
9298             this.fireEvent('rowclick', this, row, rowIndex, e);
9299         //}
9300          
9301     },
9302         
9303     onDblClick : function(e,el)
9304     {
9305         var cell = Roo.get(el);
9306         
9307         if(!cell || (!this.cellSelection && !this.rowSelection)){
9308             return;
9309         }
9310         
9311         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9312             cell = cell.findParent('td', false, true);
9313         }
9314         
9315         if(!cell || typeof(cell) == 'undefined'){
9316             return;
9317         }
9318         
9319         var row = cell.findParent('tr', false, true);
9320         
9321         if(!row || typeof(row) == 'undefined'){
9322             return;
9323         }
9324         
9325         var cellIndex = cell.dom.cellIndex;
9326         var rowIndex = this.getRowIndex(row);
9327         
9328         if(this.cellSelection){
9329             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9330         }
9331         
9332         if(this.rowSelection){
9333             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9334         }
9335     },
9336     findRowIndex : function(el)
9337     {
9338         var cell = Roo.get(el);
9339         if(!cell) {
9340             return false;
9341         }
9342         var row = cell.findParent('tr', false, true);
9343         
9344         if(!row || typeof(row) == 'undefined'){
9345             return false;
9346         }
9347         return this.getRowIndex(row);
9348     },
9349     sort : function(e,el)
9350     {
9351         var col = Roo.get(el);
9352         
9353         if(!col.hasClass('sortable')){
9354             return;
9355         }
9356         
9357         var sort = col.attr('sort');
9358         var dir = 'ASC';
9359         
9360         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9361             dir = 'DESC';
9362         }
9363         
9364         this.store.sortInfo = {field : sort, direction : dir};
9365         
9366         if (this.footer) {
9367             Roo.log("calling footer first");
9368             this.footer.onClick('first');
9369         } else {
9370         
9371             this.store.load({ params : { start : 0 } });
9372         }
9373     },
9374     
9375     renderHeader : function()
9376     {
9377         var header = {
9378             tag: 'thead',
9379             cn : []
9380         };
9381         
9382         var cm = this.cm;
9383         this.totalWidth = 0;
9384         
9385         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9386             
9387             var config = cm.config[i];
9388             
9389             var c = {
9390                 tag: 'th',
9391                 cls : 'x-hcol-' + i,
9392                 style : '',
9393                 
9394                 html: cm.getColumnHeader(i)
9395             };
9396             
9397             var tooltip = cm.getColumnTooltip(i);
9398             if (tooltip) {
9399                 c.tooltip = tooltip;
9400             }
9401             
9402             
9403             var hh = '';
9404             
9405             if(typeof(config.sortable) != 'undefined' && config.sortable){
9406                 c.cls += ' sortable';
9407                 c.html = '<i class="fa"></i>' + c.html;
9408             }
9409             
9410             // could use BS4 hidden-..-down 
9411             
9412             if(typeof(config.lgHeader) != 'undefined'){
9413                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9414             }
9415             
9416             if(typeof(config.mdHeader) != 'undefined'){
9417                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9418             }
9419             
9420             if(typeof(config.smHeader) != 'undefined'){
9421                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9422             }
9423             
9424             if(typeof(config.xsHeader) != 'undefined'){
9425                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9426             }
9427             
9428             if(hh.length){
9429                 c.html = hh;
9430             }
9431             
9432             if(typeof(config.tooltip) != 'undefined'){
9433                 c.tooltip = config.tooltip;
9434             }
9435             
9436             if(typeof(config.colspan) != 'undefined'){
9437                 c.colspan = config.colspan;
9438             }
9439             
9440             // hidden is handled by CSS now
9441             
9442             if(typeof(config.dataIndex) != 'undefined'){
9443                 c.sort = config.dataIndex;
9444             }
9445             
9446            
9447             
9448             if(typeof(config.align) != 'undefined' && config.align.length){
9449                 c.style += ' text-align:' + config.align + ';';
9450             }
9451             
9452             /* width is done in CSS
9453              *if(typeof(config.width) != 'undefined'){
9454                 c.style += ' width:' + config.width + 'px;';
9455                 this.totalWidth += config.width;
9456             } else {
9457                 this.totalWidth += 100; // assume minimum of 100 per column?
9458             }
9459             */
9460             
9461             if(typeof(config.cls) != 'undefined'){
9462                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9463             }
9464             // this is the bit that doesnt reall work at all...
9465             
9466             if (this.responsive) {
9467                  
9468             
9469                 ['xs','sm','md','lg'].map(function(size){
9470                     
9471                     if(typeof(config[size]) == 'undefined'){
9472                         return;
9473                     }
9474                      
9475                     if (!config[size]) { // 0 = hidden
9476                         // BS 4 '0' is treated as hide that column and below.
9477                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9478                         return;
9479                     }
9480                     
9481                     c.cls += ' col-' + size + '-' + config[size] + (
9482                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9483                     );
9484                     
9485                     
9486                 });
9487             }
9488             // at the end?
9489             
9490             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9491             
9492             
9493             
9494             
9495             header.cn.push(c)
9496         }
9497         
9498         return header;
9499     },
9500     
9501     renderBody : function()
9502     {
9503         var body = {
9504             tag: 'tbody',
9505             cn : [
9506                 {
9507                     tag: 'tr',
9508                     cn : [
9509                         {
9510                             tag : 'td',
9511                             colspan :  this.cm.getColumnCount()
9512                         }
9513                     ]
9514                 }
9515             ]
9516         };
9517         
9518         return body;
9519     },
9520     
9521     renderFooter : function()
9522     {
9523         var footer = {
9524             tag: 'tfoot',
9525             cn : [
9526                 {
9527                     tag: 'tr',
9528                     cn : [
9529                         {
9530                             tag : 'td',
9531                             colspan :  this.cm.getColumnCount()
9532                         }
9533                     ]
9534                 }
9535             ]
9536         };
9537         
9538         return footer;
9539     },
9540     
9541     
9542     
9543     onLoad : function()
9544     {
9545 //        Roo.log('ds onload');
9546         this.clear();
9547         
9548         var _this = this;
9549         var cm = this.cm;
9550         var ds = this.store;
9551         
9552         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9553             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9554             if (_this.store.sortInfo) {
9555                     
9556                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9557                     e.select('i', true).addClass(['fa-arrow-up']);
9558                 }
9559                 
9560                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9561                     e.select('i', true).addClass(['fa-arrow-down']);
9562                 }
9563             }
9564         });
9565         
9566         var tbody =  this.bodyEl;
9567               
9568         if(ds.getCount() > 0){
9569             ds.data.each(function(d,rowIndex){
9570                 var row =  this.renderRow(cm, ds, rowIndex);
9571                 
9572                 tbody.createChild(row);
9573                 
9574                 var _this = this;
9575                 
9576                 if(row.cellObjects.length){
9577                     Roo.each(row.cellObjects, function(r){
9578                         _this.renderCellObject(r);
9579                     })
9580                 }
9581                 
9582             }, this);
9583         }
9584         
9585         var tfoot = this.el.select('tfoot', true).first();
9586         
9587         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9588             
9589             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9590             
9591             var total = this.ds.getTotalCount();
9592             
9593             if(this.footer.pageSize < total){
9594                 this.mainFoot.show();
9595             }
9596         }
9597         
9598         Roo.each(this.el.select('tbody td', true).elements, function(e){
9599             e.on('mouseover', _this.onMouseover, _this);
9600         });
9601         
9602         Roo.each(this.el.select('tbody td', true).elements, function(e){
9603             e.on('mouseout', _this.onMouseout, _this);
9604         });
9605         this.fireEvent('rowsrendered', this);
9606         
9607         this.autoSize();
9608         
9609         this.initCSS(); /// resize cols
9610
9611         
9612     },
9613     
9614     
9615     onUpdate : function(ds,record)
9616     {
9617         this.refreshRow(record);
9618         this.autoSize();
9619     },
9620     
9621     onRemove : function(ds, record, index, isUpdate){
9622         if(isUpdate !== true){
9623             this.fireEvent("beforerowremoved", this, index, record);
9624         }
9625         var bt = this.bodyEl.dom;
9626         
9627         var rows = this.el.select('tbody > tr', true).elements;
9628         
9629         if(typeof(rows[index]) != 'undefined'){
9630             bt.removeChild(rows[index].dom);
9631         }
9632         
9633 //        if(bt.rows[index]){
9634 //            bt.removeChild(bt.rows[index]);
9635 //        }
9636         
9637         if(isUpdate !== true){
9638             //this.stripeRows(index);
9639             //this.syncRowHeights(index, index);
9640             //this.layout();
9641             this.fireEvent("rowremoved", this, index, record);
9642         }
9643     },
9644     
9645     onAdd : function(ds, records, rowIndex)
9646     {
9647         //Roo.log('on Add called');
9648         // - note this does not handle multiple adding very well..
9649         var bt = this.bodyEl.dom;
9650         for (var i =0 ; i < records.length;i++) {
9651             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9652             //Roo.log(records[i]);
9653             //Roo.log(this.store.getAt(rowIndex+i));
9654             this.insertRow(this.store, rowIndex + i, false);
9655             return;
9656         }
9657         
9658     },
9659     
9660     
9661     refreshRow : function(record){
9662         var ds = this.store, index;
9663         if(typeof record == 'number'){
9664             index = record;
9665             record = ds.getAt(index);
9666         }else{
9667             index = ds.indexOf(record);
9668             if (index < 0) {
9669                 return; // should not happen - but seems to 
9670             }
9671         }
9672         this.insertRow(ds, index, true);
9673         this.autoSize();
9674         this.onRemove(ds, record, index+1, true);
9675         this.autoSize();
9676         //this.syncRowHeights(index, index);
9677         //this.layout();
9678         this.fireEvent("rowupdated", this, index, record);
9679     },
9680     // private - called by RowSelection
9681     onRowSelect : function(rowIndex){
9682         var row = this.getRowDom(rowIndex);
9683         row.addClass(['bg-info','info']);
9684     },
9685     // private - called by RowSelection
9686     onRowDeselect : function(rowIndex)
9687     {
9688         if (rowIndex < 0) {
9689             return;
9690         }
9691         var row = this.getRowDom(rowIndex);
9692         row.removeClass(['bg-info','info']);
9693     },
9694       /**
9695      * Focuses the specified row.
9696      * @param {Number} row The row index
9697      */
9698     focusRow : function(row)
9699     {
9700         //Roo.log('GridView.focusRow');
9701         var x = this.bodyEl.dom.scrollLeft;
9702         this.focusCell(row, 0, false);
9703         this.bodyEl.dom.scrollLeft = x;
9704
9705     },
9706      /**
9707      * Focuses the specified cell.
9708      * @param {Number} row The row index
9709      * @param {Number} col The column index
9710      * @param {Boolean} hscroll false to disable horizontal scrolling
9711      */
9712     focusCell : function(row, col, hscroll)
9713     {
9714         //Roo.log('GridView.focusCell');
9715         var el = this.ensureVisible(row, col, hscroll);
9716         // not sure what focusEL achives = it's a <a> pos relative 
9717         //this.focusEl.alignTo(el, "tl-tl");
9718         //if(Roo.isGecko){
9719         //    this.focusEl.focus();
9720         //}else{
9721         //    this.focusEl.focus.defer(1, this.focusEl);
9722         //}
9723     },
9724     
9725      /**
9726      * Scrolls the specified cell into view
9727      * @param {Number} row The row index
9728      * @param {Number} col The column index
9729      * @param {Boolean} hscroll false to disable horizontal scrolling
9730      */
9731     ensureVisible : function(row, col, hscroll)
9732     {
9733         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9734         //return null; //disable for testing.
9735         if(typeof row != "number"){
9736             row = row.rowIndex;
9737         }
9738         if(row < 0 && row >= this.ds.getCount()){
9739             return  null;
9740         }
9741         col = (col !== undefined ? col : 0);
9742         var cm = this.cm;
9743         while(cm.isHidden(col)){
9744             col++;
9745         }
9746
9747         var el = this.getCellDom(row, col);
9748         if(!el){
9749             return null;
9750         }
9751         var c = this.bodyEl.dom;
9752
9753         var ctop = parseInt(el.offsetTop, 10);
9754         var cleft = parseInt(el.offsetLeft, 10);
9755         var cbot = ctop + el.offsetHeight;
9756         var cright = cleft + el.offsetWidth;
9757
9758         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9759         var ch = 0; //?? header is not withing the area?
9760         var stop = parseInt(c.scrollTop, 10);
9761         var sleft = parseInt(c.scrollLeft, 10);
9762         var sbot = stop + ch;
9763         var sright = sleft + c.clientWidth;
9764         /*
9765         Roo.log('GridView.ensureVisible:' +
9766                 ' ctop:' + ctop +
9767                 ' c.clientHeight:' + c.clientHeight +
9768                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9769                 ' stop:' + stop +
9770                 ' cbot:' + cbot +
9771                 ' sbot:' + sbot +
9772                 ' ch:' + ch  
9773                 );
9774         */
9775         if(ctop < stop){
9776             c.scrollTop = ctop;
9777             //Roo.log("set scrolltop to ctop DISABLE?");
9778         }else if(cbot > sbot){
9779             //Roo.log("set scrolltop to cbot-ch");
9780             c.scrollTop = cbot-ch;
9781         }
9782
9783         if(hscroll !== false){
9784             if(cleft < sleft){
9785                 c.scrollLeft = cleft;
9786             }else if(cright > sright){
9787                 c.scrollLeft = cright-c.clientWidth;
9788             }
9789         }
9790
9791         return el;
9792     },
9793     
9794     
9795     insertRow : function(dm, rowIndex, isUpdate){
9796         
9797         if(!isUpdate){
9798             this.fireEvent("beforerowsinserted", this, rowIndex);
9799         }
9800             //var s = this.getScrollState();
9801         var row = this.renderRow(this.cm, this.store, rowIndex);
9802         // insert before rowIndex..
9803         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9804         
9805         var _this = this;
9806                 
9807         if(row.cellObjects.length){
9808             Roo.each(row.cellObjects, function(r){
9809                 _this.renderCellObject(r);
9810             })
9811         }
9812             
9813         if(!isUpdate){
9814             this.fireEvent("rowsinserted", this, rowIndex);
9815             //this.syncRowHeights(firstRow, lastRow);
9816             //this.stripeRows(firstRow);
9817             //this.layout();
9818         }
9819         
9820     },
9821     
9822     
9823     getRowDom : function(rowIndex)
9824     {
9825         var rows = this.el.select('tbody > tr', true).elements;
9826         
9827         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9828         
9829     },
9830     getCellDom : function(rowIndex, colIndex)
9831     {
9832         var row = this.getRowDom(rowIndex);
9833         if (row === false) {
9834             return false;
9835         }
9836         var cols = row.select('td', true).elements;
9837         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9838         
9839     },
9840     
9841     // returns the object tree for a tr..
9842   
9843     
9844     renderRow : function(cm, ds, rowIndex) 
9845     {
9846         var d = ds.getAt(rowIndex);
9847         
9848         var row = {
9849             tag : 'tr',
9850             cls : 'x-row-' + rowIndex,
9851             cn : []
9852         };
9853             
9854         var cellObjects = [];
9855         
9856         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9857             var config = cm.config[i];
9858             
9859             var renderer = cm.getRenderer(i);
9860             var value = '';
9861             var id = false;
9862             
9863             if(typeof(renderer) !== 'undefined'){
9864                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9865             }
9866             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9867             // and are rendered into the cells after the row is rendered - using the id for the element.
9868             
9869             if(typeof(value) === 'object'){
9870                 id = Roo.id();
9871                 cellObjects.push({
9872                     container : id,
9873                     cfg : value 
9874                 })
9875             }
9876             
9877             var rowcfg = {
9878                 record: d,
9879                 rowIndex : rowIndex,
9880                 colIndex : i,
9881                 rowClass : ''
9882             };
9883
9884             this.fireEvent('rowclass', this, rowcfg);
9885             
9886             var td = {
9887                 tag: 'td',
9888                 // this might end up displaying HTML?
9889                 // this is too messy... - better to only do it on columsn you know are going to be too long
9890                 //tooltip : (typeof(value) === 'object') ? '' : value,
9891                 cls : rowcfg.rowClass + ' x-col-' + i,
9892                 style: '',
9893                 html: (typeof(value) === 'object') ? '' : value
9894             };
9895             
9896             if (id) {
9897                 td.id = id;
9898             }
9899             
9900             if(typeof(config.colspan) != 'undefined'){
9901                 td.colspan = config.colspan;
9902             }
9903             
9904             
9905             
9906             if(typeof(config.align) != 'undefined' && config.align.length){
9907                 td.style += ' text-align:' + config.align + ';';
9908             }
9909             if(typeof(config.valign) != 'undefined' && config.valign.length){
9910                 td.style += ' vertical-align:' + config.valign + ';';
9911             }
9912             /*
9913             if(typeof(config.width) != 'undefined'){
9914                 td.style += ' width:' +  config.width + 'px;';
9915             }
9916             */
9917             
9918             if(typeof(config.cursor) != 'undefined'){
9919                 td.style += ' cursor:' +  config.cursor + ';';
9920             }
9921             
9922             if(typeof(config.cls) != 'undefined'){
9923                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9924             }
9925             if (this.responsive) {
9926                 ['xs','sm','md','lg'].map(function(size){
9927                     
9928                     if(typeof(config[size]) == 'undefined'){
9929                         return;
9930                     }
9931                     
9932                     
9933                       
9934                     if (!config[size]) { // 0 = hidden
9935                         // BS 4 '0' is treated as hide that column and below.
9936                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9937                         return;
9938                     }
9939                     
9940                     td.cls += ' col-' + size + '-' + config[size] + (
9941                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9942                     );
9943                      
9944     
9945                 });
9946             }
9947             row.cn.push(td);
9948            
9949         }
9950         
9951         row.cellObjects = cellObjects;
9952         
9953         return row;
9954           
9955     },
9956     
9957     
9958     
9959     onBeforeLoad : function()
9960     {
9961         
9962     },
9963      /**
9964      * Remove all rows
9965      */
9966     clear : function()
9967     {
9968         this.el.select('tbody', true).first().dom.innerHTML = '';
9969     },
9970     /**
9971      * Show or hide a row.
9972      * @param {Number} rowIndex to show or hide
9973      * @param {Boolean} state hide
9974      */
9975     setRowVisibility : function(rowIndex, state)
9976     {
9977         var bt = this.bodyEl.dom;
9978         
9979         var rows = this.el.select('tbody > tr', true).elements;
9980         
9981         if(typeof(rows[rowIndex]) == 'undefined'){
9982             return;
9983         }
9984         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9985         
9986     },
9987     
9988     
9989     getSelectionModel : function(){
9990         if(!this.selModel){
9991             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9992         }
9993         return this.selModel;
9994     },
9995     /*
9996      * Render the Roo.bootstrap object from renderder
9997      */
9998     renderCellObject : function(r)
9999     {
10000         var _this = this;
10001         
10002         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10003         
10004         var t = r.cfg.render(r.container);
10005         
10006         if(r.cfg.cn){
10007             Roo.each(r.cfg.cn, function(c){
10008                 var child = {
10009                     container: t.getChildContainer(),
10010                     cfg: c
10011                 };
10012                 _this.renderCellObject(child);
10013             })
10014         }
10015     },
10016     /**
10017      * get the Row Index from a dom element.
10018      * @param {Roo.Element} row The row to look for
10019      * @returns {Number} the row
10020      */
10021     getRowIndex : function(row)
10022     {
10023         var rowIndex = -1;
10024         
10025         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10026             if(el != row){
10027                 return;
10028             }
10029             
10030             rowIndex = index;
10031         });
10032         
10033         return rowIndex;
10034     },
10035     /**
10036      * get the header TH element for columnIndex
10037      * @param {Number} columnIndex
10038      * @returns {Roo.Element}
10039      */
10040     getHeaderIndex: function(colIndex)
10041     {
10042         var cols = this.headEl.select('th', true).elements;
10043         return cols[colIndex]; 
10044     },
10045     /**
10046      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10047      * @param {domElement} cell to look for
10048      * @returns {Number} the column
10049      */
10050     getCellIndex : function(cell)
10051     {
10052         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10053         if(id){
10054             return parseInt(id[1], 10);
10055         }
10056         return 0;
10057     },
10058      /**
10059      * Returns the grid's underlying element = used by panel.Grid
10060      * @return {Element} The element
10061      */
10062     getGridEl : function(){
10063         return this.el;
10064     },
10065      /**
10066      * Forces a resize - used by panel.Grid
10067      * @return {Element} The element
10068      */
10069     autoSize : function()
10070     {
10071         //var ctr = Roo.get(this.container.dom.parentElement);
10072         var ctr = Roo.get(this.el.dom);
10073         
10074         var thd = this.getGridEl().select('thead',true).first();
10075         var tbd = this.getGridEl().select('tbody', true).first();
10076         var tfd = this.getGridEl().select('tfoot', true).first();
10077         
10078         var cw = ctr.getWidth();
10079         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10080         
10081         if (tbd) {
10082             
10083             tbd.setWidth(ctr.getWidth());
10084             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10085             // this needs fixing for various usage - currently only hydra job advers I think..
10086             //tdb.setHeight(
10087             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10088             //); 
10089             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10090             cw -= barsize;
10091         }
10092         cw = Math.max(cw, this.totalWidth);
10093         this.getGridEl().select('tbody tr',true).setWidth(cw);
10094         this.initCSS();
10095         
10096         // resize 'expandable coloumn?
10097         
10098         return; // we doe not have a view in this design..
10099         
10100     },
10101     onBodyScroll: function()
10102     {
10103         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10104         if(this.headEl){
10105             this.headEl.setStyle({
10106                 'position' : 'relative',
10107                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10108             });
10109         }
10110         
10111         if(this.lazyLoad){
10112             
10113             var scrollHeight = this.bodyEl.dom.scrollHeight;
10114             
10115             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10116             
10117             var height = this.bodyEl.getHeight();
10118             
10119             if(scrollHeight - height == scrollTop) {
10120                 
10121                 var total = this.ds.getTotalCount();
10122                 
10123                 if(this.footer.cursor + this.footer.pageSize < total){
10124                     
10125                     this.footer.ds.load({
10126                         params : {
10127                             start : this.footer.cursor + this.footer.pageSize,
10128                             limit : this.footer.pageSize
10129                         },
10130                         add : true
10131                     });
10132                 }
10133             }
10134             
10135         }
10136     },
10137     onColumnSplitterMoved : function(i, diff)
10138     {
10139         this.userResized = true;
10140         
10141         var cm = this.colModel;
10142         
10143         var w = this.getHeaderIndex(i).getWidth() + diff;
10144         
10145         
10146         cm.setColumnWidth(i, w, true);
10147         this.initCSS();
10148         //var cid = cm.getColumnId(i); << not used in this version?
10149        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10150         
10151         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10152         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10153         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10154 */
10155         //this.updateSplitters();
10156         //this.layout(); << ??
10157         this.fireEvent("columnresize", i, w);
10158     },
10159     onHeaderChange : function()
10160     {
10161         var header = this.renderHeader();
10162         var table = this.el.select('table', true).first();
10163         
10164         this.headEl.remove();
10165         this.headEl = table.createChild(header, this.bodyEl, false);
10166         
10167         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10168             e.on('click', this.sort, this);
10169         }, this);
10170         
10171         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10172             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10173         }
10174         
10175     },
10176     
10177     onHiddenChange : function(colModel, colIndex, hidden)
10178     {
10179         /*
10180         this.cm.setHidden()
10181         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10182         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10183         
10184         this.CSS.updateRule(thSelector, "display", "");
10185         this.CSS.updateRule(tdSelector, "display", "");
10186         
10187         if(hidden){
10188             this.CSS.updateRule(thSelector, "display", "none");
10189             this.CSS.updateRule(tdSelector, "display", "none");
10190         }
10191         */
10192         // onload calls initCSS()
10193         this.onHeaderChange();
10194         this.onLoad();
10195     },
10196     
10197     setColumnWidth: function(col_index, width)
10198     {
10199         // width = "md-2 xs-2..."
10200         if(!this.colModel.config[col_index]) {
10201             return;
10202         }
10203         
10204         var w = width.split(" ");
10205         
10206         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10207         
10208         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10209         
10210         
10211         for(var j = 0; j < w.length; j++) {
10212             
10213             if(!w[j]) {
10214                 continue;
10215             }
10216             
10217             var size_cls = w[j].split("-");
10218             
10219             if(!Number.isInteger(size_cls[1] * 1)) {
10220                 continue;
10221             }
10222             
10223             if(!this.colModel.config[col_index][size_cls[0]]) {
10224                 continue;
10225             }
10226             
10227             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10228                 continue;
10229             }
10230             
10231             h_row[0].classList.replace(
10232                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10233                 "col-"+size_cls[0]+"-"+size_cls[1]
10234             );
10235             
10236             for(var i = 0; i < rows.length; i++) {
10237                 
10238                 var size_cls = w[j].split("-");
10239                 
10240                 if(!Number.isInteger(size_cls[1] * 1)) {
10241                     continue;
10242                 }
10243                 
10244                 if(!this.colModel.config[col_index][size_cls[0]]) {
10245                     continue;
10246                 }
10247                 
10248                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10249                     continue;
10250                 }
10251                 
10252                 rows[i].classList.replace(
10253                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10254                     "col-"+size_cls[0]+"-"+size_cls[1]
10255                 );
10256             }
10257             
10258             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10259         }
10260     }
10261 });
10262
10263 // currently only used to find the split on drag.. 
10264 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10265
10266 /**
10267  * @depricated
10268 */
10269 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10270 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10271 /*
10272  * - LGPL
10273  *
10274  * table cell
10275  * 
10276  */
10277
10278 /**
10279  * @class Roo.bootstrap.TableCell
10280  * @extends Roo.bootstrap.Component
10281  * Bootstrap TableCell class
10282  * @cfg {String} html cell contain text
10283  * @cfg {String} cls cell class
10284  * @cfg {String} tag cell tag (td|th) default td
10285  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10286  * @cfg {String} align Aligns the content in a cell
10287  * @cfg {String} axis Categorizes cells
10288  * @cfg {String} bgcolor Specifies the background color of a cell
10289  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10290  * @cfg {Number} colspan Specifies the number of columns a cell should span
10291  * @cfg {String} headers Specifies one or more header cells a cell is related to
10292  * @cfg {Number} height Sets the height of a cell
10293  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10294  * @cfg {Number} rowspan Sets the number of rows a cell should span
10295  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10296  * @cfg {String} valign Vertical aligns the content in a cell
10297  * @cfg {Number} width Specifies the width of a cell
10298  * 
10299  * @constructor
10300  * Create a new TableCell
10301  * @param {Object} config The config object
10302  */
10303
10304 Roo.bootstrap.TableCell = function(config){
10305     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10306 };
10307
10308 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10309     
10310     html: false,
10311     cls: false,
10312     tag: false,
10313     abbr: false,
10314     align: false,
10315     axis: false,
10316     bgcolor: false,
10317     charoff: false,
10318     colspan: false,
10319     headers: false,
10320     height: false,
10321     nowrap: false,
10322     rowspan: false,
10323     scope: false,
10324     valign: false,
10325     width: false,
10326     
10327     
10328     getAutoCreate : function(){
10329         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10330         
10331         cfg = {
10332             tag: 'td'
10333         };
10334         
10335         if(this.tag){
10336             cfg.tag = this.tag;
10337         }
10338         
10339         if (this.html) {
10340             cfg.html=this.html
10341         }
10342         if (this.cls) {
10343             cfg.cls=this.cls
10344         }
10345         if (this.abbr) {
10346             cfg.abbr=this.abbr
10347         }
10348         if (this.align) {
10349             cfg.align=this.align
10350         }
10351         if (this.axis) {
10352             cfg.axis=this.axis
10353         }
10354         if (this.bgcolor) {
10355             cfg.bgcolor=this.bgcolor
10356         }
10357         if (this.charoff) {
10358             cfg.charoff=this.charoff
10359         }
10360         if (this.colspan) {
10361             cfg.colspan=this.colspan
10362         }
10363         if (this.headers) {
10364             cfg.headers=this.headers
10365         }
10366         if (this.height) {
10367             cfg.height=this.height
10368         }
10369         if (this.nowrap) {
10370             cfg.nowrap=this.nowrap
10371         }
10372         if (this.rowspan) {
10373             cfg.rowspan=this.rowspan
10374         }
10375         if (this.scope) {
10376             cfg.scope=this.scope
10377         }
10378         if (this.valign) {
10379             cfg.valign=this.valign
10380         }
10381         if (this.width) {
10382             cfg.width=this.width
10383         }
10384         
10385         
10386         return cfg;
10387     }
10388    
10389 });
10390
10391  
10392
10393  /*
10394  * - LGPL
10395  *
10396  * table row
10397  * 
10398  */
10399
10400 /**
10401  * @class Roo.bootstrap.TableRow
10402  * @extends Roo.bootstrap.Component
10403  * Bootstrap TableRow class
10404  * @cfg {String} cls row class
10405  * @cfg {String} align Aligns the content in a table row
10406  * @cfg {String} bgcolor Specifies a background color for a table row
10407  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10408  * @cfg {String} valign Vertical aligns the content in a table row
10409  * 
10410  * @constructor
10411  * Create a new TableRow
10412  * @param {Object} config The config object
10413  */
10414
10415 Roo.bootstrap.TableRow = function(config){
10416     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10417 };
10418
10419 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10420     
10421     cls: false,
10422     align: false,
10423     bgcolor: false,
10424     charoff: false,
10425     valign: false,
10426     
10427     getAutoCreate : function(){
10428         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10429         
10430         cfg = {
10431             tag: 'tr'
10432         };
10433             
10434         if(this.cls){
10435             cfg.cls = this.cls;
10436         }
10437         if(this.align){
10438             cfg.align = this.align;
10439         }
10440         if(this.bgcolor){
10441             cfg.bgcolor = this.bgcolor;
10442         }
10443         if(this.charoff){
10444             cfg.charoff = this.charoff;
10445         }
10446         if(this.valign){
10447             cfg.valign = this.valign;
10448         }
10449         
10450         return cfg;
10451     }
10452    
10453 });
10454
10455  
10456
10457  /*
10458  * - LGPL
10459  *
10460  * table body
10461  * 
10462  */
10463
10464 /**
10465  * @class Roo.bootstrap.TableBody
10466  * @extends Roo.bootstrap.Component
10467  * Bootstrap TableBody class
10468  * @cfg {String} cls element class
10469  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10470  * @cfg {String} align Aligns the content inside the element
10471  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10472  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10473  * 
10474  * @constructor
10475  * Create a new TableBody
10476  * @param {Object} config The config object
10477  */
10478
10479 Roo.bootstrap.TableBody = function(config){
10480     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10481 };
10482
10483 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10484     
10485     cls: false,
10486     tag: false,
10487     align: false,
10488     charoff: false,
10489     valign: false,
10490     
10491     getAutoCreate : function(){
10492         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10493         
10494         cfg = {
10495             tag: 'tbody'
10496         };
10497             
10498         if (this.cls) {
10499             cfg.cls=this.cls
10500         }
10501         if(this.tag){
10502             cfg.tag = this.tag;
10503         }
10504         
10505         if(this.align){
10506             cfg.align = this.align;
10507         }
10508         if(this.charoff){
10509             cfg.charoff = this.charoff;
10510         }
10511         if(this.valign){
10512             cfg.valign = this.valign;
10513         }
10514         
10515         return cfg;
10516     }
10517     
10518     
10519 //    initEvents : function()
10520 //    {
10521 //        
10522 //        if(!this.store){
10523 //            return;
10524 //        }
10525 //        
10526 //        this.store = Roo.factory(this.store, Roo.data);
10527 //        this.store.on('load', this.onLoad, this);
10528 //        
10529 //        this.store.load();
10530 //        
10531 //    },
10532 //    
10533 //    onLoad: function () 
10534 //    {   
10535 //        this.fireEvent('load', this);
10536 //    }
10537 //    
10538 //   
10539 });
10540
10541  
10542
10543  /*
10544  * Based on:
10545  * Ext JS Library 1.1.1
10546  * Copyright(c) 2006-2007, Ext JS, LLC.
10547  *
10548  * Originally Released Under LGPL - original licence link has changed is not relivant.
10549  *
10550  * Fork - LGPL
10551  * <script type="text/javascript">
10552  */
10553
10554 // as we use this in bootstrap.
10555 Roo.namespace('Roo.form');
10556  /**
10557  * @class Roo.form.Action
10558  * Internal Class used to handle form actions
10559  * @constructor
10560  * @param {Roo.form.BasicForm} el The form element or its id
10561  * @param {Object} config Configuration options
10562  */
10563
10564  
10565  
10566 // define the action interface
10567 Roo.form.Action = function(form, options){
10568     this.form = form;
10569     this.options = options || {};
10570 };
10571 /**
10572  * Client Validation Failed
10573  * @const 
10574  */
10575 Roo.form.Action.CLIENT_INVALID = 'client';
10576 /**
10577  * Server Validation Failed
10578  * @const 
10579  */
10580 Roo.form.Action.SERVER_INVALID = 'server';
10581  /**
10582  * Connect to Server Failed
10583  * @const 
10584  */
10585 Roo.form.Action.CONNECT_FAILURE = 'connect';
10586 /**
10587  * Reading Data from Server Failed
10588  * @const 
10589  */
10590 Roo.form.Action.LOAD_FAILURE = 'load';
10591
10592 Roo.form.Action.prototype = {
10593     type : 'default',
10594     failureType : undefined,
10595     response : undefined,
10596     result : undefined,
10597
10598     // interface method
10599     run : function(options){
10600
10601     },
10602
10603     // interface method
10604     success : function(response){
10605
10606     },
10607
10608     // interface method
10609     handleResponse : function(response){
10610
10611     },
10612
10613     // default connection failure
10614     failure : function(response){
10615         
10616         this.response = response;
10617         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10618         this.form.afterAction(this, false);
10619     },
10620
10621     processResponse : function(response){
10622         this.response = response;
10623         if(!response.responseText){
10624             return true;
10625         }
10626         this.result = this.handleResponse(response);
10627         return this.result;
10628     },
10629
10630     // utility functions used internally
10631     getUrl : function(appendParams){
10632         var url = this.options.url || this.form.url || this.form.el.dom.action;
10633         if(appendParams){
10634             var p = this.getParams();
10635             if(p){
10636                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10637             }
10638         }
10639         return url;
10640     },
10641
10642     getMethod : function(){
10643         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10644     },
10645
10646     getParams : function(){
10647         var bp = this.form.baseParams;
10648         var p = this.options.params;
10649         if(p){
10650             if(typeof p == "object"){
10651                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10652             }else if(typeof p == 'string' && bp){
10653                 p += '&' + Roo.urlEncode(bp);
10654             }
10655         }else if(bp){
10656             p = Roo.urlEncode(bp);
10657         }
10658         return p;
10659     },
10660
10661     createCallback : function(){
10662         return {
10663             success: this.success,
10664             failure: this.failure,
10665             scope: this,
10666             timeout: (this.form.timeout*1000),
10667             upload: this.form.fileUpload ? this.success : undefined
10668         };
10669     }
10670 };
10671
10672 Roo.form.Action.Submit = function(form, options){
10673     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10674 };
10675
10676 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10677     type : 'submit',
10678
10679     haveProgress : false,
10680     uploadComplete : false,
10681     
10682     // uploadProgress indicator.
10683     uploadProgress : function()
10684     {
10685         if (!this.form.progressUrl) {
10686             return;
10687         }
10688         
10689         if (!this.haveProgress) {
10690             Roo.MessageBox.progress("Uploading", "Uploading");
10691         }
10692         if (this.uploadComplete) {
10693            Roo.MessageBox.hide();
10694            return;
10695         }
10696         
10697         this.haveProgress = true;
10698    
10699         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10700         
10701         var c = new Roo.data.Connection();
10702         c.request({
10703             url : this.form.progressUrl,
10704             params: {
10705                 id : uid
10706             },
10707             method: 'GET',
10708             success : function(req){
10709                //console.log(data);
10710                 var rdata = false;
10711                 var edata;
10712                 try  {
10713                    rdata = Roo.decode(req.responseText)
10714                 } catch (e) {
10715                     Roo.log("Invalid data from server..");
10716                     Roo.log(edata);
10717                     return;
10718                 }
10719                 if (!rdata || !rdata.success) {
10720                     Roo.log(rdata);
10721                     Roo.MessageBox.alert(Roo.encode(rdata));
10722                     return;
10723                 }
10724                 var data = rdata.data;
10725                 
10726                 if (this.uploadComplete) {
10727                    Roo.MessageBox.hide();
10728                    return;
10729                 }
10730                    
10731                 if (data){
10732                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10733                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10734                     );
10735                 }
10736                 this.uploadProgress.defer(2000,this);
10737             },
10738        
10739             failure: function(data) {
10740                 Roo.log('progress url failed ');
10741                 Roo.log(data);
10742             },
10743             scope : this
10744         });
10745            
10746     },
10747     
10748     
10749     run : function()
10750     {
10751         // run get Values on the form, so it syncs any secondary forms.
10752         this.form.getValues();
10753         
10754         var o = this.options;
10755         var method = this.getMethod();
10756         var isPost = method == 'POST';
10757         if(o.clientValidation === false || this.form.isValid()){
10758             
10759             if (this.form.progressUrl) {
10760                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10761                     (new Date() * 1) + '' + Math.random());
10762                     
10763             } 
10764             
10765             
10766             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10767                 form:this.form.el.dom,
10768                 url:this.getUrl(!isPost),
10769                 method: method,
10770                 params:isPost ? this.getParams() : null,
10771                 isUpload: this.form.fileUpload,
10772                 formData : this.form.formData
10773             }));
10774             
10775             this.uploadProgress();
10776
10777         }else if (o.clientValidation !== false){ // client validation failed
10778             this.failureType = Roo.form.Action.CLIENT_INVALID;
10779             this.form.afterAction(this, false);
10780         }
10781     },
10782
10783     success : function(response)
10784     {
10785         this.uploadComplete= true;
10786         if (this.haveProgress) {
10787             Roo.MessageBox.hide();
10788         }
10789         
10790         
10791         var result = this.processResponse(response);
10792         if(result === true || result.success){
10793             this.form.afterAction(this, true);
10794             return;
10795         }
10796         if(result.errors){
10797             this.form.markInvalid(result.errors);
10798             this.failureType = Roo.form.Action.SERVER_INVALID;
10799         }
10800         this.form.afterAction(this, false);
10801     },
10802     failure : function(response)
10803     {
10804         this.uploadComplete= true;
10805         if (this.haveProgress) {
10806             Roo.MessageBox.hide();
10807         }
10808         
10809         this.response = response;
10810         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10811         this.form.afterAction(this, false);
10812     },
10813     
10814     handleResponse : function(response){
10815         if(this.form.errorReader){
10816             var rs = this.form.errorReader.read(response);
10817             var errors = [];
10818             if(rs.records){
10819                 for(var i = 0, len = rs.records.length; i < len; i++) {
10820                     var r = rs.records[i];
10821                     errors[i] = r.data;
10822                 }
10823             }
10824             if(errors.length < 1){
10825                 errors = null;
10826             }
10827             return {
10828                 success : rs.success,
10829                 errors : errors
10830             };
10831         }
10832         var ret = false;
10833         try {
10834             ret = Roo.decode(response.responseText);
10835         } catch (e) {
10836             ret = {
10837                 success: false,
10838                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10839                 errors : []
10840             };
10841         }
10842         return ret;
10843         
10844     }
10845 });
10846
10847
10848 Roo.form.Action.Load = function(form, options){
10849     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10850     this.reader = this.form.reader;
10851 };
10852
10853 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10854     type : 'load',
10855
10856     run : function(){
10857         
10858         Roo.Ajax.request(Roo.apply(
10859                 this.createCallback(), {
10860                     method:this.getMethod(),
10861                     url:this.getUrl(false),
10862                     params:this.getParams()
10863         }));
10864     },
10865
10866     success : function(response){
10867         
10868         var result = this.processResponse(response);
10869         if(result === true || !result.success || !result.data){
10870             this.failureType = Roo.form.Action.LOAD_FAILURE;
10871             this.form.afterAction(this, false);
10872             return;
10873         }
10874         this.form.clearInvalid();
10875         this.form.setValues(result.data);
10876         this.form.afterAction(this, true);
10877     },
10878
10879     handleResponse : function(response){
10880         if(this.form.reader){
10881             var rs = this.form.reader.read(response);
10882             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10883             return {
10884                 success : rs.success,
10885                 data : data
10886             };
10887         }
10888         return Roo.decode(response.responseText);
10889     }
10890 });
10891
10892 Roo.form.Action.ACTION_TYPES = {
10893     'load' : Roo.form.Action.Load,
10894     'submit' : Roo.form.Action.Submit
10895 };/*
10896  * - LGPL
10897  *
10898  * form
10899  *
10900  */
10901
10902 /**
10903  * @class Roo.bootstrap.Form
10904  * @extends Roo.bootstrap.Component
10905  * Bootstrap Form class
10906  * @cfg {String} method  GET | POST (default POST)
10907  * @cfg {String} labelAlign top | left (default top)
10908  * @cfg {String} align left  | right - for navbars
10909  * @cfg {Boolean} loadMask load mask when submit (default true)
10910
10911  *
10912  * @constructor
10913  * Create a new Form
10914  * @param {Object} config The config object
10915  */
10916
10917
10918 Roo.bootstrap.Form = function(config){
10919     
10920     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10921     
10922     Roo.bootstrap.Form.popover.apply();
10923     
10924     this.addEvents({
10925         /**
10926          * @event clientvalidation
10927          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10928          * @param {Form} this
10929          * @param {Boolean} valid true if the form has passed client-side validation
10930          */
10931         clientvalidation: true,
10932         /**
10933          * @event beforeaction
10934          * Fires before any action is performed. Return false to cancel the action.
10935          * @param {Form} this
10936          * @param {Action} action The action to be performed
10937          */
10938         beforeaction: true,
10939         /**
10940          * @event actionfailed
10941          * Fires when an action fails.
10942          * @param {Form} this
10943          * @param {Action} action The action that failed
10944          */
10945         actionfailed : true,
10946         /**
10947          * @event actioncomplete
10948          * Fires when an action is completed.
10949          * @param {Form} this
10950          * @param {Action} action The action that completed
10951          */
10952         actioncomplete : true
10953     });
10954 };
10955
10956 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10957
10958      /**
10959      * @cfg {String} method
10960      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10961      */
10962     method : 'POST',
10963     /**
10964      * @cfg {String} url
10965      * The URL to use for form actions if one isn't supplied in the action options.
10966      */
10967     /**
10968      * @cfg {Boolean} fileUpload
10969      * Set to true if this form is a file upload.
10970      */
10971
10972     /**
10973      * @cfg {Object} baseParams
10974      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10975      */
10976
10977     /**
10978      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10979      */
10980     timeout: 30,
10981     /**
10982      * @cfg {Sting} align (left|right) for navbar forms
10983      */
10984     align : 'left',
10985
10986     // private
10987     activeAction : null,
10988
10989     /**
10990      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10991      * element by passing it or its id or mask the form itself by passing in true.
10992      * @type Mixed
10993      */
10994     waitMsgTarget : false,
10995
10996     loadMask : true,
10997     
10998     /**
10999      * @cfg {Boolean} errorMask (true|false) default false
11000      */
11001     errorMask : false,
11002     
11003     /**
11004      * @cfg {Number} maskOffset Default 100
11005      */
11006     maskOffset : 100,
11007     
11008     /**
11009      * @cfg {Boolean} maskBody
11010      */
11011     maskBody : false,
11012
11013     getAutoCreate : function(){
11014
11015         var cfg = {
11016             tag: 'form',
11017             method : this.method || 'POST',
11018             id : this.id || Roo.id(),
11019             cls : ''
11020         };
11021         if (this.parent().xtype.match(/^Nav/)) {
11022             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11023
11024         }
11025
11026         if (this.labelAlign == 'left' ) {
11027             cfg.cls += ' form-horizontal';
11028         }
11029
11030
11031         return cfg;
11032     },
11033     initEvents : function()
11034     {
11035         this.el.on('submit', this.onSubmit, this);
11036         // this was added as random key presses on the form where triggering form submit.
11037         this.el.on('keypress', function(e) {
11038             if (e.getCharCode() != 13) {
11039                 return true;
11040             }
11041             // we might need to allow it for textareas.. and some other items.
11042             // check e.getTarget().
11043
11044             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11045                 return true;
11046             }
11047
11048             Roo.log("keypress blocked");
11049
11050             e.preventDefault();
11051             return false;
11052         });
11053         
11054     },
11055     // private
11056     onSubmit : function(e){
11057         e.stopEvent();
11058     },
11059
11060      /**
11061      * Returns true if client-side validation on the form is successful.
11062      * @return Boolean
11063      */
11064     isValid : function(){
11065         var items = this.getItems();
11066         var valid = true;
11067         var target = false;
11068         
11069         items.each(function(f){
11070             
11071             if(f.validate()){
11072                 return;
11073             }
11074             
11075             Roo.log('invalid field: ' + f.name);
11076             
11077             valid = false;
11078
11079             if(!target && f.el.isVisible(true)){
11080                 target = f;
11081             }
11082            
11083         });
11084         
11085         if(this.errorMask && !valid){
11086             Roo.bootstrap.Form.popover.mask(this, target);
11087         }
11088         
11089         return valid;
11090     },
11091     
11092     /**
11093      * Returns true if any fields in this form have changed since their original load.
11094      * @return Boolean
11095      */
11096     isDirty : function(){
11097         var dirty = false;
11098         var items = this.getItems();
11099         items.each(function(f){
11100            if(f.isDirty()){
11101                dirty = true;
11102                return false;
11103            }
11104            return true;
11105         });
11106         return dirty;
11107     },
11108      /**
11109      * Performs a predefined action (submit or load) or custom actions you define on this form.
11110      * @param {String} actionName The name of the action type
11111      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11112      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11113      * accept other config options):
11114      * <pre>
11115 Property          Type             Description
11116 ----------------  ---------------  ----------------------------------------------------------------------------------
11117 url               String           The url for the action (defaults to the form's url)
11118 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11119 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11120 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11121                                    validate the form on the client (defaults to false)
11122      * </pre>
11123      * @return {BasicForm} this
11124      */
11125     doAction : function(action, options){
11126         if(typeof action == 'string'){
11127             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11128         }
11129         if(this.fireEvent('beforeaction', this, action) !== false){
11130             this.beforeAction(action);
11131             action.run.defer(100, action);
11132         }
11133         return this;
11134     },
11135
11136     // private
11137     beforeAction : function(action){
11138         var o = action.options;
11139         
11140         if(this.loadMask){
11141             
11142             if(this.maskBody){
11143                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11144             } else {
11145                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11146             }
11147         }
11148         // not really supported yet.. ??
11149
11150         //if(this.waitMsgTarget === true){
11151         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11152         //}else if(this.waitMsgTarget){
11153         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11154         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11155         //}else {
11156         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11157        // }
11158
11159     },
11160
11161     // private
11162     afterAction : function(action, success){
11163         this.activeAction = null;
11164         var o = action.options;
11165
11166         if(this.loadMask){
11167             
11168             if(this.maskBody){
11169                 Roo.get(document.body).unmask();
11170             } else {
11171                 this.el.unmask();
11172             }
11173         }
11174         
11175         //if(this.waitMsgTarget === true){
11176 //            this.el.unmask();
11177         //}else if(this.waitMsgTarget){
11178         //    this.waitMsgTarget.unmask();
11179         //}else{
11180         //    Roo.MessageBox.updateProgress(1);
11181         //    Roo.MessageBox.hide();
11182        // }
11183         //
11184         if(success){
11185             if(o.reset){
11186                 this.reset();
11187             }
11188             Roo.callback(o.success, o.scope, [this, action]);
11189             this.fireEvent('actioncomplete', this, action);
11190
11191         }else{
11192
11193             // failure condition..
11194             // we have a scenario where updates need confirming.
11195             // eg. if a locking scenario exists..
11196             // we look for { errors : { needs_confirm : true }} in the response.
11197             if (
11198                 (typeof(action.result) != 'undefined')  &&
11199                 (typeof(action.result.errors) != 'undefined')  &&
11200                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11201            ){
11202                 var _t = this;
11203                 Roo.log("not supported yet");
11204                  /*
11205
11206                 Roo.MessageBox.confirm(
11207                     "Change requires confirmation",
11208                     action.result.errorMsg,
11209                     function(r) {
11210                         if (r != 'yes') {
11211                             return;
11212                         }
11213                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11214                     }
11215
11216                 );
11217                 */
11218
11219
11220                 return;
11221             }
11222
11223             Roo.callback(o.failure, o.scope, [this, action]);
11224             // show an error message if no failed handler is set..
11225             if (!this.hasListener('actionfailed')) {
11226                 Roo.log("need to add dialog support");
11227                 /*
11228                 Roo.MessageBox.alert("Error",
11229                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11230                         action.result.errorMsg :
11231                         "Saving Failed, please check your entries or try again"
11232                 );
11233                 */
11234             }
11235
11236             this.fireEvent('actionfailed', this, action);
11237         }
11238
11239     },
11240     /**
11241      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11242      * @param {String} id The value to search for
11243      * @return Field
11244      */
11245     findField : function(id){
11246         var items = this.getItems();
11247         var field = items.get(id);
11248         if(!field){
11249              items.each(function(f){
11250                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11251                     field = f;
11252                     return false;
11253                 }
11254                 return true;
11255             });
11256         }
11257         return field || null;
11258     },
11259      /**
11260      * Mark fields in this form invalid in bulk.
11261      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11262      * @return {BasicForm} this
11263      */
11264     markInvalid : function(errors){
11265         if(errors instanceof Array){
11266             for(var i = 0, len = errors.length; i < len; i++){
11267                 var fieldError = errors[i];
11268                 var f = this.findField(fieldError.id);
11269                 if(f){
11270                     f.markInvalid(fieldError.msg);
11271                 }
11272             }
11273         }else{
11274             var field, id;
11275             for(id in errors){
11276                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11277                     field.markInvalid(errors[id]);
11278                 }
11279             }
11280         }
11281         //Roo.each(this.childForms || [], function (f) {
11282         //    f.markInvalid(errors);
11283         //});
11284
11285         return this;
11286     },
11287
11288     /**
11289      * Set values for fields in this form in bulk.
11290      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11291      * @return {BasicForm} this
11292      */
11293     setValues : function(values){
11294         if(values instanceof Array){ // array of objects
11295             for(var i = 0, len = values.length; i < len; i++){
11296                 var v = values[i];
11297                 var f = this.findField(v.id);
11298                 if(f){
11299                     f.setValue(v.value);
11300                     if(this.trackResetOnLoad){
11301                         f.originalValue = f.getValue();
11302                     }
11303                 }
11304             }
11305         }else{ // object hash
11306             var field, id;
11307             for(id in values){
11308                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11309
11310                     if (field.setFromData &&
11311                         field.valueField &&
11312                         field.displayField &&
11313                         // combos' with local stores can
11314                         // be queried via setValue()
11315                         // to set their value..
11316                         (field.store && !field.store.isLocal)
11317                         ) {
11318                         // it's a combo
11319                         var sd = { };
11320                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11321                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11322                         field.setFromData(sd);
11323
11324                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11325                         
11326                         field.setFromData(values);
11327                         
11328                     } else {
11329                         field.setValue(values[id]);
11330                     }
11331
11332
11333                     if(this.trackResetOnLoad){
11334                         field.originalValue = field.getValue();
11335                     }
11336                 }
11337             }
11338         }
11339
11340         //Roo.each(this.childForms || [], function (f) {
11341         //    f.setValues(values);
11342         //});
11343
11344         return this;
11345     },
11346
11347     /**
11348      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11349      * they are returned as an array.
11350      * @param {Boolean} asString
11351      * @return {Object}
11352      */
11353     getValues : function(asString){
11354         //if (this.childForms) {
11355             // copy values from the child forms
11356         //    Roo.each(this.childForms, function (f) {
11357         //        this.setValues(f.getValues());
11358         //    }, this);
11359         //}
11360
11361
11362
11363         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11364         if(asString === true){
11365             return fs;
11366         }
11367         return Roo.urlDecode(fs);
11368     },
11369
11370     /**
11371      * Returns the fields in this form as an object with key/value pairs.
11372      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11373      * @return {Object}
11374      */
11375     getFieldValues : function(with_hidden)
11376     {
11377         var items = this.getItems();
11378         var ret = {};
11379         items.each(function(f){
11380             
11381             if (!f.getName()) {
11382                 return;
11383             }
11384             
11385             var v = f.getValue();
11386             
11387             if (f.inputType =='radio') {
11388                 if (typeof(ret[f.getName()]) == 'undefined') {
11389                     ret[f.getName()] = ''; // empty..
11390                 }
11391
11392                 if (!f.el.dom.checked) {
11393                     return;
11394
11395                 }
11396                 v = f.el.dom.value;
11397
11398             }
11399             
11400             if(f.xtype == 'MoneyField'){
11401                 ret[f.currencyName] = f.getCurrency();
11402             }
11403
11404             // not sure if this supported any more..
11405             if ((typeof(v) == 'object') && f.getRawValue) {
11406                 v = f.getRawValue() ; // dates..
11407             }
11408             // combo boxes where name != hiddenName...
11409             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11410                 ret[f.name] = f.getRawValue();
11411             }
11412             ret[f.getName()] = v;
11413         });
11414
11415         return ret;
11416     },
11417
11418     /**
11419      * Clears all invalid messages in this form.
11420      * @return {BasicForm} this
11421      */
11422     clearInvalid : function(){
11423         var items = this.getItems();
11424
11425         items.each(function(f){
11426            f.clearInvalid();
11427         });
11428
11429         return this;
11430     },
11431
11432     /**
11433      * Resets this form.
11434      * @return {BasicForm} this
11435      */
11436     reset : function(){
11437         var items = this.getItems();
11438         items.each(function(f){
11439             f.reset();
11440         });
11441
11442         Roo.each(this.childForms || [], function (f) {
11443             f.reset();
11444         });
11445
11446
11447         return this;
11448     },
11449     
11450     getItems : function()
11451     {
11452         var r=new Roo.util.MixedCollection(false, function(o){
11453             return o.id || (o.id = Roo.id());
11454         });
11455         var iter = function(el) {
11456             if (el.inputEl) {
11457                 r.add(el);
11458             }
11459             if (!el.items) {
11460                 return;
11461             }
11462             Roo.each(el.items,function(e) {
11463                 iter(e);
11464             });
11465         };
11466
11467         iter(this);
11468         return r;
11469     },
11470     
11471     hideFields : function(items)
11472     {
11473         Roo.each(items, function(i){
11474             
11475             var f = this.findField(i);
11476             
11477             if(!f){
11478                 return;
11479             }
11480             
11481             f.hide();
11482             
11483         }, this);
11484     },
11485     
11486     showFields : function(items)
11487     {
11488         Roo.each(items, function(i){
11489             
11490             var f = this.findField(i);
11491             
11492             if(!f){
11493                 return;
11494             }
11495             
11496             f.show();
11497             
11498         }, this);
11499     }
11500
11501 });
11502
11503 Roo.apply(Roo.bootstrap.Form, {
11504     
11505     popover : {
11506         
11507         padding : 5,
11508         
11509         isApplied : false,
11510         
11511         isMasked : false,
11512         
11513         form : false,
11514         
11515         target : false,
11516         
11517         toolTip : false,
11518         
11519         intervalID : false,
11520         
11521         maskEl : false,
11522         
11523         apply : function()
11524         {
11525             if(this.isApplied){
11526                 return;
11527             }
11528             
11529             this.maskEl = {
11530                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11531                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11532                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11533                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11534             };
11535             
11536             this.maskEl.top.enableDisplayMode("block");
11537             this.maskEl.left.enableDisplayMode("block");
11538             this.maskEl.bottom.enableDisplayMode("block");
11539             this.maskEl.right.enableDisplayMode("block");
11540             
11541             this.toolTip = new Roo.bootstrap.Tooltip({
11542                 cls : 'roo-form-error-popover',
11543                 alignment : {
11544                     'left' : ['r-l', [-2,0], 'right'],
11545                     'right' : ['l-r', [2,0], 'left'],
11546                     'bottom' : ['tl-bl', [0,2], 'top'],
11547                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11548                 }
11549             });
11550             
11551             this.toolTip.render(Roo.get(document.body));
11552
11553             this.toolTip.el.enableDisplayMode("block");
11554             
11555             Roo.get(document.body).on('click', function(){
11556                 this.unmask();
11557             }, this);
11558             
11559             Roo.get(document.body).on('touchstart', function(){
11560                 this.unmask();
11561             }, this);
11562             
11563             this.isApplied = true
11564         },
11565         
11566         mask : function(form, target)
11567         {
11568             this.form = form;
11569             
11570             this.target = target;
11571             
11572             if(!this.form.errorMask || !target.el){
11573                 return;
11574             }
11575             
11576             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11577             
11578             Roo.log(scrollable);
11579             
11580             var ot = this.target.el.calcOffsetsTo(scrollable);
11581             
11582             var scrollTo = ot[1] - this.form.maskOffset;
11583             
11584             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11585             
11586             scrollable.scrollTo('top', scrollTo);
11587             
11588             var box = this.target.el.getBox();
11589             Roo.log(box);
11590             var zIndex = Roo.bootstrap.Modal.zIndex++;
11591
11592             
11593             this.maskEl.top.setStyle('position', 'absolute');
11594             this.maskEl.top.setStyle('z-index', zIndex);
11595             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11596             this.maskEl.top.setLeft(0);
11597             this.maskEl.top.setTop(0);
11598             this.maskEl.top.show();
11599             
11600             this.maskEl.left.setStyle('position', 'absolute');
11601             this.maskEl.left.setStyle('z-index', zIndex);
11602             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11603             this.maskEl.left.setLeft(0);
11604             this.maskEl.left.setTop(box.y - this.padding);
11605             this.maskEl.left.show();
11606
11607             this.maskEl.bottom.setStyle('position', 'absolute');
11608             this.maskEl.bottom.setStyle('z-index', zIndex);
11609             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11610             this.maskEl.bottom.setLeft(0);
11611             this.maskEl.bottom.setTop(box.bottom + this.padding);
11612             this.maskEl.bottom.show();
11613
11614             this.maskEl.right.setStyle('position', 'absolute');
11615             this.maskEl.right.setStyle('z-index', zIndex);
11616             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11617             this.maskEl.right.setLeft(box.right + this.padding);
11618             this.maskEl.right.setTop(box.y - this.padding);
11619             this.maskEl.right.show();
11620
11621             this.toolTip.bindEl = this.target.el;
11622
11623             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11624
11625             var tip = this.target.blankText;
11626
11627             if(this.target.getValue() !== '' ) {
11628                 
11629                 if (this.target.invalidText.length) {
11630                     tip = this.target.invalidText;
11631                 } else if (this.target.regexText.length){
11632                     tip = this.target.regexText;
11633                 }
11634             }
11635
11636             this.toolTip.show(tip);
11637
11638             this.intervalID = window.setInterval(function() {
11639                 Roo.bootstrap.Form.popover.unmask();
11640             }, 10000);
11641
11642             window.onwheel = function(){ return false;};
11643             
11644             (function(){ this.isMasked = true; }).defer(500, this);
11645             
11646         },
11647         
11648         unmask : function()
11649         {
11650             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11651                 return;
11652             }
11653             
11654             this.maskEl.top.setStyle('position', 'absolute');
11655             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11656             this.maskEl.top.hide();
11657
11658             this.maskEl.left.setStyle('position', 'absolute');
11659             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11660             this.maskEl.left.hide();
11661
11662             this.maskEl.bottom.setStyle('position', 'absolute');
11663             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11664             this.maskEl.bottom.hide();
11665
11666             this.maskEl.right.setStyle('position', 'absolute');
11667             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11668             this.maskEl.right.hide();
11669             
11670             this.toolTip.hide();
11671             
11672             this.toolTip.el.hide();
11673             
11674             window.onwheel = function(){ return true;};
11675             
11676             if(this.intervalID){
11677                 window.clearInterval(this.intervalID);
11678                 this.intervalID = false;
11679             }
11680             
11681             this.isMasked = false;
11682             
11683         }
11684         
11685     }
11686     
11687 });
11688
11689 /*
11690  * Based on:
11691  * Ext JS Library 1.1.1
11692  * Copyright(c) 2006-2007, Ext JS, LLC.
11693  *
11694  * Originally Released Under LGPL - original licence link has changed is not relivant.
11695  *
11696  * Fork - LGPL
11697  * <script type="text/javascript">
11698  */
11699 /**
11700  * @class Roo.form.VTypes
11701  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11702  * @singleton
11703  */
11704 Roo.form.VTypes = function(){
11705     // closure these in so they are only created once.
11706     var alpha = /^[a-zA-Z_]+$/;
11707     var alphanum = /^[a-zA-Z0-9_]+$/;
11708     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11709     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11710
11711     // All these messages and functions are configurable
11712     return {
11713         /**
11714          * The function used to validate email addresses
11715          * @param {String} value The email address
11716          */
11717         'email' : function(v){
11718             return email.test(v);
11719         },
11720         /**
11721          * The error text to display when the email validation function returns false
11722          * @type String
11723          */
11724         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11725         /**
11726          * The keystroke filter mask to be applied on email input
11727          * @type RegExp
11728          */
11729         'emailMask' : /[a-z0-9_\.\-@]/i,
11730
11731         /**
11732          * The function used to validate URLs
11733          * @param {String} value The URL
11734          */
11735         'url' : function(v){
11736             return url.test(v);
11737         },
11738         /**
11739          * The error text to display when the url validation function returns false
11740          * @type String
11741          */
11742         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11743         
11744         /**
11745          * The function used to validate alpha values
11746          * @param {String} value The value
11747          */
11748         'alpha' : function(v){
11749             return alpha.test(v);
11750         },
11751         /**
11752          * The error text to display when the alpha validation function returns false
11753          * @type String
11754          */
11755         'alphaText' : 'This field should only contain letters and _',
11756         /**
11757          * The keystroke filter mask to be applied on alpha input
11758          * @type RegExp
11759          */
11760         'alphaMask' : /[a-z_]/i,
11761
11762         /**
11763          * The function used to validate alphanumeric values
11764          * @param {String} value The value
11765          */
11766         'alphanum' : function(v){
11767             return alphanum.test(v);
11768         },
11769         /**
11770          * The error text to display when the alphanumeric validation function returns false
11771          * @type String
11772          */
11773         'alphanumText' : 'This field should only contain letters, numbers and _',
11774         /**
11775          * The keystroke filter mask to be applied on alphanumeric input
11776          * @type RegExp
11777          */
11778         'alphanumMask' : /[a-z0-9_]/i
11779     };
11780 }();/*
11781  * - LGPL
11782  *
11783  * Input
11784  * 
11785  */
11786
11787 /**
11788  * @class Roo.bootstrap.Input
11789  * @extends Roo.bootstrap.Component
11790  * Bootstrap Input class
11791  * @cfg {Boolean} disabled is it disabled
11792  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11793  * @cfg {String} name name of the input
11794  * @cfg {string} fieldLabel - the label associated
11795  * @cfg {string} placeholder - placeholder to put in text.
11796  * @cfg {string}  before - input group add on before
11797  * @cfg {string} after - input group add on after
11798  * @cfg {string} size - (lg|sm) or leave empty..
11799  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11800  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11801  * @cfg {Number} md colspan out of 12 for computer-sized screens
11802  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11803  * @cfg {string} value default value of the input
11804  * @cfg {Number} labelWidth set the width of label 
11805  * @cfg {Number} labellg set the width of label (1-12)
11806  * @cfg {Number} labelmd set the width of label (1-12)
11807  * @cfg {Number} labelsm set the width of label (1-12)
11808  * @cfg {Number} labelxs set the width of label (1-12)
11809  * @cfg {String} labelAlign (top|left)
11810  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11811  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11812  * @cfg {String} indicatorpos (left|right) default left
11813  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11814  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11815  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11816
11817  * @cfg {String} align (left|center|right) Default left
11818  * @cfg {Boolean} forceFeedback (true|false) Default false
11819  * 
11820  * @constructor
11821  * Create a new Input
11822  * @param {Object} config The config object
11823  */
11824
11825 Roo.bootstrap.Input = function(config){
11826     
11827     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11828     
11829     this.addEvents({
11830         /**
11831          * @event focus
11832          * Fires when this field receives input focus.
11833          * @param {Roo.form.Field} this
11834          */
11835         focus : true,
11836         /**
11837          * @event blur
11838          * Fires when this field loses input focus.
11839          * @param {Roo.form.Field} this
11840          */
11841         blur : true,
11842         /**
11843          * @event specialkey
11844          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11845          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11846          * @param {Roo.form.Field} this
11847          * @param {Roo.EventObject} e The event object
11848          */
11849         specialkey : true,
11850         /**
11851          * @event change
11852          * Fires just before the field blurs if the field value has changed.
11853          * @param {Roo.form.Field} this
11854          * @param {Mixed} newValue The new value
11855          * @param {Mixed} oldValue The original value
11856          */
11857         change : true,
11858         /**
11859          * @event invalid
11860          * Fires after the field has been marked as invalid.
11861          * @param {Roo.form.Field} this
11862          * @param {String} msg The validation message
11863          */
11864         invalid : true,
11865         /**
11866          * @event valid
11867          * Fires after the field has been validated with no errors.
11868          * @param {Roo.form.Field} this
11869          */
11870         valid : true,
11871          /**
11872          * @event keyup
11873          * Fires after the key up
11874          * @param {Roo.form.Field} this
11875          * @param {Roo.EventObject}  e The event Object
11876          */
11877         keyup : true,
11878         /**
11879          * @event paste
11880          * Fires after the user pastes into input
11881          * @param {Roo.form.Field} this
11882          * @param {Roo.EventObject}  e The event Object
11883          */
11884         paste : true
11885     });
11886 };
11887
11888 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11889      /**
11890      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11891       automatic validation (defaults to "keyup").
11892      */
11893     validationEvent : "keyup",
11894      /**
11895      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11896      */
11897     validateOnBlur : true,
11898     /**
11899      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11900      */
11901     validationDelay : 250,
11902      /**
11903      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11904      */
11905     focusClass : "x-form-focus",  // not needed???
11906     
11907        
11908     /**
11909      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11910      */
11911     invalidClass : "has-warning",
11912     
11913     /**
11914      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11915      */
11916     validClass : "has-success",
11917     
11918     /**
11919      * @cfg {Boolean} hasFeedback (true|false) default true
11920      */
11921     hasFeedback : true,
11922     
11923     /**
11924      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11925      */
11926     invalidFeedbackClass : "glyphicon-warning-sign",
11927     
11928     /**
11929      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11930      */
11931     validFeedbackClass : "glyphicon-ok",
11932     
11933     /**
11934      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11935      */
11936     selectOnFocus : false,
11937     
11938      /**
11939      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11940      */
11941     maskRe : null,
11942        /**
11943      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11944      */
11945     vtype : null,
11946     
11947       /**
11948      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11949      */
11950     disableKeyFilter : false,
11951     
11952        /**
11953      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11954      */
11955     disabled : false,
11956      /**
11957      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11958      */
11959     allowBlank : true,
11960     /**
11961      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11962      */
11963     blankText : "Please complete this mandatory field",
11964     
11965      /**
11966      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11967      */
11968     minLength : 0,
11969     /**
11970      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11971      */
11972     maxLength : Number.MAX_VALUE,
11973     /**
11974      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11975      */
11976     minLengthText : "The minimum length for this field is {0}",
11977     /**
11978      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11979      */
11980     maxLengthText : "The maximum length for this field is {0}",
11981   
11982     
11983     /**
11984      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11985      * If available, this function will be called only after the basic validators all return true, and will be passed the
11986      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11987      */
11988     validator : null,
11989     /**
11990      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11991      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11992      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11993      */
11994     regex : null,
11995     /**
11996      * @cfg {String} regexText -- Depricated - use Invalid Text
11997      */
11998     regexText : "",
11999     
12000     /**
12001      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12002      */
12003     invalidText : "",
12004     
12005     
12006     
12007     autocomplete: false,
12008     
12009     
12010     fieldLabel : '',
12011     inputType : 'text',
12012     
12013     name : false,
12014     placeholder: false,
12015     before : false,
12016     after : false,
12017     size : false,
12018     hasFocus : false,
12019     preventMark: false,
12020     isFormField : true,
12021     value : '',
12022     labelWidth : 2,
12023     labelAlign : false,
12024     readOnly : false,
12025     align : false,
12026     formatedValue : false,
12027     forceFeedback : false,
12028     
12029     indicatorpos : 'left',
12030     
12031     labellg : 0,
12032     labelmd : 0,
12033     labelsm : 0,
12034     labelxs : 0,
12035     
12036     capture : '',
12037     accept : '',
12038     
12039     parentLabelAlign : function()
12040     {
12041         var parent = this;
12042         while (parent.parent()) {
12043             parent = parent.parent();
12044             if (typeof(parent.labelAlign) !='undefined') {
12045                 return parent.labelAlign;
12046             }
12047         }
12048         return 'left';
12049         
12050     },
12051     
12052     getAutoCreate : function()
12053     {
12054         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12055         
12056         var id = Roo.id();
12057         
12058         var cfg = {};
12059         
12060         if(this.inputType != 'hidden'){
12061             cfg.cls = 'form-group' //input-group
12062         }
12063         
12064         var input =  {
12065             tag: 'input',
12066             id : id,
12067             type : this.inputType,
12068             value : this.value,
12069             cls : 'form-control',
12070             placeholder : this.placeholder || '',
12071             autocomplete : this.autocomplete || 'new-password'
12072         };
12073         if (this.inputType == 'file') {
12074             input.style = 'overflow:hidden'; // why not in CSS?
12075         }
12076         
12077         if(this.capture.length){
12078             input.capture = this.capture;
12079         }
12080         
12081         if(this.accept.length){
12082             input.accept = this.accept + "/*";
12083         }
12084         
12085         if(this.align){
12086             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12087         }
12088         
12089         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12090             input.maxLength = this.maxLength;
12091         }
12092         
12093         if (this.disabled) {
12094             input.disabled=true;
12095         }
12096         
12097         if (this.readOnly) {
12098             input.readonly=true;
12099         }
12100         
12101         if (this.name) {
12102             input.name = this.name;
12103         }
12104         
12105         if (this.size) {
12106             input.cls += ' input-' + this.size;
12107         }
12108         
12109         var settings=this;
12110         ['xs','sm','md','lg'].map(function(size){
12111             if (settings[size]) {
12112                 cfg.cls += ' col-' + size + '-' + settings[size];
12113             }
12114         });
12115         
12116         var inputblock = input;
12117         
12118         var feedback = {
12119             tag: 'span',
12120             cls: 'glyphicon form-control-feedback'
12121         };
12122             
12123         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12124             
12125             inputblock = {
12126                 cls : 'has-feedback',
12127                 cn :  [
12128                     input,
12129                     feedback
12130                 ] 
12131             };  
12132         }
12133         
12134         if (this.before || this.after) {
12135             
12136             inputblock = {
12137                 cls : 'input-group',
12138                 cn :  [] 
12139             };
12140             
12141             if (this.before && typeof(this.before) == 'string') {
12142                 
12143                 inputblock.cn.push({
12144                     tag :'span',
12145                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12146                     html : this.before
12147                 });
12148             }
12149             if (this.before && typeof(this.before) == 'object') {
12150                 this.before = Roo.factory(this.before);
12151                 
12152                 inputblock.cn.push({
12153                     tag :'span',
12154                     cls : 'roo-input-before input-group-prepend   input-group-' +
12155                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12156                 });
12157             }
12158             
12159             inputblock.cn.push(input);
12160             
12161             if (this.after && typeof(this.after) == 'string') {
12162                 inputblock.cn.push({
12163                     tag :'span',
12164                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12165                     html : this.after
12166                 });
12167             }
12168             if (this.after && typeof(this.after) == 'object') {
12169                 this.after = Roo.factory(this.after);
12170                 
12171                 inputblock.cn.push({
12172                     tag :'span',
12173                     cls : 'roo-input-after input-group-append  input-group-' +
12174                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12175                 });
12176             }
12177             
12178             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12179                 inputblock.cls += ' has-feedback';
12180                 inputblock.cn.push(feedback);
12181             }
12182         };
12183         var indicator = {
12184             tag : 'i',
12185             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12186             tooltip : 'This field is required'
12187         };
12188         if (this.allowBlank ) {
12189             indicator.style = this.allowBlank ? ' display:none' : '';
12190         }
12191         if (align ==='left' && this.fieldLabel.length) {
12192             
12193             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12194             
12195             cfg.cn = [
12196                 indicator,
12197                 {
12198                     tag: 'label',
12199                     'for' :  id,
12200                     cls : 'control-label col-form-label',
12201                     html : this.fieldLabel
12202
12203                 },
12204                 {
12205                     cls : "", 
12206                     cn: [
12207                         inputblock
12208                     ]
12209                 }
12210             ];
12211             
12212             var labelCfg = cfg.cn[1];
12213             var contentCfg = cfg.cn[2];
12214             
12215             if(this.indicatorpos == 'right'){
12216                 cfg.cn = [
12217                     {
12218                         tag: 'label',
12219                         'for' :  id,
12220                         cls : 'control-label col-form-label',
12221                         cn : [
12222                             {
12223                                 tag : 'span',
12224                                 html : this.fieldLabel
12225                             },
12226                             indicator
12227                         ]
12228                     },
12229                     {
12230                         cls : "",
12231                         cn: [
12232                             inputblock
12233                         ]
12234                     }
12235
12236                 ];
12237                 
12238                 labelCfg = cfg.cn[0];
12239                 contentCfg = cfg.cn[1];
12240             
12241             }
12242             
12243             if(this.labelWidth > 12){
12244                 labelCfg.style = "width: " + this.labelWidth + 'px';
12245             }
12246             
12247             if(this.labelWidth < 13 && this.labelmd == 0){
12248                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12249             }
12250             
12251             if(this.labellg > 0){
12252                 labelCfg.cls += ' col-lg-' + this.labellg;
12253                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12254             }
12255             
12256             if(this.labelmd > 0){
12257                 labelCfg.cls += ' col-md-' + this.labelmd;
12258                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12259             }
12260             
12261             if(this.labelsm > 0){
12262                 labelCfg.cls += ' col-sm-' + this.labelsm;
12263                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12264             }
12265             
12266             if(this.labelxs > 0){
12267                 labelCfg.cls += ' col-xs-' + this.labelxs;
12268                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12269             }
12270             
12271             
12272         } else if ( this.fieldLabel.length) {
12273                 
12274             
12275             
12276             cfg.cn = [
12277                 {
12278                     tag : 'i',
12279                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12280                     tooltip : 'This field is required',
12281                     style : this.allowBlank ? ' display:none' : '' 
12282                 },
12283                 {
12284                     tag: 'label',
12285                    //cls : 'input-group-addon',
12286                     html : this.fieldLabel
12287
12288                 },
12289
12290                inputblock
12291
12292            ];
12293            
12294            if(this.indicatorpos == 'right'){
12295        
12296                 cfg.cn = [
12297                     {
12298                         tag: 'label',
12299                        //cls : 'input-group-addon',
12300                         html : this.fieldLabel
12301
12302                     },
12303                     {
12304                         tag : 'i',
12305                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12306                         tooltip : 'This field is required',
12307                         style : this.allowBlank ? ' display:none' : '' 
12308                     },
12309
12310                    inputblock
12311
12312                ];
12313
12314             }
12315
12316         } else {
12317             
12318             cfg.cn = [
12319
12320                     inputblock
12321
12322             ];
12323                 
12324                 
12325         };
12326         
12327         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12328            cfg.cls += ' navbar-form';
12329         }
12330         
12331         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12332             // on BS4 we do this only if not form 
12333             cfg.cls += ' navbar-form';
12334             cfg.tag = 'li';
12335         }
12336         
12337         return cfg;
12338         
12339     },
12340     /**
12341      * return the real input element.
12342      */
12343     inputEl: function ()
12344     {
12345         return this.el.select('input.form-control',true).first();
12346     },
12347     
12348     tooltipEl : function()
12349     {
12350         return this.inputEl();
12351     },
12352     
12353     indicatorEl : function()
12354     {
12355         if (Roo.bootstrap.version == 4) {
12356             return false; // not enabled in v4 yet.
12357         }
12358         
12359         var indicator = this.el.select('i.roo-required-indicator',true).first();
12360         
12361         if(!indicator){
12362             return false;
12363         }
12364         
12365         return indicator;
12366         
12367     },
12368     
12369     setDisabled : function(v)
12370     {
12371         var i  = this.inputEl().dom;
12372         if (!v) {
12373             i.removeAttribute('disabled');
12374             return;
12375             
12376         }
12377         i.setAttribute('disabled','true');
12378     },
12379     initEvents : function()
12380     {
12381           
12382         this.inputEl().on("keydown" , this.fireKey,  this);
12383         this.inputEl().on("focus", this.onFocus,  this);
12384         this.inputEl().on("blur", this.onBlur,  this);
12385         
12386         this.inputEl().relayEvent('keyup', this);
12387         this.inputEl().relayEvent('paste', this);
12388         
12389         this.indicator = this.indicatorEl();
12390         
12391         if(this.indicator){
12392             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12393         }
12394  
12395         // reference to original value for reset
12396         this.originalValue = this.getValue();
12397         //Roo.form.TextField.superclass.initEvents.call(this);
12398         if(this.validationEvent == 'keyup'){
12399             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12400             this.inputEl().on('keyup', this.filterValidation, this);
12401         }
12402         else if(this.validationEvent !== false){
12403             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12404         }
12405         
12406         if(this.selectOnFocus){
12407             this.on("focus", this.preFocus, this);
12408             
12409         }
12410         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12411             this.inputEl().on("keypress", this.filterKeys, this);
12412         } else {
12413             this.inputEl().relayEvent('keypress', this);
12414         }
12415        /* if(this.grow){
12416             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12417             this.el.on("click", this.autoSize,  this);
12418         }
12419         */
12420         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12421             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12422         }
12423         
12424         if (typeof(this.before) == 'object') {
12425             this.before.render(this.el.select('.roo-input-before',true).first());
12426         }
12427         if (typeof(this.after) == 'object') {
12428             this.after.render(this.el.select('.roo-input-after',true).first());
12429         }
12430         
12431         this.inputEl().on('change', this.onChange, this);
12432         
12433     },
12434     filterValidation : function(e){
12435         if(!e.isNavKeyPress()){
12436             this.validationTask.delay(this.validationDelay);
12437         }
12438     },
12439      /**
12440      * Validates the field value
12441      * @return {Boolean} True if the value is valid, else false
12442      */
12443     validate : function(){
12444         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12445         if(this.disabled || this.validateValue(this.getRawValue())){
12446             this.markValid();
12447             return true;
12448         }
12449         
12450         this.markInvalid();
12451         return false;
12452     },
12453     
12454     
12455     /**
12456      * Validates a value according to the field's validation rules and marks the field as invalid
12457      * if the validation fails
12458      * @param {Mixed} value The value to validate
12459      * @return {Boolean} True if the value is valid, else false
12460      */
12461     validateValue : function(value)
12462     {
12463         if(this.getVisibilityEl().hasClass('hidden')){
12464             return true;
12465         }
12466         
12467         if(value.length < 1)  { // if it's blank
12468             if(this.allowBlank){
12469                 return true;
12470             }
12471             return false;
12472         }
12473         
12474         if(value.length < this.minLength){
12475             return false;
12476         }
12477         if(value.length > this.maxLength){
12478             return false;
12479         }
12480         if(this.vtype){
12481             var vt = Roo.form.VTypes;
12482             if(!vt[this.vtype](value, this)){
12483                 return false;
12484             }
12485         }
12486         if(typeof this.validator == "function"){
12487             var msg = this.validator(value);
12488             if(msg !== true){
12489                 return false;
12490             }
12491             if (typeof(msg) == 'string') {
12492                 this.invalidText = msg;
12493             }
12494         }
12495         
12496         if(this.regex && !this.regex.test(value)){
12497             return false;
12498         }
12499         
12500         return true;
12501     },
12502     
12503      // private
12504     fireKey : function(e){
12505         //Roo.log('field ' + e.getKey());
12506         if(e.isNavKeyPress()){
12507             this.fireEvent("specialkey", this, e);
12508         }
12509     },
12510     focus : function (selectText){
12511         if(this.rendered){
12512             this.inputEl().focus();
12513             if(selectText === true){
12514                 this.inputEl().dom.select();
12515             }
12516         }
12517         return this;
12518     } ,
12519     
12520     onFocus : function(){
12521         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12522            // this.el.addClass(this.focusClass);
12523         }
12524         if(!this.hasFocus){
12525             this.hasFocus = true;
12526             this.startValue = this.getValue();
12527             this.fireEvent("focus", this);
12528         }
12529     },
12530     
12531     beforeBlur : Roo.emptyFn,
12532
12533     
12534     // private
12535     onBlur : function(){
12536         this.beforeBlur();
12537         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12538             //this.el.removeClass(this.focusClass);
12539         }
12540         this.hasFocus = false;
12541         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12542             this.validate();
12543         }
12544         var v = this.getValue();
12545         if(String(v) !== String(this.startValue)){
12546             this.fireEvent('change', this, v, this.startValue);
12547         }
12548         this.fireEvent("blur", this);
12549     },
12550     
12551     onChange : function(e)
12552     {
12553         var v = this.getValue();
12554         if(String(v) !== String(this.startValue)){
12555             this.fireEvent('change', this, v, this.startValue);
12556         }
12557         
12558     },
12559     
12560     /**
12561      * Resets the current field value to the originally loaded value and clears any validation messages
12562      */
12563     reset : function(){
12564         this.setValue(this.originalValue);
12565         this.validate();
12566     },
12567      /**
12568      * Returns the name of the field
12569      * @return {Mixed} name The name field
12570      */
12571     getName: function(){
12572         return this.name;
12573     },
12574      /**
12575      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12576      * @return {Mixed} value The field value
12577      */
12578     getValue : function(){
12579         
12580         var v = this.inputEl().getValue();
12581         
12582         return v;
12583     },
12584     /**
12585      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12586      * @return {Mixed} value The field value
12587      */
12588     getRawValue : function(){
12589         var v = this.inputEl().getValue();
12590         
12591         return v;
12592     },
12593     
12594     /**
12595      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12596      * @param {Mixed} value The value to set
12597      */
12598     setRawValue : function(v){
12599         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12600     },
12601     
12602     selectText : function(start, end){
12603         var v = this.getRawValue();
12604         if(v.length > 0){
12605             start = start === undefined ? 0 : start;
12606             end = end === undefined ? v.length : end;
12607             var d = this.inputEl().dom;
12608             if(d.setSelectionRange){
12609                 d.setSelectionRange(start, end);
12610             }else if(d.createTextRange){
12611                 var range = d.createTextRange();
12612                 range.moveStart("character", start);
12613                 range.moveEnd("character", v.length-end);
12614                 range.select();
12615             }
12616         }
12617     },
12618     
12619     /**
12620      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12621      * @param {Mixed} value The value to set
12622      */
12623     setValue : function(v){
12624         this.value = v;
12625         if(this.rendered){
12626             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12627             this.validate();
12628         }
12629     },
12630     
12631     /*
12632     processValue : function(value){
12633         if(this.stripCharsRe){
12634             var newValue = value.replace(this.stripCharsRe, '');
12635             if(newValue !== value){
12636                 this.setRawValue(newValue);
12637                 return newValue;
12638             }
12639         }
12640         return value;
12641     },
12642   */
12643     preFocus : function(){
12644         
12645         if(this.selectOnFocus){
12646             this.inputEl().dom.select();
12647         }
12648     },
12649     filterKeys : function(e){
12650         var k = e.getKey();
12651         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12652             return;
12653         }
12654         var c = e.getCharCode(), cc = String.fromCharCode(c);
12655         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12656             return;
12657         }
12658         if(!this.maskRe.test(cc)){
12659             e.stopEvent();
12660         }
12661     },
12662      /**
12663      * Clear any invalid styles/messages for this field
12664      */
12665     clearInvalid : function(){
12666         
12667         if(!this.el || this.preventMark){ // not rendered
12668             return;
12669         }
12670         
12671         
12672         this.el.removeClass([this.invalidClass, 'is-invalid']);
12673         
12674         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12675             
12676             var feedback = this.el.select('.form-control-feedback', true).first();
12677             
12678             if(feedback){
12679                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12680             }
12681             
12682         }
12683         
12684         if(this.indicator){
12685             this.indicator.removeClass('visible');
12686             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12687         }
12688         
12689         this.fireEvent('valid', this);
12690     },
12691     
12692      /**
12693      * Mark this field as valid
12694      */
12695     markValid : function()
12696     {
12697         if(!this.el  || this.preventMark){ // not rendered...
12698             return;
12699         }
12700         
12701         this.el.removeClass([this.invalidClass, this.validClass]);
12702         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12703
12704         var feedback = this.el.select('.form-control-feedback', true).first();
12705             
12706         if(feedback){
12707             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12708         }
12709         
12710         if(this.indicator){
12711             this.indicator.removeClass('visible');
12712             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12713         }
12714         
12715         if(this.disabled){
12716             return;
12717         }
12718         
12719            
12720         if(this.allowBlank && !this.getRawValue().length){
12721             return;
12722         }
12723         if (Roo.bootstrap.version == 3) {
12724             this.el.addClass(this.validClass);
12725         } else {
12726             this.inputEl().addClass('is-valid');
12727         }
12728
12729         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12730             
12731             var feedback = this.el.select('.form-control-feedback', true).first();
12732             
12733             if(feedback){
12734                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12735                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12736             }
12737             
12738         }
12739         
12740         this.fireEvent('valid', this);
12741     },
12742     
12743      /**
12744      * Mark this field as invalid
12745      * @param {String} msg The validation message
12746      */
12747     markInvalid : function(msg)
12748     {
12749         if(!this.el  || this.preventMark){ // not rendered
12750             return;
12751         }
12752         
12753         this.el.removeClass([this.invalidClass, this.validClass]);
12754         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12755         
12756         var feedback = this.el.select('.form-control-feedback', true).first();
12757             
12758         if(feedback){
12759             this.el.select('.form-control-feedback', true).first().removeClass(
12760                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12761         }
12762
12763         if(this.disabled){
12764             return;
12765         }
12766         
12767         if(this.allowBlank && !this.getRawValue().length){
12768             return;
12769         }
12770         
12771         if(this.indicator){
12772             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12773             this.indicator.addClass('visible');
12774         }
12775         if (Roo.bootstrap.version == 3) {
12776             this.el.addClass(this.invalidClass);
12777         } else {
12778             this.inputEl().addClass('is-invalid');
12779         }
12780         
12781         
12782         
12783         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12784             
12785             var feedback = this.el.select('.form-control-feedback', true).first();
12786             
12787             if(feedback){
12788                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12789                 
12790                 if(this.getValue().length || this.forceFeedback){
12791                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12792                 }
12793                 
12794             }
12795             
12796         }
12797         
12798         this.fireEvent('invalid', this, msg);
12799     },
12800     // private
12801     SafariOnKeyDown : function(event)
12802     {
12803         // this is a workaround for a password hang bug on chrome/ webkit.
12804         if (this.inputEl().dom.type != 'password') {
12805             return;
12806         }
12807         
12808         var isSelectAll = false;
12809         
12810         if(this.inputEl().dom.selectionEnd > 0){
12811             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12812         }
12813         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12814             event.preventDefault();
12815             this.setValue('');
12816             return;
12817         }
12818         
12819         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12820             
12821             event.preventDefault();
12822             // this is very hacky as keydown always get's upper case.
12823             //
12824             var cc = String.fromCharCode(event.getCharCode());
12825             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12826             
12827         }
12828     },
12829     adjustWidth : function(tag, w){
12830         tag = tag.toLowerCase();
12831         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12832             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12833                 if(tag == 'input'){
12834                     return w + 2;
12835                 }
12836                 if(tag == 'textarea'){
12837                     return w-2;
12838                 }
12839             }else if(Roo.isOpera){
12840                 if(tag == 'input'){
12841                     return w + 2;
12842                 }
12843                 if(tag == 'textarea'){
12844                     return w-2;
12845                 }
12846             }
12847         }
12848         return w;
12849     },
12850     
12851     setFieldLabel : function(v)
12852     {
12853         if(!this.rendered){
12854             return;
12855         }
12856         
12857         if(this.indicatorEl()){
12858             var ar = this.el.select('label > span',true);
12859             
12860             if (ar.elements.length) {
12861                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12862                 this.fieldLabel = v;
12863                 return;
12864             }
12865             
12866             var br = this.el.select('label',true);
12867             
12868             if(br.elements.length) {
12869                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12870                 this.fieldLabel = v;
12871                 return;
12872             }
12873             
12874             Roo.log('Cannot Found any of label > span || label in input');
12875             return;
12876         }
12877         
12878         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12879         this.fieldLabel = v;
12880         
12881         
12882     }
12883 });
12884
12885  
12886 /*
12887  * - LGPL
12888  *
12889  * Input
12890  * 
12891  */
12892
12893 /**
12894  * @class Roo.bootstrap.TextArea
12895  * @extends Roo.bootstrap.Input
12896  * Bootstrap TextArea class
12897  * @cfg {Number} cols Specifies the visible width of a text area
12898  * @cfg {Number} rows Specifies the visible number of lines in a text area
12899  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12900  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12901  * @cfg {string} html text
12902  * 
12903  * @constructor
12904  * Create a new TextArea
12905  * @param {Object} config The config object
12906  */
12907
12908 Roo.bootstrap.TextArea = function(config){
12909     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12910    
12911 };
12912
12913 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12914      
12915     cols : false,
12916     rows : 5,
12917     readOnly : false,
12918     warp : 'soft',
12919     resize : false,
12920     value: false,
12921     html: false,
12922     
12923     getAutoCreate : function(){
12924         
12925         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12926         
12927         var id = Roo.id();
12928         
12929         var cfg = {};
12930         
12931         if(this.inputType != 'hidden'){
12932             cfg.cls = 'form-group' //input-group
12933         }
12934         
12935         var input =  {
12936             tag: 'textarea',
12937             id : id,
12938             warp : this.warp,
12939             rows : this.rows,
12940             value : this.value || '',
12941             html: this.html || '',
12942             cls : 'form-control',
12943             placeholder : this.placeholder || '' 
12944             
12945         };
12946         
12947         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12948             input.maxLength = this.maxLength;
12949         }
12950         
12951         if(this.resize){
12952             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12953         }
12954         
12955         if(this.cols){
12956             input.cols = this.cols;
12957         }
12958         
12959         if (this.readOnly) {
12960             input.readonly = true;
12961         }
12962         
12963         if (this.name) {
12964             input.name = this.name;
12965         }
12966         
12967         if (this.size) {
12968             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12969         }
12970         
12971         var settings=this;
12972         ['xs','sm','md','lg'].map(function(size){
12973             if (settings[size]) {
12974                 cfg.cls += ' col-' + size + '-' + settings[size];
12975             }
12976         });
12977         
12978         var inputblock = input;
12979         
12980         if(this.hasFeedback && !this.allowBlank){
12981             
12982             var feedback = {
12983                 tag: 'span',
12984                 cls: 'glyphicon form-control-feedback'
12985             };
12986
12987             inputblock = {
12988                 cls : 'has-feedback',
12989                 cn :  [
12990                     input,
12991                     feedback
12992                 ] 
12993             };  
12994         }
12995         
12996         
12997         if (this.before || this.after) {
12998             
12999             inputblock = {
13000                 cls : 'input-group',
13001                 cn :  [] 
13002             };
13003             if (this.before) {
13004                 inputblock.cn.push({
13005                     tag :'span',
13006                     cls : 'input-group-addon',
13007                     html : this.before
13008                 });
13009             }
13010             
13011             inputblock.cn.push(input);
13012             
13013             if(this.hasFeedback && !this.allowBlank){
13014                 inputblock.cls += ' has-feedback';
13015                 inputblock.cn.push(feedback);
13016             }
13017             
13018             if (this.after) {
13019                 inputblock.cn.push({
13020                     tag :'span',
13021                     cls : 'input-group-addon',
13022                     html : this.after
13023                 });
13024             }
13025             
13026         }
13027         
13028         if (align ==='left' && this.fieldLabel.length) {
13029             cfg.cn = [
13030                 {
13031                     tag: 'label',
13032                     'for' :  id,
13033                     cls : 'control-label',
13034                     html : this.fieldLabel
13035                 },
13036                 {
13037                     cls : "",
13038                     cn: [
13039                         inputblock
13040                     ]
13041                 }
13042
13043             ];
13044             
13045             if(this.labelWidth > 12){
13046                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13047             }
13048
13049             if(this.labelWidth < 13 && this.labelmd == 0){
13050                 this.labelmd = this.labelWidth;
13051             }
13052
13053             if(this.labellg > 0){
13054                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13055                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13056             }
13057
13058             if(this.labelmd > 0){
13059                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13060                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13061             }
13062
13063             if(this.labelsm > 0){
13064                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13065                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13066             }
13067
13068             if(this.labelxs > 0){
13069                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13070                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13071             }
13072             
13073         } else if ( this.fieldLabel.length) {
13074             cfg.cn = [
13075
13076                {
13077                    tag: 'label',
13078                    //cls : 'input-group-addon',
13079                    html : this.fieldLabel
13080
13081                },
13082
13083                inputblock
13084
13085            ];
13086
13087         } else {
13088
13089             cfg.cn = [
13090
13091                 inputblock
13092
13093             ];
13094                 
13095         }
13096         
13097         if (this.disabled) {
13098             input.disabled=true;
13099         }
13100         
13101         return cfg;
13102         
13103     },
13104     /**
13105      * return the real textarea element.
13106      */
13107     inputEl: function ()
13108     {
13109         return this.el.select('textarea.form-control',true).first();
13110     },
13111     
13112     /**
13113      * Clear any invalid styles/messages for this field
13114      */
13115     clearInvalid : function()
13116     {
13117         
13118         if(!this.el || this.preventMark){ // not rendered
13119             return;
13120         }
13121         
13122         var label = this.el.select('label', true).first();
13123         var icon = this.el.select('i.fa-star', true).first();
13124         
13125         if(label && icon){
13126             icon.remove();
13127         }
13128         this.el.removeClass( this.validClass);
13129         this.inputEl().removeClass('is-invalid');
13130          
13131         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13132             
13133             var feedback = this.el.select('.form-control-feedback', true).first();
13134             
13135             if(feedback){
13136                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13137             }
13138             
13139         }
13140         
13141         this.fireEvent('valid', this);
13142     },
13143     
13144      /**
13145      * Mark this field as valid
13146      */
13147     markValid : function()
13148     {
13149         if(!this.el  || this.preventMark){ // not rendered
13150             return;
13151         }
13152         
13153         this.el.removeClass([this.invalidClass, this.validClass]);
13154         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13155         
13156         var feedback = this.el.select('.form-control-feedback', true).first();
13157             
13158         if(feedback){
13159             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13160         }
13161
13162         if(this.disabled || this.allowBlank){
13163             return;
13164         }
13165         
13166         var label = this.el.select('label', true).first();
13167         var icon = this.el.select('i.fa-star', true).first();
13168         
13169         if(label && icon){
13170             icon.remove();
13171         }
13172         if (Roo.bootstrap.version == 3) {
13173             this.el.addClass(this.validClass);
13174         } else {
13175             this.inputEl().addClass('is-valid');
13176         }
13177         
13178         
13179         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13180             
13181             var feedback = this.el.select('.form-control-feedback', true).first();
13182             
13183             if(feedback){
13184                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13185                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13186             }
13187             
13188         }
13189         
13190         this.fireEvent('valid', this);
13191     },
13192     
13193      /**
13194      * Mark this field as invalid
13195      * @param {String} msg The validation message
13196      */
13197     markInvalid : function(msg)
13198     {
13199         if(!this.el  || this.preventMark){ // not rendered
13200             return;
13201         }
13202         
13203         this.el.removeClass([this.invalidClass, this.validClass]);
13204         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13205         
13206         var feedback = this.el.select('.form-control-feedback', true).first();
13207             
13208         if(feedback){
13209             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13210         }
13211
13212         if(this.disabled || this.allowBlank){
13213             return;
13214         }
13215         
13216         var label = this.el.select('label', true).first();
13217         var icon = this.el.select('i.fa-star', true).first();
13218         
13219         if(!this.getValue().length && label && !icon){
13220             this.el.createChild({
13221                 tag : 'i',
13222                 cls : 'text-danger fa fa-lg fa-star',
13223                 tooltip : 'This field is required',
13224                 style : 'margin-right:5px;'
13225             }, label, true);
13226         }
13227         
13228         if (Roo.bootstrap.version == 3) {
13229             this.el.addClass(this.invalidClass);
13230         } else {
13231             this.inputEl().addClass('is-invalid');
13232         }
13233         
13234         // fixme ... this may be depricated need to test..
13235         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13236             
13237             var feedback = this.el.select('.form-control-feedback', true).first();
13238             
13239             if(feedback){
13240                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13241                 
13242                 if(this.getValue().length || this.forceFeedback){
13243                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13244                 }
13245                 
13246             }
13247             
13248         }
13249         
13250         this.fireEvent('invalid', this, msg);
13251     }
13252 });
13253
13254  
13255 /*
13256  * - LGPL
13257  *
13258  * trigger field - base class for combo..
13259  * 
13260  */
13261  
13262 /**
13263  * @class Roo.bootstrap.TriggerField
13264  * @extends Roo.bootstrap.Input
13265  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13266  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13267  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13268  * for which you can provide a custom implementation.  For example:
13269  * <pre><code>
13270 var trigger = new Roo.bootstrap.TriggerField();
13271 trigger.onTriggerClick = myTriggerFn;
13272 trigger.applyTo('my-field');
13273 </code></pre>
13274  *
13275  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13276  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13277  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13278  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13279  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13280
13281  * @constructor
13282  * Create a new TriggerField.
13283  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13284  * to the base TextField)
13285  */
13286 Roo.bootstrap.TriggerField = function(config){
13287     this.mimicing = false;
13288     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13289 };
13290
13291 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13292     /**
13293      * @cfg {String} triggerClass A CSS class to apply to the trigger
13294      */
13295      /**
13296      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13297      */
13298     hideTrigger:false,
13299
13300     /**
13301      * @cfg {Boolean} removable (true|false) special filter default false
13302      */
13303     removable : false,
13304     
13305     /** @cfg {Boolean} grow @hide */
13306     /** @cfg {Number} growMin @hide */
13307     /** @cfg {Number} growMax @hide */
13308
13309     /**
13310      * @hide 
13311      * @method
13312      */
13313     autoSize: Roo.emptyFn,
13314     // private
13315     monitorTab : true,
13316     // private
13317     deferHeight : true,
13318
13319     
13320     actionMode : 'wrap',
13321     
13322     caret : false,
13323     
13324     
13325     getAutoCreate : function(){
13326        
13327         var align = this.labelAlign || this.parentLabelAlign();
13328         
13329         var id = Roo.id();
13330         
13331         var cfg = {
13332             cls: 'form-group' //input-group
13333         };
13334         
13335         
13336         var input =  {
13337             tag: 'input',
13338             id : id,
13339             type : this.inputType,
13340             cls : 'form-control',
13341             autocomplete: 'new-password',
13342             placeholder : this.placeholder || '' 
13343             
13344         };
13345         if (this.name) {
13346             input.name = this.name;
13347         }
13348         if (this.size) {
13349             input.cls += ' input-' + this.size;
13350         }
13351         
13352         if (this.disabled) {
13353             input.disabled=true;
13354         }
13355         
13356         var inputblock = input;
13357         
13358         if(this.hasFeedback && !this.allowBlank){
13359             
13360             var feedback = {
13361                 tag: 'span',
13362                 cls: 'glyphicon form-control-feedback'
13363             };
13364             
13365             if(this.removable && !this.editable  ){
13366                 inputblock = {
13367                     cls : 'has-feedback',
13368                     cn :  [
13369                         inputblock,
13370                         {
13371                             tag: 'button',
13372                             html : 'x',
13373                             cls : 'roo-combo-removable-btn close'
13374                         },
13375                         feedback
13376                     ] 
13377                 };
13378             } else {
13379                 inputblock = {
13380                     cls : 'has-feedback',
13381                     cn :  [
13382                         inputblock,
13383                         feedback
13384                     ] 
13385                 };
13386             }
13387
13388         } else {
13389             if(this.removable && !this.editable ){
13390                 inputblock = {
13391                     cls : 'roo-removable',
13392                     cn :  [
13393                         inputblock,
13394                         {
13395                             tag: 'button',
13396                             html : 'x',
13397                             cls : 'roo-combo-removable-btn close'
13398                         }
13399                     ] 
13400                 };
13401             }
13402         }
13403         
13404         if (this.before || this.after) {
13405             
13406             inputblock = {
13407                 cls : 'input-group',
13408                 cn :  [] 
13409             };
13410             if (this.before) {
13411                 inputblock.cn.push({
13412                     tag :'span',
13413                     cls : 'input-group-addon input-group-prepend input-group-text',
13414                     html : this.before
13415                 });
13416             }
13417             
13418             inputblock.cn.push(input);
13419             
13420             if(this.hasFeedback && !this.allowBlank){
13421                 inputblock.cls += ' has-feedback';
13422                 inputblock.cn.push(feedback);
13423             }
13424             
13425             if (this.after) {
13426                 inputblock.cn.push({
13427                     tag :'span',
13428                     cls : 'input-group-addon input-group-append input-group-text',
13429                     html : this.after
13430                 });
13431             }
13432             
13433         };
13434         
13435       
13436         
13437         var ibwrap = inputblock;
13438         
13439         if(this.multiple){
13440             ibwrap = {
13441                 tag: 'ul',
13442                 cls: 'roo-select2-choices',
13443                 cn:[
13444                     {
13445                         tag: 'li',
13446                         cls: 'roo-select2-search-field',
13447                         cn: [
13448
13449                             inputblock
13450                         ]
13451                     }
13452                 ]
13453             };
13454                 
13455         }
13456         
13457         var combobox = {
13458             cls: 'roo-select2-container input-group',
13459             cn: [
13460                  {
13461                     tag: 'input',
13462                     type : 'hidden',
13463                     cls: 'form-hidden-field'
13464                 },
13465                 ibwrap
13466             ]
13467         };
13468         
13469         if(!this.multiple && this.showToggleBtn){
13470             
13471             var caret = {
13472                         tag: 'span',
13473                         cls: 'caret'
13474              };
13475             if (this.caret != false) {
13476                 caret = {
13477                      tag: 'i',
13478                      cls: 'fa fa-' + this.caret
13479                 };
13480                 
13481             }
13482             
13483             combobox.cn.push({
13484                 tag :'span',
13485                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13486                 cn : [
13487                     Roo.bootstrap.version == 3 ? caret : '',
13488                     {
13489                         tag: 'span',
13490                         cls: 'combobox-clear',
13491                         cn  : [
13492                             {
13493                                 tag : 'i',
13494                                 cls: 'icon-remove'
13495                             }
13496                         ]
13497                     }
13498                 ]
13499
13500             })
13501         }
13502         
13503         if(this.multiple){
13504             combobox.cls += ' roo-select2-container-multi';
13505         }
13506          var indicator = {
13507             tag : 'i',
13508             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13509             tooltip : 'This field is required'
13510         };
13511         if (Roo.bootstrap.version == 4) {
13512             indicator = {
13513                 tag : 'i',
13514                 style : 'display:none'
13515             };
13516         }
13517         
13518         
13519         if (align ==='left' && this.fieldLabel.length) {
13520             
13521             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13522
13523             cfg.cn = [
13524                 indicator,
13525                 {
13526                     tag: 'label',
13527                     'for' :  id,
13528                     cls : 'control-label',
13529                     html : this.fieldLabel
13530
13531                 },
13532                 {
13533                     cls : "", 
13534                     cn: [
13535                         combobox
13536                     ]
13537                 }
13538
13539             ];
13540             
13541             var labelCfg = cfg.cn[1];
13542             var contentCfg = cfg.cn[2];
13543             
13544             if(this.indicatorpos == 'right'){
13545                 cfg.cn = [
13546                     {
13547                         tag: 'label',
13548                         'for' :  id,
13549                         cls : 'control-label',
13550                         cn : [
13551                             {
13552                                 tag : 'span',
13553                                 html : this.fieldLabel
13554                             },
13555                             indicator
13556                         ]
13557                     },
13558                     {
13559                         cls : "", 
13560                         cn: [
13561                             combobox
13562                         ]
13563                     }
13564
13565                 ];
13566                 
13567                 labelCfg = cfg.cn[0];
13568                 contentCfg = cfg.cn[1];
13569             }
13570             
13571             if(this.labelWidth > 12){
13572                 labelCfg.style = "width: " + this.labelWidth + 'px';
13573             }
13574             
13575             if(this.labelWidth < 13 && this.labelmd == 0){
13576                 this.labelmd = this.labelWidth;
13577             }
13578             
13579             if(this.labellg > 0){
13580                 labelCfg.cls += ' col-lg-' + this.labellg;
13581                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13582             }
13583             
13584             if(this.labelmd > 0){
13585                 labelCfg.cls += ' col-md-' + this.labelmd;
13586                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13587             }
13588             
13589             if(this.labelsm > 0){
13590                 labelCfg.cls += ' col-sm-' + this.labelsm;
13591                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13592             }
13593             
13594             if(this.labelxs > 0){
13595                 labelCfg.cls += ' col-xs-' + this.labelxs;
13596                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13597             }
13598             
13599         } else if ( this.fieldLabel.length) {
13600 //                Roo.log(" label");
13601             cfg.cn = [
13602                 indicator,
13603                {
13604                    tag: 'label',
13605                    //cls : 'input-group-addon',
13606                    html : this.fieldLabel
13607
13608                },
13609
13610                combobox
13611
13612             ];
13613             
13614             if(this.indicatorpos == 'right'){
13615                 
13616                 cfg.cn = [
13617                     {
13618                        tag: 'label',
13619                        cn : [
13620                            {
13621                                tag : 'span',
13622                                html : this.fieldLabel
13623                            },
13624                            indicator
13625                        ]
13626
13627                     },
13628                     combobox
13629
13630                 ];
13631
13632             }
13633
13634         } else {
13635             
13636 //                Roo.log(" no label && no align");
13637                 cfg = combobox
13638                      
13639                 
13640         }
13641         
13642         var settings=this;
13643         ['xs','sm','md','lg'].map(function(size){
13644             if (settings[size]) {
13645                 cfg.cls += ' col-' + size + '-' + settings[size];
13646             }
13647         });
13648         
13649         return cfg;
13650         
13651     },
13652     
13653     
13654     
13655     // private
13656     onResize : function(w, h){
13657 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13658 //        if(typeof w == 'number'){
13659 //            var x = w - this.trigger.getWidth();
13660 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13661 //            this.trigger.setStyle('left', x+'px');
13662 //        }
13663     },
13664
13665     // private
13666     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13667
13668     // private
13669     getResizeEl : function(){
13670         return this.inputEl();
13671     },
13672
13673     // private
13674     getPositionEl : function(){
13675         return this.inputEl();
13676     },
13677
13678     // private
13679     alignErrorIcon : function(){
13680         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13681     },
13682
13683     // private
13684     initEvents : function(){
13685         
13686         this.createList();
13687         
13688         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13689         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13690         if(!this.multiple && this.showToggleBtn){
13691             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13692             if(this.hideTrigger){
13693                 this.trigger.setDisplayed(false);
13694             }
13695             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13696         }
13697         
13698         if(this.multiple){
13699             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13700         }
13701         
13702         if(this.removable && !this.editable && !this.tickable){
13703             var close = this.closeTriggerEl();
13704             
13705             if(close){
13706                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13707                 close.on('click', this.removeBtnClick, this, close);
13708             }
13709         }
13710         
13711         //this.trigger.addClassOnOver('x-form-trigger-over');
13712         //this.trigger.addClassOnClick('x-form-trigger-click');
13713         
13714         //if(!this.width){
13715         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13716         //}
13717     },
13718     
13719     closeTriggerEl : function()
13720     {
13721         var close = this.el.select('.roo-combo-removable-btn', true).first();
13722         return close ? close : false;
13723     },
13724     
13725     removeBtnClick : function(e, h, el)
13726     {
13727         e.preventDefault();
13728         
13729         if(this.fireEvent("remove", this) !== false){
13730             this.reset();
13731             this.fireEvent("afterremove", this)
13732         }
13733     },
13734     
13735     createList : function()
13736     {
13737         this.list = Roo.get(document.body).createChild({
13738             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13739             cls: 'typeahead typeahead-long dropdown-menu shadow',
13740             style: 'display:none'
13741         });
13742         
13743         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13744         
13745     },
13746
13747     // private
13748     initTrigger : function(){
13749        
13750     },
13751
13752     // private
13753     onDestroy : function(){
13754         if(this.trigger){
13755             this.trigger.removeAllListeners();
13756           //  this.trigger.remove();
13757         }
13758         //if(this.wrap){
13759         //    this.wrap.remove();
13760         //}
13761         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13762     },
13763
13764     // private
13765     onFocus : function(){
13766         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13767         /*
13768         if(!this.mimicing){
13769             this.wrap.addClass('x-trigger-wrap-focus');
13770             this.mimicing = true;
13771             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13772             if(this.monitorTab){
13773                 this.el.on("keydown", this.checkTab, this);
13774             }
13775         }
13776         */
13777     },
13778
13779     // private
13780     checkTab : function(e){
13781         if(e.getKey() == e.TAB){
13782             this.triggerBlur();
13783         }
13784     },
13785
13786     // private
13787     onBlur : function(){
13788         // do nothing
13789     },
13790
13791     // private
13792     mimicBlur : function(e, t){
13793         /*
13794         if(!this.wrap.contains(t) && this.validateBlur()){
13795             this.triggerBlur();
13796         }
13797         */
13798     },
13799
13800     // private
13801     triggerBlur : function(){
13802         this.mimicing = false;
13803         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13804         if(this.monitorTab){
13805             this.el.un("keydown", this.checkTab, this);
13806         }
13807         //this.wrap.removeClass('x-trigger-wrap-focus');
13808         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13809     },
13810
13811     // private
13812     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13813     validateBlur : function(e, t){
13814         return true;
13815     },
13816
13817     // private
13818     onDisable : function(){
13819         this.inputEl().dom.disabled = true;
13820         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13821         //if(this.wrap){
13822         //    this.wrap.addClass('x-item-disabled');
13823         //}
13824     },
13825
13826     // private
13827     onEnable : function(){
13828         this.inputEl().dom.disabled = false;
13829         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13830         //if(this.wrap){
13831         //    this.el.removeClass('x-item-disabled');
13832         //}
13833     },
13834
13835     // private
13836     onShow : function(){
13837         var ae = this.getActionEl();
13838         
13839         if(ae){
13840             ae.dom.style.display = '';
13841             ae.dom.style.visibility = 'visible';
13842         }
13843     },
13844
13845     // private
13846     
13847     onHide : function(){
13848         var ae = this.getActionEl();
13849         ae.dom.style.display = 'none';
13850     },
13851
13852     /**
13853      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13854      * by an implementing function.
13855      * @method
13856      * @param {EventObject} e
13857      */
13858     onTriggerClick : Roo.emptyFn
13859 });
13860  
13861 /*
13862 * Licence: LGPL
13863 */
13864
13865 /**
13866  * @class Roo.bootstrap.CardUploader
13867  * @extends Roo.bootstrap.Button
13868  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13869  * @cfg {Number} errorTimeout default 3000
13870  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13871  * @cfg {Array}  html The button text.
13872
13873  *
13874  * @constructor
13875  * Create a new CardUploader
13876  * @param {Object} config The config object
13877  */
13878
13879 Roo.bootstrap.CardUploader = function(config){
13880     
13881  
13882     
13883     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13884     
13885     
13886     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13887         return r.data.id
13888      });
13889     
13890      this.addEvents({
13891          // raw events
13892         /**
13893          * @event preview
13894          * When a image is clicked on - and needs to display a slideshow or similar..
13895          * @param {Roo.bootstrap.Card} this
13896          * @param {Object} The image information data 
13897          *
13898          */
13899         'preview' : true,
13900          /**
13901          * @event download
13902          * When a the download link is clicked
13903          * @param {Roo.bootstrap.Card} this
13904          * @param {Object} The image information data  contains 
13905          */
13906         'download' : true
13907         
13908     });
13909 };
13910  
13911 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13912     
13913      
13914     errorTimeout : 3000,
13915      
13916     images : false,
13917    
13918     fileCollection : false,
13919     allowBlank : true,
13920     
13921     getAutoCreate : function()
13922     {
13923         
13924         var cfg =  {
13925             cls :'form-group' ,
13926             cn : [
13927                
13928                 {
13929                     tag: 'label',
13930                    //cls : 'input-group-addon',
13931                     html : this.fieldLabel
13932
13933                 },
13934
13935                 {
13936                     tag: 'input',
13937                     type : 'hidden',
13938                     name : this.name,
13939                     value : this.value,
13940                     cls : 'd-none  form-control'
13941                 },
13942                 
13943                 {
13944                     tag: 'input',
13945                     multiple : 'multiple',
13946                     type : 'file',
13947                     cls : 'd-none  roo-card-upload-selector'
13948                 },
13949                 
13950                 {
13951                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13952                 },
13953                 {
13954                     cls : 'card-columns roo-card-uploader-container'
13955                 }
13956
13957             ]
13958         };
13959            
13960          
13961         return cfg;
13962     },
13963     
13964     getChildContainer : function() /// what children are added to.
13965     {
13966         return this.containerEl;
13967     },
13968    
13969     getButtonContainer : function() /// what children are added to.
13970     {
13971         return this.el.select(".roo-card-uploader-button-container").first();
13972     },
13973    
13974     initEvents : function()
13975     {
13976         
13977         Roo.bootstrap.Input.prototype.initEvents.call(this);
13978         
13979         var t = this;
13980         this.addxtype({
13981             xns: Roo.bootstrap,
13982
13983             xtype : 'Button',
13984             container_method : 'getButtonContainer' ,            
13985             html :  this.html, // fix changable?
13986             cls : 'w-100 ',
13987             listeners : {
13988                 'click' : function(btn, e) {
13989                     t.onClick(e);
13990                 }
13991             }
13992         });
13993         
13994         
13995         
13996         
13997         this.urlAPI = (window.createObjectURL && window) || 
13998                                 (window.URL && URL.revokeObjectURL && URL) || 
13999                                 (window.webkitURL && webkitURL);
14000                         
14001          
14002          
14003          
14004         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14005         
14006         this.selectorEl.on('change', this.onFileSelected, this);
14007         if (this.images) {
14008             var t = this;
14009             this.images.forEach(function(img) {
14010                 t.addCard(img)
14011             });
14012             this.images = false;
14013         }
14014         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14015          
14016        
14017     },
14018     
14019    
14020     onClick : function(e)
14021     {
14022         e.preventDefault();
14023          
14024         this.selectorEl.dom.click();
14025          
14026     },
14027     
14028     onFileSelected : function(e)
14029     {
14030         e.preventDefault();
14031         
14032         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14033             return;
14034         }
14035         
14036         Roo.each(this.selectorEl.dom.files, function(file){    
14037             this.addFile(file);
14038         }, this);
14039          
14040     },
14041     
14042       
14043     
14044       
14045     
14046     addFile : function(file)
14047     {
14048            
14049         if(typeof(file) === 'string'){
14050             throw "Add file by name?"; // should not happen
14051             return;
14052         }
14053         
14054         if(!file || !this.urlAPI){
14055             return;
14056         }
14057         
14058         // file;
14059         // file.type;
14060         
14061         var _this = this;
14062         
14063         
14064         var url = _this.urlAPI.createObjectURL( file);
14065            
14066         this.addCard({
14067             id : Roo.bootstrap.CardUploader.ID--,
14068             is_uploaded : false,
14069             src : url,
14070             srcfile : file,
14071             title : file.name,
14072             mimetype : file.type,
14073             preview : false,
14074             is_deleted : 0
14075         });
14076         
14077     },
14078     
14079     /**
14080      * addCard - add an Attachment to the uploader
14081      * @param data - the data about the image to upload
14082      *
14083      * {
14084           id : 123
14085           title : "Title of file",
14086           is_uploaded : false,
14087           src : "http://.....",
14088           srcfile : { the File upload object },
14089           mimetype : file.type,
14090           preview : false,
14091           is_deleted : 0
14092           .. any other data...
14093         }
14094      *
14095      * 
14096     */
14097     
14098     addCard : function (data)
14099     {
14100         // hidden input element?
14101         // if the file is not an image...
14102         //then we need to use something other that and header_image
14103         var t = this;
14104         //   remove.....
14105         var footer = [
14106             {
14107                 xns : Roo.bootstrap,
14108                 xtype : 'CardFooter',
14109                  items: [
14110                     {
14111                         xns : Roo.bootstrap,
14112                         xtype : 'Element',
14113                         cls : 'd-flex',
14114                         items : [
14115                             
14116                             {
14117                                 xns : Roo.bootstrap,
14118                                 xtype : 'Button',
14119                                 html : String.format("<small>{0}</small>", data.title),
14120                                 cls : 'col-10 text-left',
14121                                 size: 'sm',
14122                                 weight: 'link',
14123                                 fa : 'download',
14124                                 listeners : {
14125                                     click : function() {
14126                                      
14127                                         t.fireEvent( "download", t, data );
14128                                     }
14129                                 }
14130                             },
14131                           
14132                             {
14133                                 xns : Roo.bootstrap,
14134                                 xtype : 'Button',
14135                                 style: 'max-height: 28px; ',
14136                                 size : 'sm',
14137                                 weight: 'danger',
14138                                 cls : 'col-2',
14139                                 fa : 'times',
14140                                 listeners : {
14141                                     click : function() {
14142                                         t.removeCard(data.id)
14143                                     }
14144                                 }
14145                             }
14146                         ]
14147                     }
14148                     
14149                 ] 
14150             }
14151             
14152         ];
14153         
14154         var cn = this.addxtype(
14155             {
14156                  
14157                 xns : Roo.bootstrap,
14158                 xtype : 'Card',
14159                 closeable : true,
14160                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14161                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14162                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14163                 data : data,
14164                 html : false,
14165                  
14166                 items : footer,
14167                 initEvents : function() {
14168                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14169                     var card = this;
14170                     this.imgEl = this.el.select('.card-img-top').first();
14171                     if (this.imgEl) {
14172                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14173                         this.imgEl.set({ 'pointer' : 'cursor' });
14174                                   
14175                     }
14176                     this.getCardFooter().addClass('p-1');
14177                     
14178                   
14179                 }
14180                 
14181             }
14182         );
14183         // dont' really need ot update items.
14184         // this.items.push(cn);
14185         this.fileCollection.add(cn);
14186         
14187         if (!data.srcfile) {
14188             this.updateInput();
14189             return;
14190         }
14191             
14192         var _t = this;
14193         var reader = new FileReader();
14194         reader.addEventListener("load", function() {  
14195             data.srcdata =  reader.result;
14196             _t.updateInput();
14197         });
14198         reader.readAsDataURL(data.srcfile);
14199         
14200         
14201         
14202     },
14203     removeCard : function(id)
14204     {
14205         
14206         var card  = this.fileCollection.get(id);
14207         card.data.is_deleted = 1;
14208         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14209         //this.fileCollection.remove(card);
14210         //this.items = this.items.filter(function(e) { return e != card });
14211         // dont' really need ot update items.
14212         card.el.dom.parentNode.removeChild(card.el.dom);
14213         this.updateInput();
14214
14215         
14216     },
14217     reset: function()
14218     {
14219         this.fileCollection.each(function(card) {
14220             if (card.el.dom && card.el.dom.parentNode) {
14221                 card.el.dom.parentNode.removeChild(card.el.dom);
14222             }
14223         });
14224         this.fileCollection.clear();
14225         this.updateInput();
14226     },
14227     
14228     updateInput : function()
14229     {
14230          var data = [];
14231         this.fileCollection.each(function(e) {
14232             data.push(e.data);
14233             
14234         });
14235         this.inputEl().dom.value = JSON.stringify(data);
14236         
14237         
14238         
14239     }
14240     
14241     
14242 });
14243
14244
14245 Roo.bootstrap.CardUploader.ID = -1;/*
14246  * Based on:
14247  * Ext JS Library 1.1.1
14248  * Copyright(c) 2006-2007, Ext JS, LLC.
14249  *
14250  * Originally Released Under LGPL - original licence link has changed is not relivant.
14251  *
14252  * Fork - LGPL
14253  * <script type="text/javascript">
14254  */
14255
14256
14257 /**
14258  * @class Roo.data.SortTypes
14259  * @singleton
14260  * Defines the default sorting (casting?) comparison functions used when sorting data.
14261  */
14262 Roo.data.SortTypes = {
14263     /**
14264      * Default sort that does nothing
14265      * @param {Mixed} s The value being converted
14266      * @return {Mixed} The comparison value
14267      */
14268     none : function(s){
14269         return s;
14270     },
14271     
14272     /**
14273      * The regular expression used to strip tags
14274      * @type {RegExp}
14275      * @property
14276      */
14277     stripTagsRE : /<\/?[^>]+>/gi,
14278     
14279     /**
14280      * Strips all HTML tags to sort on text only
14281      * @param {Mixed} s The value being converted
14282      * @return {String} The comparison value
14283      */
14284     asText : function(s){
14285         return String(s).replace(this.stripTagsRE, "");
14286     },
14287     
14288     /**
14289      * Strips all HTML tags to sort on text only - Case insensitive
14290      * @param {Mixed} s The value being converted
14291      * @return {String} The comparison value
14292      */
14293     asUCText : function(s){
14294         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14295     },
14296     
14297     /**
14298      * Case insensitive string
14299      * @param {Mixed} s The value being converted
14300      * @return {String} The comparison value
14301      */
14302     asUCString : function(s) {
14303         return String(s).toUpperCase();
14304     },
14305     
14306     /**
14307      * Date sorting
14308      * @param {Mixed} s The value being converted
14309      * @return {Number} The comparison value
14310      */
14311     asDate : function(s) {
14312         if(!s){
14313             return 0;
14314         }
14315         if(s instanceof Date){
14316             return s.getTime();
14317         }
14318         return Date.parse(String(s));
14319     },
14320     
14321     /**
14322      * Float sorting
14323      * @param {Mixed} s The value being converted
14324      * @return {Float} The comparison value
14325      */
14326     asFloat : function(s) {
14327         var val = parseFloat(String(s).replace(/,/g, ""));
14328         if(isNaN(val)) {
14329             val = 0;
14330         }
14331         return val;
14332     },
14333     
14334     /**
14335      * Integer sorting
14336      * @param {Mixed} s The value being converted
14337      * @return {Number} The comparison value
14338      */
14339     asInt : function(s) {
14340         var val = parseInt(String(s).replace(/,/g, ""));
14341         if(isNaN(val)) {
14342             val = 0;
14343         }
14344         return val;
14345     }
14346 };/*
14347  * Based on:
14348  * Ext JS Library 1.1.1
14349  * Copyright(c) 2006-2007, Ext JS, LLC.
14350  *
14351  * Originally Released Under LGPL - original licence link has changed is not relivant.
14352  *
14353  * Fork - LGPL
14354  * <script type="text/javascript">
14355  */
14356
14357 /**
14358 * @class Roo.data.Record
14359  * Instances of this class encapsulate both record <em>definition</em> information, and record
14360  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14361  * to access Records cached in an {@link Roo.data.Store} object.<br>
14362  * <p>
14363  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14364  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14365  * objects.<br>
14366  * <p>
14367  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14368  * @constructor
14369  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14370  * {@link #create}. The parameters are the same.
14371  * @param {Array} data An associative Array of data values keyed by the field name.
14372  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14373  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14374  * not specified an integer id is generated.
14375  */
14376 Roo.data.Record = function(data, id){
14377     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14378     this.data = data;
14379 };
14380
14381 /**
14382  * Generate a constructor for a specific record layout.
14383  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14384  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14385  * Each field definition object may contain the following properties: <ul>
14386  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14387  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14388  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14389  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14390  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14391  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14392  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14393  * this may be omitted.</p></li>
14394  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14395  * <ul><li>auto (Default, implies no conversion)</li>
14396  * <li>string</li>
14397  * <li>int</li>
14398  * <li>float</li>
14399  * <li>boolean</li>
14400  * <li>date</li></ul></p></li>
14401  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14402  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14403  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14404  * by the Reader into an object that will be stored in the Record. It is passed the
14405  * following parameters:<ul>
14406  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14407  * </ul></p></li>
14408  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14409  * </ul>
14410  * <br>usage:<br><pre><code>
14411 var TopicRecord = Roo.data.Record.create(
14412     {name: 'title', mapping: 'topic_title'},
14413     {name: 'author', mapping: 'username'},
14414     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14415     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14416     {name: 'lastPoster', mapping: 'user2'},
14417     {name: 'excerpt', mapping: 'post_text'}
14418 );
14419
14420 var myNewRecord = new TopicRecord({
14421     title: 'Do my job please',
14422     author: 'noobie',
14423     totalPosts: 1,
14424     lastPost: new Date(),
14425     lastPoster: 'Animal',
14426     excerpt: 'No way dude!'
14427 });
14428 myStore.add(myNewRecord);
14429 </code></pre>
14430  * @method create
14431  * @static
14432  */
14433 Roo.data.Record.create = function(o){
14434     var f = function(){
14435         f.superclass.constructor.apply(this, arguments);
14436     };
14437     Roo.extend(f, Roo.data.Record);
14438     var p = f.prototype;
14439     p.fields = new Roo.util.MixedCollection(false, function(field){
14440         return field.name;
14441     });
14442     for(var i = 0, len = o.length; i < len; i++){
14443         p.fields.add(new Roo.data.Field(o[i]));
14444     }
14445     f.getField = function(name){
14446         return p.fields.get(name);  
14447     };
14448     return f;
14449 };
14450
14451 Roo.data.Record.AUTO_ID = 1000;
14452 Roo.data.Record.EDIT = 'edit';
14453 Roo.data.Record.REJECT = 'reject';
14454 Roo.data.Record.COMMIT = 'commit';
14455
14456 Roo.data.Record.prototype = {
14457     /**
14458      * Readonly flag - true if this record has been modified.
14459      * @type Boolean
14460      */
14461     dirty : false,
14462     editing : false,
14463     error: null,
14464     modified: null,
14465
14466     // private
14467     join : function(store){
14468         this.store = store;
14469     },
14470
14471     /**
14472      * Set the named field to the specified value.
14473      * @param {String} name The name of the field to set.
14474      * @param {Object} value The value to set the field to.
14475      */
14476     set : function(name, value){
14477         if(this.data[name] == value){
14478             return;
14479         }
14480         this.dirty = true;
14481         if(!this.modified){
14482             this.modified = {};
14483         }
14484         if(typeof this.modified[name] == 'undefined'){
14485             this.modified[name] = this.data[name];
14486         }
14487         this.data[name] = value;
14488         if(!this.editing && this.store){
14489             this.store.afterEdit(this);
14490         }       
14491     },
14492
14493     /**
14494      * Get the value of the named field.
14495      * @param {String} name The name of the field to get the value of.
14496      * @return {Object} The value of the field.
14497      */
14498     get : function(name){
14499         return this.data[name]; 
14500     },
14501
14502     // private
14503     beginEdit : function(){
14504         this.editing = true;
14505         this.modified = {}; 
14506     },
14507
14508     // private
14509     cancelEdit : function(){
14510         this.editing = false;
14511         delete this.modified;
14512     },
14513
14514     // private
14515     endEdit : function(){
14516         this.editing = false;
14517         if(this.dirty && this.store){
14518             this.store.afterEdit(this);
14519         }
14520     },
14521
14522     /**
14523      * Usually called by the {@link Roo.data.Store} which owns the Record.
14524      * Rejects all changes made to the Record since either creation, or the last commit operation.
14525      * Modified fields are reverted to their original values.
14526      * <p>
14527      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14528      * of reject operations.
14529      */
14530     reject : function(){
14531         var m = this.modified;
14532         for(var n in m){
14533             if(typeof m[n] != "function"){
14534                 this.data[n] = m[n];
14535             }
14536         }
14537         this.dirty = false;
14538         delete this.modified;
14539         this.editing = false;
14540         if(this.store){
14541             this.store.afterReject(this);
14542         }
14543     },
14544
14545     /**
14546      * Usually called by the {@link Roo.data.Store} which owns the Record.
14547      * Commits all changes made to the Record since either creation, or the last commit operation.
14548      * <p>
14549      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14550      * of commit operations.
14551      */
14552     commit : function(){
14553         this.dirty = false;
14554         delete this.modified;
14555         this.editing = false;
14556         if(this.store){
14557             this.store.afterCommit(this);
14558         }
14559     },
14560
14561     // private
14562     hasError : function(){
14563         return this.error != null;
14564     },
14565
14566     // private
14567     clearError : function(){
14568         this.error = null;
14569     },
14570
14571     /**
14572      * Creates a copy of this record.
14573      * @param {String} id (optional) A new record id if you don't want to use this record's id
14574      * @return {Record}
14575      */
14576     copy : function(newId) {
14577         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14578     }
14579 };/*
14580  * Based on:
14581  * Ext JS Library 1.1.1
14582  * Copyright(c) 2006-2007, Ext JS, LLC.
14583  *
14584  * Originally Released Under LGPL - original licence link has changed is not relivant.
14585  *
14586  * Fork - LGPL
14587  * <script type="text/javascript">
14588  */
14589
14590
14591
14592 /**
14593  * @class Roo.data.Store
14594  * @extends Roo.util.Observable
14595  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14596  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14597  * <p>
14598  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
14599  * has no knowledge of the format of the data returned by the Proxy.<br>
14600  * <p>
14601  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14602  * instances from the data object. These records are cached and made available through accessor functions.
14603  * @constructor
14604  * Creates a new Store.
14605  * @param {Object} config A config object containing the objects needed for the Store to access data,
14606  * and read the data into Records.
14607  */
14608 Roo.data.Store = function(config){
14609     this.data = new Roo.util.MixedCollection(false);
14610     this.data.getKey = function(o){
14611         return o.id;
14612     };
14613     this.baseParams = {};
14614     // private
14615     this.paramNames = {
14616         "start" : "start",
14617         "limit" : "limit",
14618         "sort" : "sort",
14619         "dir" : "dir",
14620         "multisort" : "_multisort"
14621     };
14622
14623     if(config && config.data){
14624         this.inlineData = config.data;
14625         delete config.data;
14626     }
14627
14628     Roo.apply(this, config);
14629     
14630     if(this.reader){ // reader passed
14631         this.reader = Roo.factory(this.reader, Roo.data);
14632         this.reader.xmodule = this.xmodule || false;
14633         if(!this.recordType){
14634             this.recordType = this.reader.recordType;
14635         }
14636         if(this.reader.onMetaChange){
14637             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14638         }
14639     }
14640
14641     if(this.recordType){
14642         this.fields = this.recordType.prototype.fields;
14643     }
14644     this.modified = [];
14645
14646     this.addEvents({
14647         /**
14648          * @event datachanged
14649          * Fires when the data cache has changed, and a widget which is using this Store
14650          * as a Record cache should refresh its view.
14651          * @param {Store} this
14652          */
14653         datachanged : true,
14654         /**
14655          * @event metachange
14656          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14657          * @param {Store} this
14658          * @param {Object} meta The JSON metadata
14659          */
14660         metachange : true,
14661         /**
14662          * @event add
14663          * Fires when Records have been added to the Store
14664          * @param {Store} this
14665          * @param {Roo.data.Record[]} records The array of Records added
14666          * @param {Number} index The index at which the record(s) were added
14667          */
14668         add : true,
14669         /**
14670          * @event remove
14671          * Fires when a Record has been removed from the Store
14672          * @param {Store} this
14673          * @param {Roo.data.Record} record The Record that was removed
14674          * @param {Number} index The index at which the record was removed
14675          */
14676         remove : true,
14677         /**
14678          * @event update
14679          * Fires when a Record has been updated
14680          * @param {Store} this
14681          * @param {Roo.data.Record} record The Record that was updated
14682          * @param {String} operation The update operation being performed.  Value may be one of:
14683          * <pre><code>
14684  Roo.data.Record.EDIT
14685  Roo.data.Record.REJECT
14686  Roo.data.Record.COMMIT
14687          * </code></pre>
14688          */
14689         update : true,
14690         /**
14691          * @event clear
14692          * Fires when the data cache has been cleared.
14693          * @param {Store} this
14694          */
14695         clear : true,
14696         /**
14697          * @event beforeload
14698          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14699          * the load action will be canceled.
14700          * @param {Store} this
14701          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14702          */
14703         beforeload : true,
14704         /**
14705          * @event beforeloadadd
14706          * Fires after a new set of Records has been loaded.
14707          * @param {Store} this
14708          * @param {Roo.data.Record[]} records The Records that were loaded
14709          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14710          */
14711         beforeloadadd : true,
14712         /**
14713          * @event load
14714          * Fires after a new set of Records has been loaded, before they are added to the store.
14715          * @param {Store} this
14716          * @param {Roo.data.Record[]} records The Records that were loaded
14717          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14718          * @params {Object} return from reader
14719          */
14720         load : true,
14721         /**
14722          * @event loadexception
14723          * Fires if an exception occurs in the Proxy during loading.
14724          * Called with the signature of the Proxy's "loadexception" event.
14725          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14726          * 
14727          * @param {Proxy} 
14728          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14729          * @param {Object} load options 
14730          * @param {Object} jsonData from your request (normally this contains the Exception)
14731          */
14732         loadexception : true
14733     });
14734     
14735     if(this.proxy){
14736         this.proxy = Roo.factory(this.proxy, Roo.data);
14737         this.proxy.xmodule = this.xmodule || false;
14738         this.relayEvents(this.proxy,  ["loadexception"]);
14739     }
14740     this.sortToggle = {};
14741     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14742
14743     Roo.data.Store.superclass.constructor.call(this);
14744
14745     if(this.inlineData){
14746         this.loadData(this.inlineData);
14747         delete this.inlineData;
14748     }
14749 };
14750
14751 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14752      /**
14753     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14754     * without a remote query - used by combo/forms at present.
14755     */
14756     
14757     /**
14758     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14759     */
14760     /**
14761     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14762     */
14763     /**
14764     * @cfg {Roo.data.Reader} reader [required]  The Reader object which processes the data object and returns
14765     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14766     */
14767     /**
14768     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14769     * on any HTTP request
14770     */
14771     /**
14772     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14773     */
14774     /**
14775     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14776     */
14777     multiSort: false,
14778     /**
14779     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14780     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14781     */
14782     remoteSort : false,
14783
14784     /**
14785     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14786      * loaded or when a record is removed. (defaults to false).
14787     */
14788     pruneModifiedRecords : false,
14789
14790     // private
14791     lastOptions : null,
14792
14793     /**
14794      * Add Records to the Store and fires the add event.
14795      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14796      */
14797     add : function(records){
14798         records = [].concat(records);
14799         for(var i = 0, len = records.length; i < len; i++){
14800             records[i].join(this);
14801         }
14802         var index = this.data.length;
14803         this.data.addAll(records);
14804         this.fireEvent("add", this, records, index);
14805     },
14806
14807     /**
14808      * Remove a Record from the Store and fires the remove event.
14809      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14810      */
14811     remove : function(record){
14812         var index = this.data.indexOf(record);
14813         this.data.removeAt(index);
14814  
14815         if(this.pruneModifiedRecords){
14816             this.modified.remove(record);
14817         }
14818         this.fireEvent("remove", this, record, index);
14819     },
14820
14821     /**
14822      * Remove all Records from the Store and fires the clear event.
14823      */
14824     removeAll : function(){
14825         this.data.clear();
14826         if(this.pruneModifiedRecords){
14827             this.modified = [];
14828         }
14829         this.fireEvent("clear", this);
14830     },
14831
14832     /**
14833      * Inserts Records to the Store at the given index and fires the add event.
14834      * @param {Number} index The start index at which to insert the passed Records.
14835      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14836      */
14837     insert : function(index, records){
14838         records = [].concat(records);
14839         for(var i = 0, len = records.length; i < len; i++){
14840             this.data.insert(index, records[i]);
14841             records[i].join(this);
14842         }
14843         this.fireEvent("add", this, records, index);
14844     },
14845
14846     /**
14847      * Get the index within the cache of the passed Record.
14848      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14849      * @return {Number} The index of the passed Record. Returns -1 if not found.
14850      */
14851     indexOf : function(record){
14852         return this.data.indexOf(record);
14853     },
14854
14855     /**
14856      * Get the index within the cache of the Record with the passed id.
14857      * @param {String} id The id of the Record to find.
14858      * @return {Number} The index of the Record. Returns -1 if not found.
14859      */
14860     indexOfId : function(id){
14861         return this.data.indexOfKey(id);
14862     },
14863
14864     /**
14865      * Get the Record with the specified id.
14866      * @param {String} id The id of the Record to find.
14867      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14868      */
14869     getById : function(id){
14870         return this.data.key(id);
14871     },
14872
14873     /**
14874      * Get the Record at the specified index.
14875      * @param {Number} index The index of the Record to find.
14876      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14877      */
14878     getAt : function(index){
14879         return this.data.itemAt(index);
14880     },
14881
14882     /**
14883      * Returns a range of Records between specified indices.
14884      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14885      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14886      * @return {Roo.data.Record[]} An array of Records
14887      */
14888     getRange : function(start, end){
14889         return this.data.getRange(start, end);
14890     },
14891
14892     // private
14893     storeOptions : function(o){
14894         o = Roo.apply({}, o);
14895         delete o.callback;
14896         delete o.scope;
14897         this.lastOptions = o;
14898     },
14899
14900     /**
14901      * Loads the Record cache from the configured Proxy using the configured Reader.
14902      * <p>
14903      * If using remote paging, then the first load call must specify the <em>start</em>
14904      * and <em>limit</em> properties in the options.params property to establish the initial
14905      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14906      * <p>
14907      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14908      * and this call will return before the new data has been loaded. Perform any post-processing
14909      * in a callback function, or in a "load" event handler.</strong>
14910      * <p>
14911      * @param {Object} options An object containing properties which control loading options:<ul>
14912      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14913      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14914      * passed the following arguments:<ul>
14915      * <li>r : Roo.data.Record[]</li>
14916      * <li>options: Options object from the load call</li>
14917      * <li>success: Boolean success indicator</li></ul></li>
14918      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14919      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14920      * </ul>
14921      */
14922     load : function(options){
14923         options = options || {};
14924         if(this.fireEvent("beforeload", this, options) !== false){
14925             this.storeOptions(options);
14926             var p = Roo.apply(options.params || {}, this.baseParams);
14927             // if meta was not loaded from remote source.. try requesting it.
14928             if (!this.reader.metaFromRemote) {
14929                 p._requestMeta = 1;
14930             }
14931             if(this.sortInfo && this.remoteSort){
14932                 var pn = this.paramNames;
14933                 p[pn["sort"]] = this.sortInfo.field;
14934                 p[pn["dir"]] = this.sortInfo.direction;
14935             }
14936             if (this.multiSort) {
14937                 var pn = this.paramNames;
14938                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14939             }
14940             
14941             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14942         }
14943     },
14944
14945     /**
14946      * Reloads the Record cache from the configured Proxy using the configured Reader and
14947      * the options from the last load operation performed.
14948      * @param {Object} options (optional) An object containing properties which may override the options
14949      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14950      * the most recently used options are reused).
14951      */
14952     reload : function(options){
14953         this.load(Roo.applyIf(options||{}, this.lastOptions));
14954     },
14955
14956     // private
14957     // Called as a callback by the Reader during a load operation.
14958     loadRecords : function(o, options, success){
14959         if(!o || success === false){
14960             if(success !== false){
14961                 this.fireEvent("load", this, [], options, o);
14962             }
14963             if(options.callback){
14964                 options.callback.call(options.scope || this, [], options, false);
14965             }
14966             return;
14967         }
14968         // if data returned failure - throw an exception.
14969         if (o.success === false) {
14970             // show a message if no listener is registered.
14971             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14972                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14973             }
14974             // loadmask wil be hooked into this..
14975             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14976             return;
14977         }
14978         var r = o.records, t = o.totalRecords || r.length;
14979         
14980         this.fireEvent("beforeloadadd", this, r, options, o);
14981         
14982         if(!options || options.add !== true){
14983             if(this.pruneModifiedRecords){
14984                 this.modified = [];
14985             }
14986             for(var i = 0, len = r.length; i < len; i++){
14987                 r[i].join(this);
14988             }
14989             if(this.snapshot){
14990                 this.data = this.snapshot;
14991                 delete this.snapshot;
14992             }
14993             this.data.clear();
14994             this.data.addAll(r);
14995             this.totalLength = t;
14996             this.applySort();
14997             this.fireEvent("datachanged", this);
14998         }else{
14999             this.totalLength = Math.max(t, this.data.length+r.length);
15000             this.add(r);
15001         }
15002         
15003         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15004                 
15005             var e = new Roo.data.Record({});
15006
15007             e.set(this.parent.displayField, this.parent.emptyTitle);
15008             e.set(this.parent.valueField, '');
15009
15010             this.insert(0, e);
15011         }
15012             
15013         this.fireEvent("load", this, r, options, o);
15014         if(options.callback){
15015             options.callback.call(options.scope || this, r, options, true);
15016         }
15017     },
15018
15019
15020     /**
15021      * Loads data from a passed data block. A Reader which understands the format of the data
15022      * must have been configured in the constructor.
15023      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15024      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15025      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15026      */
15027     loadData : function(o, append){
15028         var r = this.reader.readRecords(o);
15029         this.loadRecords(r, {add: append}, true);
15030     },
15031     
15032      /**
15033      * using 'cn' the nested child reader read the child array into it's child stores.
15034      * @param {Object} rec The record with a 'children array
15035      */
15036     loadDataFromChildren : function(rec)
15037     {
15038         this.loadData(this.reader.toLoadData(rec));
15039     },
15040     
15041
15042     /**
15043      * Gets the number of cached records.
15044      * <p>
15045      * <em>If using paging, this may not be the total size of the dataset. If the data object
15046      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15047      * the data set size</em>
15048      */
15049     getCount : function(){
15050         return this.data.length || 0;
15051     },
15052
15053     /**
15054      * Gets the total number of records in the dataset as returned by the server.
15055      * <p>
15056      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15057      * the dataset size</em>
15058      */
15059     getTotalCount : function(){
15060         return this.totalLength || 0;
15061     },
15062
15063     /**
15064      * Returns the sort state of the Store as an object with two properties:
15065      * <pre><code>
15066  field {String} The name of the field by which the Records are sorted
15067  direction {String} The sort order, "ASC" or "DESC"
15068      * </code></pre>
15069      */
15070     getSortState : function(){
15071         return this.sortInfo;
15072     },
15073
15074     // private
15075     applySort : function(){
15076         if(this.sortInfo && !this.remoteSort){
15077             var s = this.sortInfo, f = s.field;
15078             var st = this.fields.get(f).sortType;
15079             var fn = function(r1, r2){
15080                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15081                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15082             };
15083             this.data.sort(s.direction, fn);
15084             if(this.snapshot && this.snapshot != this.data){
15085                 this.snapshot.sort(s.direction, fn);
15086             }
15087         }
15088     },
15089
15090     /**
15091      * Sets the default sort column and order to be used by the next load operation.
15092      * @param {String} fieldName The name of the field to sort by.
15093      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15094      */
15095     setDefaultSort : function(field, dir){
15096         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15097     },
15098
15099     /**
15100      * Sort the Records.
15101      * If remote sorting is used, the sort is performed on the server, and the cache is
15102      * reloaded. If local sorting is used, the cache is sorted internally.
15103      * @param {String} fieldName The name of the field to sort by.
15104      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15105      */
15106     sort : function(fieldName, dir){
15107         var f = this.fields.get(fieldName);
15108         if(!dir){
15109             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15110             
15111             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15112                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15113             }else{
15114                 dir = f.sortDir;
15115             }
15116         }
15117         this.sortToggle[f.name] = dir;
15118         this.sortInfo = {field: f.name, direction: dir};
15119         if(!this.remoteSort){
15120             this.applySort();
15121             this.fireEvent("datachanged", this);
15122         }else{
15123             this.load(this.lastOptions);
15124         }
15125     },
15126
15127     /**
15128      * Calls the specified function for each of the Records in the cache.
15129      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15130      * Returning <em>false</em> aborts and exits the iteration.
15131      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15132      */
15133     each : function(fn, scope){
15134         this.data.each(fn, scope);
15135     },
15136
15137     /**
15138      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15139      * (e.g., during paging).
15140      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15141      */
15142     getModifiedRecords : function(){
15143         return this.modified;
15144     },
15145
15146     // private
15147     createFilterFn : function(property, value, anyMatch){
15148         if(!value.exec){ // not a regex
15149             value = String(value);
15150             if(value.length == 0){
15151                 return false;
15152             }
15153             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15154         }
15155         return function(r){
15156             return value.test(r.data[property]);
15157         };
15158     },
15159
15160     /**
15161      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15162      * @param {String} property A field on your records
15163      * @param {Number} start The record index to start at (defaults to 0)
15164      * @param {Number} end The last record index to include (defaults to length - 1)
15165      * @return {Number} The sum
15166      */
15167     sum : function(property, start, end){
15168         var rs = this.data.items, v = 0;
15169         start = start || 0;
15170         end = (end || end === 0) ? end : rs.length-1;
15171
15172         for(var i = start; i <= end; i++){
15173             v += (rs[i].data[property] || 0);
15174         }
15175         return v;
15176     },
15177
15178     /**
15179      * Filter the records by a specified property.
15180      * @param {String} field A field on your records
15181      * @param {String/RegExp} value Either a string that the field
15182      * should start with or a RegExp to test against the field
15183      * @param {Boolean} anyMatch True to match any part not just the beginning
15184      */
15185     filter : function(property, value, anyMatch){
15186         var fn = this.createFilterFn(property, value, anyMatch);
15187         return fn ? this.filterBy(fn) : this.clearFilter();
15188     },
15189
15190     /**
15191      * Filter by a function. The specified function will be called with each
15192      * record in this data source. If the function returns true the record is included,
15193      * otherwise it is filtered.
15194      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15195      * @param {Object} scope (optional) The scope of the function (defaults to this)
15196      */
15197     filterBy : function(fn, scope){
15198         this.snapshot = this.snapshot || this.data;
15199         this.data = this.queryBy(fn, scope||this);
15200         this.fireEvent("datachanged", this);
15201     },
15202
15203     /**
15204      * Query the records by a specified property.
15205      * @param {String} field A field on your records
15206      * @param {String/RegExp} value Either a string that the field
15207      * should start with or a RegExp to test against the field
15208      * @param {Boolean} anyMatch True to match any part not just the beginning
15209      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15210      */
15211     query : function(property, value, anyMatch){
15212         var fn = this.createFilterFn(property, value, anyMatch);
15213         return fn ? this.queryBy(fn) : this.data.clone();
15214     },
15215
15216     /**
15217      * Query by a function. The specified function will be called with each
15218      * record in this data source. If the function returns true the record is included
15219      * in the results.
15220      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15221      * @param {Object} scope (optional) The scope of the function (defaults to this)
15222       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15223      **/
15224     queryBy : function(fn, scope){
15225         var data = this.snapshot || this.data;
15226         return data.filterBy(fn, scope||this);
15227     },
15228
15229     /**
15230      * Collects unique values for a particular dataIndex from this store.
15231      * @param {String} dataIndex The property to collect
15232      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15233      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15234      * @return {Array} An array of the unique values
15235      **/
15236     collect : function(dataIndex, allowNull, bypassFilter){
15237         var d = (bypassFilter === true && this.snapshot) ?
15238                 this.snapshot.items : this.data.items;
15239         var v, sv, r = [], l = {};
15240         for(var i = 0, len = d.length; i < len; i++){
15241             v = d[i].data[dataIndex];
15242             sv = String(v);
15243             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15244                 l[sv] = true;
15245                 r[r.length] = v;
15246             }
15247         }
15248         return r;
15249     },
15250
15251     /**
15252      * Revert to a view of the Record cache with no filtering applied.
15253      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15254      */
15255     clearFilter : function(suppressEvent){
15256         if(this.snapshot && this.snapshot != this.data){
15257             this.data = this.snapshot;
15258             delete this.snapshot;
15259             if(suppressEvent !== true){
15260                 this.fireEvent("datachanged", this);
15261             }
15262         }
15263     },
15264
15265     // private
15266     afterEdit : function(record){
15267         if(this.modified.indexOf(record) == -1){
15268             this.modified.push(record);
15269         }
15270         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15271     },
15272     
15273     // private
15274     afterReject : function(record){
15275         this.modified.remove(record);
15276         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15277     },
15278
15279     // private
15280     afterCommit : function(record){
15281         this.modified.remove(record);
15282         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15283     },
15284
15285     /**
15286      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15287      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15288      */
15289     commitChanges : function(){
15290         var m = this.modified.slice(0);
15291         this.modified = [];
15292         for(var i = 0, len = m.length; i < len; i++){
15293             m[i].commit();
15294         }
15295     },
15296
15297     /**
15298      * Cancel outstanding changes on all changed records.
15299      */
15300     rejectChanges : function(){
15301         var m = this.modified.slice(0);
15302         this.modified = [];
15303         for(var i = 0, len = m.length; i < len; i++){
15304             m[i].reject();
15305         }
15306     },
15307
15308     onMetaChange : function(meta, rtype, o){
15309         this.recordType = rtype;
15310         this.fields = rtype.prototype.fields;
15311         delete this.snapshot;
15312         this.sortInfo = meta.sortInfo || this.sortInfo;
15313         this.modified = [];
15314         this.fireEvent('metachange', this, this.reader.meta);
15315     },
15316     
15317     moveIndex : function(data, type)
15318     {
15319         var index = this.indexOf(data);
15320         
15321         var newIndex = index + type;
15322         
15323         this.remove(data);
15324         
15325         this.insert(newIndex, data);
15326         
15327     }
15328 });/*
15329  * Based on:
15330  * Ext JS Library 1.1.1
15331  * Copyright(c) 2006-2007, Ext JS, LLC.
15332  *
15333  * Originally Released Under LGPL - original licence link has changed is not relivant.
15334  *
15335  * Fork - LGPL
15336  * <script type="text/javascript">
15337  */
15338
15339 /**
15340  * @class Roo.data.SimpleStore
15341  * @extends Roo.data.Store
15342  * Small helper class to make creating Stores from Array data easier.
15343  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15344  * @cfg {Array} fields An array of field definition objects, or field name strings.
15345  * @cfg {Object} an existing reader (eg. copied from another store)
15346  * @cfg {Array} data The multi-dimensional array of data
15347  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15348  * @cfg {Roo.data.Reader} reader  [not-required] 
15349  * @constructor
15350  * @param {Object} config
15351  */
15352 Roo.data.SimpleStore = function(config)
15353 {
15354     Roo.data.SimpleStore.superclass.constructor.call(this, {
15355         isLocal : true,
15356         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15357                 id: config.id
15358             },
15359             Roo.data.Record.create(config.fields)
15360         ),
15361         proxy : new Roo.data.MemoryProxy(config.data)
15362     });
15363     this.load();
15364 };
15365 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15366  * Based on:
15367  * Ext JS Library 1.1.1
15368  * Copyright(c) 2006-2007, Ext JS, LLC.
15369  *
15370  * Originally Released Under LGPL - original licence link has changed is not relivant.
15371  *
15372  * Fork - LGPL
15373  * <script type="text/javascript">
15374  */
15375
15376 /**
15377 /**
15378  * @extends Roo.data.Store
15379  * @class Roo.data.JsonStore
15380  * Small helper class to make creating Stores for JSON data easier. <br/>
15381 <pre><code>
15382 var store = new Roo.data.JsonStore({
15383     url: 'get-images.php',
15384     root: 'images',
15385     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15386 });
15387 </code></pre>
15388  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15389  * JsonReader and HttpProxy (unless inline data is provided).</b>
15390  * @cfg {Array} fields An array of field definition objects, or field name strings.
15391  * @constructor
15392  * @param {Object} config
15393  */
15394 Roo.data.JsonStore = function(c){
15395     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15396         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15397         reader: new Roo.data.JsonReader(c, c.fields)
15398     }));
15399 };
15400 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15401  * Based on:
15402  * Ext JS Library 1.1.1
15403  * Copyright(c) 2006-2007, Ext JS, LLC.
15404  *
15405  * Originally Released Under LGPL - original licence link has changed is not relivant.
15406  *
15407  * Fork - LGPL
15408  * <script type="text/javascript">
15409  */
15410
15411  
15412 Roo.data.Field = function(config){
15413     if(typeof config == "string"){
15414         config = {name: config};
15415     }
15416     Roo.apply(this, config);
15417     
15418     if(!this.type){
15419         this.type = "auto";
15420     }
15421     
15422     var st = Roo.data.SortTypes;
15423     // named sortTypes are supported, here we look them up
15424     if(typeof this.sortType == "string"){
15425         this.sortType = st[this.sortType];
15426     }
15427     
15428     // set default sortType for strings and dates
15429     if(!this.sortType){
15430         switch(this.type){
15431             case "string":
15432                 this.sortType = st.asUCString;
15433                 break;
15434             case "date":
15435                 this.sortType = st.asDate;
15436                 break;
15437             default:
15438                 this.sortType = st.none;
15439         }
15440     }
15441
15442     // define once
15443     var stripRe = /[\$,%]/g;
15444
15445     // prebuilt conversion function for this field, instead of
15446     // switching every time we're reading a value
15447     if(!this.convert){
15448         var cv, dateFormat = this.dateFormat;
15449         switch(this.type){
15450             case "":
15451             case "auto":
15452             case undefined:
15453                 cv = function(v){ return v; };
15454                 break;
15455             case "string":
15456                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15457                 break;
15458             case "int":
15459                 cv = function(v){
15460                     return v !== undefined && v !== null && v !== '' ?
15461                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15462                     };
15463                 break;
15464             case "float":
15465                 cv = function(v){
15466                     return v !== undefined && v !== null && v !== '' ?
15467                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15468                     };
15469                 break;
15470             case "bool":
15471             case "boolean":
15472                 cv = function(v){ return v === true || v === "true" || v == 1; };
15473                 break;
15474             case "date":
15475                 cv = function(v){
15476                     if(!v){
15477                         return '';
15478                     }
15479                     if(v instanceof Date){
15480                         return v;
15481                     }
15482                     if(dateFormat){
15483                         if(dateFormat == "timestamp"){
15484                             return new Date(v*1000);
15485                         }
15486                         return Date.parseDate(v, dateFormat);
15487                     }
15488                     var parsed = Date.parse(v);
15489                     return parsed ? new Date(parsed) : null;
15490                 };
15491              break;
15492             
15493         }
15494         this.convert = cv;
15495     }
15496 };
15497
15498 Roo.data.Field.prototype = {
15499     dateFormat: null,
15500     defaultValue: "",
15501     mapping: null,
15502     sortType : null,
15503     sortDir : "ASC"
15504 };/*
15505  * Based on:
15506  * Ext JS Library 1.1.1
15507  * Copyright(c) 2006-2007, Ext JS, LLC.
15508  *
15509  * Originally Released Under LGPL - original licence link has changed is not relivant.
15510  *
15511  * Fork - LGPL
15512  * <script type="text/javascript">
15513  */
15514  
15515 // Base class for reading structured data from a data source.  This class is intended to be
15516 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15517
15518 /**
15519  * @class Roo.data.DataReader
15520  * Base class for reading structured data from a data source.  This class is intended to be
15521  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15522  */
15523
15524 Roo.data.DataReader = function(meta, recordType){
15525     
15526     this.meta = meta;
15527     
15528     this.recordType = recordType instanceof Array ? 
15529         Roo.data.Record.create(recordType) : recordType;
15530 };
15531
15532 Roo.data.DataReader.prototype = {
15533     
15534     
15535     readerType : 'Data',
15536      /**
15537      * Create an empty record
15538      * @param {Object} data (optional) - overlay some values
15539      * @return {Roo.data.Record} record created.
15540      */
15541     newRow :  function(d) {
15542         var da =  {};
15543         this.recordType.prototype.fields.each(function(c) {
15544             switch( c.type) {
15545                 case 'int' : da[c.name] = 0; break;
15546                 case 'date' : da[c.name] = new Date(); break;
15547                 case 'float' : da[c.name] = 0.0; break;
15548                 case 'boolean' : da[c.name] = false; break;
15549                 default : da[c.name] = ""; break;
15550             }
15551             
15552         });
15553         return new this.recordType(Roo.apply(da, d));
15554     }
15555     
15556     
15557 };/*
15558  * Based on:
15559  * Ext JS Library 1.1.1
15560  * Copyright(c) 2006-2007, Ext JS, LLC.
15561  *
15562  * Originally Released Under LGPL - original licence link has changed is not relivant.
15563  *
15564  * Fork - LGPL
15565  * <script type="text/javascript">
15566  */
15567
15568 /**
15569  * @class Roo.data.DataProxy
15570  * @extends Roo.data.Observable
15571  * This class is an abstract base class for implementations which provide retrieval of
15572  * unformatted data objects.<br>
15573  * <p>
15574  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15575  * (of the appropriate type which knows how to parse the data object) to provide a block of
15576  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15577  * <p>
15578  * Custom implementations must implement the load method as described in
15579  * {@link Roo.data.HttpProxy#load}.
15580  */
15581 Roo.data.DataProxy = function(){
15582     this.addEvents({
15583         /**
15584          * @event beforeload
15585          * Fires before a network request is made to retrieve a data object.
15586          * @param {Object} This DataProxy object.
15587          * @param {Object} params The params parameter to the load function.
15588          */
15589         beforeload : true,
15590         /**
15591          * @event load
15592          * Fires before the load method's callback is called.
15593          * @param {Object} This DataProxy object.
15594          * @param {Object} o The data object.
15595          * @param {Object} arg The callback argument object passed to the load function.
15596          */
15597         load : true,
15598         /**
15599          * @event loadexception
15600          * Fires if an Exception occurs during data retrieval.
15601          * @param {Object} This DataProxy object.
15602          * @param {Object} o The data object.
15603          * @param {Object} arg The callback argument object passed to the load function.
15604          * @param {Object} e The Exception.
15605          */
15606         loadexception : true
15607     });
15608     Roo.data.DataProxy.superclass.constructor.call(this);
15609 };
15610
15611 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15612
15613     /**
15614      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15615      */
15616 /*
15617  * Based on:
15618  * Ext JS Library 1.1.1
15619  * Copyright(c) 2006-2007, Ext JS, LLC.
15620  *
15621  * Originally Released Under LGPL - original licence link has changed is not relivant.
15622  *
15623  * Fork - LGPL
15624  * <script type="text/javascript">
15625  */
15626 /**
15627  * @class Roo.data.MemoryProxy
15628  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15629  * to the Reader when its load method is called.
15630  * @constructor
15631  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15632  */
15633 Roo.data.MemoryProxy = function(data){
15634     if (data.data) {
15635         data = data.data;
15636     }
15637     Roo.data.MemoryProxy.superclass.constructor.call(this);
15638     this.data = data;
15639 };
15640
15641 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15642     
15643     /**
15644      * Load data from the requested source (in this case an in-memory
15645      * data object passed to the constructor), read the data object into
15646      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15647      * process that block using the passed callback.
15648      * @param {Object} params This parameter is not used by the MemoryProxy class.
15649      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15650      * object into a block of Roo.data.Records.
15651      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15652      * The function must be passed <ul>
15653      * <li>The Record block object</li>
15654      * <li>The "arg" argument from the load function</li>
15655      * <li>A boolean success indicator</li>
15656      * </ul>
15657      * @param {Object} scope The scope in which to call the callback
15658      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15659      */
15660     load : function(params, reader, callback, scope, arg){
15661         params = params || {};
15662         var result;
15663         try {
15664             result = reader.readRecords(params.data ? params.data :this.data);
15665         }catch(e){
15666             this.fireEvent("loadexception", this, arg, null, e);
15667             callback.call(scope, null, arg, false);
15668             return;
15669         }
15670         callback.call(scope, result, arg, true);
15671     },
15672     
15673     // private
15674     update : function(params, records){
15675         
15676     }
15677 });/*
15678  * Based on:
15679  * Ext JS Library 1.1.1
15680  * Copyright(c) 2006-2007, Ext JS, LLC.
15681  *
15682  * Originally Released Under LGPL - original licence link has changed is not relivant.
15683  *
15684  * Fork - LGPL
15685  * <script type="text/javascript">
15686  */
15687 /**
15688  * @class Roo.data.HttpProxy
15689  * @extends Roo.data.DataProxy
15690  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15691  * configured to reference a certain URL.<br><br>
15692  * <p>
15693  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15694  * from which the running page was served.<br><br>
15695  * <p>
15696  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15697  * <p>
15698  * Be aware that to enable the browser to parse an XML document, the server must set
15699  * the Content-Type header in the HTTP response to "text/xml".
15700  * @constructor
15701  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15702  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15703  * will be used to make the request.
15704  */
15705 Roo.data.HttpProxy = function(conn){
15706     Roo.data.HttpProxy.superclass.constructor.call(this);
15707     // is conn a conn config or a real conn?
15708     this.conn = conn;
15709     this.useAjax = !conn || !conn.events;
15710   
15711 };
15712
15713 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15714     // thse are take from connection...
15715     
15716     /**
15717      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15718      */
15719     /**
15720      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15721      * extra parameters to each request made by this object. (defaults to undefined)
15722      */
15723     /**
15724      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15725      *  to each request made by this object. (defaults to undefined)
15726      */
15727     /**
15728      * @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)
15729      */
15730     /**
15731      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15732      */
15733      /**
15734      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15735      * @type Boolean
15736      */
15737   
15738
15739     /**
15740      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15741      * @type Boolean
15742      */
15743     /**
15744      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15745      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15746      * a finer-grained basis than the DataProxy events.
15747      */
15748     getConnection : function(){
15749         return this.useAjax ? Roo.Ajax : this.conn;
15750     },
15751
15752     /**
15753      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15754      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15755      * process that block using the passed callback.
15756      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15757      * for the request to the remote server.
15758      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15759      * object into a block of Roo.data.Records.
15760      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15761      * The function must be passed <ul>
15762      * <li>The Record block object</li>
15763      * <li>The "arg" argument from the load function</li>
15764      * <li>A boolean success indicator</li>
15765      * </ul>
15766      * @param {Object} scope The scope in which to call the callback
15767      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15768      */
15769     load : function(params, reader, callback, scope, arg){
15770         if(this.fireEvent("beforeload", this, params) !== false){
15771             var  o = {
15772                 params : params || {},
15773                 request: {
15774                     callback : callback,
15775                     scope : scope,
15776                     arg : arg
15777                 },
15778                 reader: reader,
15779                 callback : this.loadResponse,
15780                 scope: this
15781             };
15782             if(this.useAjax){
15783                 Roo.applyIf(o, this.conn);
15784                 if(this.activeRequest){
15785                     Roo.Ajax.abort(this.activeRequest);
15786                 }
15787                 this.activeRequest = Roo.Ajax.request(o);
15788             }else{
15789                 this.conn.request(o);
15790             }
15791         }else{
15792             callback.call(scope||this, null, arg, false);
15793         }
15794     },
15795
15796     // private
15797     loadResponse : function(o, success, response){
15798         delete this.activeRequest;
15799         if(!success){
15800             this.fireEvent("loadexception", this, o, response);
15801             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15802             return;
15803         }
15804         var result;
15805         try {
15806             result = o.reader.read(response);
15807         }catch(e){
15808             this.fireEvent("loadexception", this, o, response, e);
15809             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15810             return;
15811         }
15812         
15813         this.fireEvent("load", this, o, o.request.arg);
15814         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15815     },
15816
15817     // private
15818     update : function(dataSet){
15819
15820     },
15821
15822     // private
15823     updateResponse : function(dataSet){
15824
15825     }
15826 });/*
15827  * Based on:
15828  * Ext JS Library 1.1.1
15829  * Copyright(c) 2006-2007, Ext JS, LLC.
15830  *
15831  * Originally Released Under LGPL - original licence link has changed is not relivant.
15832  *
15833  * Fork - LGPL
15834  * <script type="text/javascript">
15835  */
15836
15837 /**
15838  * @class Roo.data.ScriptTagProxy
15839  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15840  * other than the originating domain of the running page.<br><br>
15841  * <p>
15842  * <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
15843  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15844  * <p>
15845  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15846  * source code that is used as the source inside a &lt;script> tag.<br><br>
15847  * <p>
15848  * In order for the browser to process the returned data, the server must wrap the data object
15849  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15850  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15851  * depending on whether the callback name was passed:
15852  * <p>
15853  * <pre><code>
15854 boolean scriptTag = false;
15855 String cb = request.getParameter("callback");
15856 if (cb != null) {
15857     scriptTag = true;
15858     response.setContentType("text/javascript");
15859 } else {
15860     response.setContentType("application/x-json");
15861 }
15862 Writer out = response.getWriter();
15863 if (scriptTag) {
15864     out.write(cb + "(");
15865 }
15866 out.print(dataBlock.toJsonString());
15867 if (scriptTag) {
15868     out.write(");");
15869 }
15870 </pre></code>
15871  *
15872  * @constructor
15873  * @param {Object} config A configuration object.
15874  */
15875 Roo.data.ScriptTagProxy = function(config){
15876     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15877     Roo.apply(this, config);
15878     this.head = document.getElementsByTagName("head")[0];
15879 };
15880
15881 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15882
15883 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15884     /**
15885      * @cfg {String} url The URL from which to request the data object.
15886      */
15887     /**
15888      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15889      */
15890     timeout : 30000,
15891     /**
15892      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15893      * the server the name of the callback function set up by the load call to process the returned data object.
15894      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15895      * javascript output which calls this named function passing the data object as its only parameter.
15896      */
15897     callbackParam : "callback",
15898     /**
15899      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15900      * name to the request.
15901      */
15902     nocache : true,
15903
15904     /**
15905      * Load data from the configured URL, read the data object into
15906      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15907      * process that block using the passed callback.
15908      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15909      * for the request to the remote server.
15910      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15911      * object into a block of Roo.data.Records.
15912      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15913      * The function must be passed <ul>
15914      * <li>The Record block object</li>
15915      * <li>The "arg" argument from the load function</li>
15916      * <li>A boolean success indicator</li>
15917      * </ul>
15918      * @param {Object} scope The scope in which to call the callback
15919      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15920      */
15921     load : function(params, reader, callback, scope, arg){
15922         if(this.fireEvent("beforeload", this, params) !== false){
15923
15924             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15925
15926             var url = this.url;
15927             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15928             if(this.nocache){
15929                 url += "&_dc=" + (new Date().getTime());
15930             }
15931             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15932             var trans = {
15933                 id : transId,
15934                 cb : "stcCallback"+transId,
15935                 scriptId : "stcScript"+transId,
15936                 params : params,
15937                 arg : arg,
15938                 url : url,
15939                 callback : callback,
15940                 scope : scope,
15941                 reader : reader
15942             };
15943             var conn = this;
15944
15945             window[trans.cb] = function(o){
15946                 conn.handleResponse(o, trans);
15947             };
15948
15949             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15950
15951             if(this.autoAbort !== false){
15952                 this.abort();
15953             }
15954
15955             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15956
15957             var script = document.createElement("script");
15958             script.setAttribute("src", url);
15959             script.setAttribute("type", "text/javascript");
15960             script.setAttribute("id", trans.scriptId);
15961             this.head.appendChild(script);
15962
15963             this.trans = trans;
15964         }else{
15965             callback.call(scope||this, null, arg, false);
15966         }
15967     },
15968
15969     // private
15970     isLoading : function(){
15971         return this.trans ? true : false;
15972     },
15973
15974     /**
15975      * Abort the current server request.
15976      */
15977     abort : function(){
15978         if(this.isLoading()){
15979             this.destroyTrans(this.trans);
15980         }
15981     },
15982
15983     // private
15984     destroyTrans : function(trans, isLoaded){
15985         this.head.removeChild(document.getElementById(trans.scriptId));
15986         clearTimeout(trans.timeoutId);
15987         if(isLoaded){
15988             window[trans.cb] = undefined;
15989             try{
15990                 delete window[trans.cb];
15991             }catch(e){}
15992         }else{
15993             // if hasn't been loaded, wait for load to remove it to prevent script error
15994             window[trans.cb] = function(){
15995                 window[trans.cb] = undefined;
15996                 try{
15997                     delete window[trans.cb];
15998                 }catch(e){}
15999             };
16000         }
16001     },
16002
16003     // private
16004     handleResponse : function(o, trans){
16005         this.trans = false;
16006         this.destroyTrans(trans, true);
16007         var result;
16008         try {
16009             result = trans.reader.readRecords(o);
16010         }catch(e){
16011             this.fireEvent("loadexception", this, o, trans.arg, e);
16012             trans.callback.call(trans.scope||window, null, trans.arg, false);
16013             return;
16014         }
16015         this.fireEvent("load", this, o, trans.arg);
16016         trans.callback.call(trans.scope||window, result, trans.arg, true);
16017     },
16018
16019     // private
16020     handleFailure : function(trans){
16021         this.trans = false;
16022         this.destroyTrans(trans, false);
16023         this.fireEvent("loadexception", this, null, trans.arg);
16024         trans.callback.call(trans.scope||window, null, trans.arg, false);
16025     }
16026 });/*
16027  * Based on:
16028  * Ext JS Library 1.1.1
16029  * Copyright(c) 2006-2007, Ext JS, LLC.
16030  *
16031  * Originally Released Under LGPL - original licence link has changed is not relivant.
16032  *
16033  * Fork - LGPL
16034  * <script type="text/javascript">
16035  */
16036
16037 /**
16038  * @class Roo.data.JsonReader
16039  * @extends Roo.data.DataReader
16040  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16041  * based on mappings in a provided Roo.data.Record constructor.
16042  * 
16043  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16044  * in the reply previously. 
16045  * 
16046  * <p>
16047  * Example code:
16048  * <pre><code>
16049 var RecordDef = Roo.data.Record.create([
16050     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16051     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16052 ]);
16053 var myReader = new Roo.data.JsonReader({
16054     totalProperty: "results",    // The property which contains the total dataset size (optional)
16055     root: "rows",                // The property which contains an Array of row objects
16056     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16057 }, RecordDef);
16058 </code></pre>
16059  * <p>
16060  * This would consume a JSON file like this:
16061  * <pre><code>
16062 { 'results': 2, 'rows': [
16063     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16064     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16065 }
16066 </code></pre>
16067  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16068  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16069  * paged from the remote server.
16070  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16071  * @cfg {String} root name of the property which contains the Array of row objects.
16072  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16073  * @cfg {Array} fields Array of field definition objects
16074  * @constructor
16075  * Create a new JsonReader
16076  * @param {Object} meta Metadata configuration options
16077  * @param {Object} recordType Either an Array of field definition objects,
16078  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16079  */
16080 Roo.data.JsonReader = function(meta, recordType){
16081     
16082     meta = meta || {};
16083     // set some defaults:
16084     Roo.applyIf(meta, {
16085         totalProperty: 'total',
16086         successProperty : 'success',
16087         root : 'data',
16088         id : 'id'
16089     });
16090     
16091     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16092 };
16093 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16094     
16095     readerType : 'Json',
16096     
16097     /**
16098      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16099      * Used by Store query builder to append _requestMeta to params.
16100      * 
16101      */
16102     metaFromRemote : false,
16103     /**
16104      * This method is only used by a DataProxy which has retrieved data from a remote server.
16105      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16106      * @return {Object} data A data block which is used by an Roo.data.Store object as
16107      * a cache of Roo.data.Records.
16108      */
16109     read : function(response){
16110         var json = response.responseText;
16111        
16112         var o = /* eval:var:o */ eval("("+json+")");
16113         if(!o) {
16114             throw {message: "JsonReader.read: Json object not found"};
16115         }
16116         
16117         if(o.metaData){
16118             
16119             delete this.ef;
16120             this.metaFromRemote = true;
16121             this.meta = o.metaData;
16122             this.recordType = Roo.data.Record.create(o.metaData.fields);
16123             this.onMetaChange(this.meta, this.recordType, o);
16124         }
16125         return this.readRecords(o);
16126     },
16127
16128     // private function a store will implement
16129     onMetaChange : function(meta, recordType, o){
16130
16131     },
16132
16133     /**
16134          * @ignore
16135          */
16136     simpleAccess: function(obj, subsc) {
16137         return obj[subsc];
16138     },
16139
16140         /**
16141          * @ignore
16142          */
16143     getJsonAccessor: function(){
16144         var re = /[\[\.]/;
16145         return function(expr) {
16146             try {
16147                 return(re.test(expr))
16148                     ? new Function("obj", "return obj." + expr)
16149                     : function(obj){
16150                         return obj[expr];
16151                     };
16152             } catch(e){}
16153             return Roo.emptyFn;
16154         };
16155     }(),
16156
16157     /**
16158      * Create a data block containing Roo.data.Records from an XML document.
16159      * @param {Object} o An object which contains an Array of row objects in the property specified
16160      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16161      * which contains the total size of the dataset.
16162      * @return {Object} data A data block which is used by an Roo.data.Store object as
16163      * a cache of Roo.data.Records.
16164      */
16165     readRecords : function(o){
16166         /**
16167          * After any data loads, the raw JSON data is available for further custom processing.
16168          * @type Object
16169          */
16170         this.o = o;
16171         var s = this.meta, Record = this.recordType,
16172             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16173
16174 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16175         if (!this.ef) {
16176             if(s.totalProperty) {
16177                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16178                 }
16179                 if(s.successProperty) {
16180                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16181                 }
16182                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16183                 if (s.id) {
16184                         var g = this.getJsonAccessor(s.id);
16185                         this.getId = function(rec) {
16186                                 var r = g(rec);  
16187                                 return (r === undefined || r === "") ? null : r;
16188                         };
16189                 } else {
16190                         this.getId = function(){return null;};
16191                 }
16192             this.ef = [];
16193             for(var jj = 0; jj < fl; jj++){
16194                 f = fi[jj];
16195                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16196                 this.ef[jj] = this.getJsonAccessor(map);
16197             }
16198         }
16199
16200         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16201         if(s.totalProperty){
16202             var vt = parseInt(this.getTotal(o), 10);
16203             if(!isNaN(vt)){
16204                 totalRecords = vt;
16205             }
16206         }
16207         if(s.successProperty){
16208             var vs = this.getSuccess(o);
16209             if(vs === false || vs === 'false'){
16210                 success = false;
16211             }
16212         }
16213         var records = [];
16214         for(var i = 0; i < c; i++){
16215                 var n = root[i];
16216             var values = {};
16217             var id = this.getId(n);
16218             for(var j = 0; j < fl; j++){
16219                 f = fi[j];
16220             var v = this.ef[j](n);
16221             if (!f.convert) {
16222                 Roo.log('missing convert for ' + f.name);
16223                 Roo.log(f);
16224                 continue;
16225             }
16226             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16227             }
16228             var record = new Record(values, id);
16229             record.json = n;
16230             records[i] = record;
16231         }
16232         return {
16233             raw : o,
16234             success : success,
16235             records : records,
16236             totalRecords : totalRecords
16237         };
16238     },
16239     // used when loading children.. @see loadDataFromChildren
16240     toLoadData: function(rec)
16241     {
16242         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16243         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16244         return { data : data, total : data.length };
16245         
16246     }
16247 });/*
16248  * Based on:
16249  * Ext JS Library 1.1.1
16250  * Copyright(c) 2006-2007, Ext JS, LLC.
16251  *
16252  * Originally Released Under LGPL - original licence link has changed is not relivant.
16253  *
16254  * Fork - LGPL
16255  * <script type="text/javascript">
16256  */
16257
16258 /**
16259  * @class Roo.data.ArrayReader
16260  * @extends Roo.data.DataReader
16261  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16262  * Each element of that Array represents a row of data fields. The
16263  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16264  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16265  * <p>
16266  * Example code:.
16267  * <pre><code>
16268 var RecordDef = Roo.data.Record.create([
16269     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16270     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16271 ]);
16272 var myReader = new Roo.data.ArrayReader({
16273     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16274 }, RecordDef);
16275 </code></pre>
16276  * <p>
16277  * This would consume an Array like this:
16278  * <pre><code>
16279 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16280   </code></pre>
16281  
16282  * @constructor
16283  * Create a new JsonReader
16284  * @param {Object} meta Metadata configuration options.
16285  * @param {Object|Array} recordType Either an Array of field definition objects
16286  * 
16287  * @cfg {Array} fields Array of field definition objects
16288  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16289  * as specified to {@link Roo.data.Record#create},
16290  * or an {@link Roo.data.Record} object
16291  *
16292  * 
16293  * created using {@link Roo.data.Record#create}.
16294  */
16295 Roo.data.ArrayReader = function(meta, recordType)
16296 {    
16297     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16298 };
16299
16300 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16301     
16302       /**
16303      * Create a data block containing Roo.data.Records from an XML document.
16304      * @param {Object} o An Array of row objects which represents the dataset.
16305      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16306      * a cache of Roo.data.Records.
16307      */
16308     readRecords : function(o)
16309     {
16310         var sid = this.meta ? this.meta.id : null;
16311         var recordType = this.recordType, fields = recordType.prototype.fields;
16312         var records = [];
16313         var root = o;
16314         for(var i = 0; i < root.length; i++){
16315             var n = root[i];
16316             var values = {};
16317             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16318             for(var j = 0, jlen = fields.length; j < jlen; j++){
16319                 var f = fields.items[j];
16320                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16321                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16322                 v = f.convert(v);
16323                 values[f.name] = v;
16324             }
16325             var record = new recordType(values, id);
16326             record.json = n;
16327             records[records.length] = record;
16328         }
16329         return {
16330             records : records,
16331             totalRecords : records.length
16332         };
16333     },
16334     // used when loading children.. @see loadDataFromChildren
16335     toLoadData: function(rec)
16336     {
16337         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16338         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16339         
16340     }
16341     
16342     
16343 });/*
16344  * - LGPL
16345  * * 
16346  */
16347
16348 /**
16349  * @class Roo.bootstrap.ComboBox
16350  * @extends Roo.bootstrap.TriggerField
16351  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16352  * @cfg {Boolean} append (true|false) default false
16353  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16354  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16355  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16356  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16357  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16358  * @cfg {Boolean} animate default true
16359  * @cfg {Boolean} emptyResultText only for touch device
16360  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16361  * @cfg {String} emptyTitle default ''
16362  * @cfg {Number} width fixed with? experimental
16363  * @constructor
16364  * Create a new ComboBox.
16365  * @param {Object} config Configuration options
16366  */
16367 Roo.bootstrap.ComboBox = function(config){
16368     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16369     this.addEvents({
16370         /**
16371          * @event expand
16372          * Fires when the dropdown list is expanded
16373         * @param {Roo.bootstrap.ComboBox} combo This combo box
16374         */
16375         'expand' : true,
16376         /**
16377          * @event collapse
16378          * Fires when the dropdown list is collapsed
16379         * @param {Roo.bootstrap.ComboBox} combo This combo box
16380         */
16381         'collapse' : true,
16382         /**
16383          * @event beforeselect
16384          * Fires before a list item is selected. Return false to cancel the selection.
16385         * @param {Roo.bootstrap.ComboBox} combo This combo box
16386         * @param {Roo.data.Record} record The data record returned from the underlying store
16387         * @param {Number} index The index of the selected item in the dropdown list
16388         */
16389         'beforeselect' : true,
16390         /**
16391          * @event select
16392          * Fires when a list item is selected
16393         * @param {Roo.bootstrap.ComboBox} combo This combo box
16394         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16395         * @param {Number} index The index of the selected item in the dropdown list
16396         */
16397         'select' : true,
16398         /**
16399          * @event beforequery
16400          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16401          * The event object passed has these properties:
16402         * @param {Roo.bootstrap.ComboBox} combo This combo box
16403         * @param {String} query The query
16404         * @param {Boolean} forceAll true to force "all" query
16405         * @param {Boolean} cancel true to cancel the query
16406         * @param {Object} e The query event object
16407         */
16408         'beforequery': true,
16409          /**
16410          * @event add
16411          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16412         * @param {Roo.bootstrap.ComboBox} combo This combo box
16413         */
16414         'add' : true,
16415         /**
16416          * @event edit
16417          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16418         * @param {Roo.bootstrap.ComboBox} combo This combo box
16419         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16420         */
16421         'edit' : true,
16422         /**
16423          * @event remove
16424          * Fires when the remove value from the combobox array
16425         * @param {Roo.bootstrap.ComboBox} combo This combo box
16426         */
16427         'remove' : true,
16428         /**
16429          * @event afterremove
16430          * Fires when the remove value from the combobox array
16431         * @param {Roo.bootstrap.ComboBox} combo This combo box
16432         */
16433         'afterremove' : true,
16434         /**
16435          * @event specialfilter
16436          * Fires when specialfilter
16437             * @param {Roo.bootstrap.ComboBox} combo This combo box
16438             */
16439         'specialfilter' : true,
16440         /**
16441          * @event tick
16442          * Fires when tick the element
16443             * @param {Roo.bootstrap.ComboBox} combo This combo box
16444             */
16445         'tick' : true,
16446         /**
16447          * @event touchviewdisplay
16448          * Fires when touch view require special display (default is using displayField)
16449             * @param {Roo.bootstrap.ComboBox} combo This combo box
16450             * @param {Object} cfg set html .
16451             */
16452         'touchviewdisplay' : true
16453         
16454     });
16455     
16456     this.item = [];
16457     this.tickItems = [];
16458     
16459     this.selectedIndex = -1;
16460     if(this.mode == 'local'){
16461         if(config.queryDelay === undefined){
16462             this.queryDelay = 10;
16463         }
16464         if(config.minChars === undefined){
16465             this.minChars = 0;
16466         }
16467     }
16468 };
16469
16470 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16471      
16472     /**
16473      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16474      * rendering into an Roo.Editor, defaults to false)
16475      */
16476     /**
16477      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16478      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16479      */
16480     /**
16481      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16482      */
16483     /**
16484      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16485      * the dropdown list (defaults to undefined, with no header element)
16486      */
16487
16488      /**
16489      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16490      */
16491      
16492      /**
16493      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16494      */
16495     listWidth: undefined,
16496     /**
16497      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16498      * mode = 'remote' or 'text' if mode = 'local')
16499      */
16500     displayField: undefined,
16501     
16502     /**
16503      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16504      * mode = 'remote' or 'value' if mode = 'local'). 
16505      * Note: use of a valueField requires the user make a selection
16506      * in order for a value to be mapped.
16507      */
16508     valueField: undefined,
16509     /**
16510      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16511      */
16512     modalTitle : '',
16513     
16514     /**
16515      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16516      * field's data value (defaults to the underlying DOM element's name)
16517      */
16518     hiddenName: undefined,
16519     /**
16520      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16521      */
16522     listClass: '',
16523     /**
16524      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16525      */
16526     selectedClass: 'active',
16527     
16528     /**
16529      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16530      */
16531     shadow:'sides',
16532     /**
16533      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16534      * anchor positions (defaults to 'tl-bl')
16535      */
16536     listAlign: 'tl-bl?',
16537     /**
16538      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16539      */
16540     maxHeight: 300,
16541     /**
16542      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16543      * query specified by the allQuery config option (defaults to 'query')
16544      */
16545     triggerAction: 'query',
16546     /**
16547      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16548      * (defaults to 4, does not apply if editable = false)
16549      */
16550     minChars : 4,
16551     /**
16552      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16553      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16554      */
16555     typeAhead: false,
16556     /**
16557      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16558      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16559      */
16560     queryDelay: 500,
16561     /**
16562      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16563      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16564      */
16565     pageSize: 0,
16566     /**
16567      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16568      * when editable = true (defaults to false)
16569      */
16570     selectOnFocus:false,
16571     /**
16572      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16573      */
16574     queryParam: 'query',
16575     /**
16576      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16577      * when mode = 'remote' (defaults to 'Loading...')
16578      */
16579     loadingText: 'Loading...',
16580     /**
16581      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16582      */
16583     resizable: false,
16584     /**
16585      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16586      */
16587     handleHeight : 8,
16588     /**
16589      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16590      * traditional select (defaults to true)
16591      */
16592     editable: true,
16593     /**
16594      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16595      */
16596     allQuery: '',
16597     /**
16598      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16599      */
16600     mode: 'remote',
16601     /**
16602      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16603      * listWidth has a higher value)
16604      */
16605     minListWidth : 70,
16606     /**
16607      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16608      * allow the user to set arbitrary text into the field (defaults to false)
16609      */
16610     forceSelection:false,
16611     /**
16612      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16613      * if typeAhead = true (defaults to 250)
16614      */
16615     typeAheadDelay : 250,
16616     /**
16617      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16618      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16619      */
16620     valueNotFoundText : undefined,
16621     /**
16622      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16623      */
16624     blockFocus : false,
16625     
16626     /**
16627      * @cfg {Boolean} disableClear Disable showing of clear button.
16628      */
16629     disableClear : false,
16630     /**
16631      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16632      */
16633     alwaysQuery : false,
16634     
16635     /**
16636      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16637      */
16638     multiple : false,
16639     
16640     /**
16641      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16642      */
16643     invalidClass : "has-warning",
16644     
16645     /**
16646      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16647      */
16648     validClass : "has-success",
16649     
16650     /**
16651      * @cfg {Boolean} specialFilter (true|false) special filter default false
16652      */
16653     specialFilter : false,
16654     
16655     /**
16656      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16657      */
16658     mobileTouchView : true,
16659     
16660     /**
16661      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16662      */
16663     useNativeIOS : false,
16664     
16665     /**
16666      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16667      */
16668     mobile_restrict_height : false,
16669     
16670     ios_options : false,
16671     
16672     //private
16673     addicon : false,
16674     editicon: false,
16675     
16676     page: 0,
16677     hasQuery: false,
16678     append: false,
16679     loadNext: false,
16680     autoFocus : true,
16681     tickable : false,
16682     btnPosition : 'right',
16683     triggerList : true,
16684     showToggleBtn : true,
16685     animate : true,
16686     emptyResultText: 'Empty',
16687     triggerText : 'Select',
16688     emptyTitle : '',
16689     width : false,
16690     
16691     // element that contains real text value.. (when hidden is used..)
16692     
16693     getAutoCreate : function()
16694     {   
16695         var cfg = false;
16696         //render
16697         /*
16698          * Render classic select for iso
16699          */
16700         
16701         if(Roo.isIOS && this.useNativeIOS){
16702             cfg = this.getAutoCreateNativeIOS();
16703             return cfg;
16704         }
16705         
16706         /*
16707          * Touch Devices
16708          */
16709         
16710         if(Roo.isTouch && this.mobileTouchView){
16711             cfg = this.getAutoCreateTouchView();
16712             return cfg;;
16713         }
16714         
16715         /*
16716          *  Normal ComboBox
16717          */
16718         if(!this.tickable){
16719             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16720             return cfg;
16721         }
16722         
16723         /*
16724          *  ComboBox with tickable selections
16725          */
16726              
16727         var align = this.labelAlign || this.parentLabelAlign();
16728         
16729         cfg = {
16730             cls : 'form-group roo-combobox-tickable' //input-group
16731         };
16732         
16733         var btn_text_select = '';
16734         var btn_text_done = '';
16735         var btn_text_cancel = '';
16736         
16737         if (this.btn_text_show) {
16738             btn_text_select = 'Select';
16739             btn_text_done = 'Done';
16740             btn_text_cancel = 'Cancel'; 
16741         }
16742         
16743         var buttons = {
16744             tag : 'div',
16745             cls : 'tickable-buttons',
16746             cn : [
16747                 {
16748                     tag : 'button',
16749                     type : 'button',
16750                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16751                     //html : this.triggerText
16752                     html: btn_text_select
16753                 },
16754                 {
16755                     tag : 'button',
16756                     type : 'button',
16757                     name : 'ok',
16758                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16759                     //html : 'Done'
16760                     html: btn_text_done
16761                 },
16762                 {
16763                     tag : 'button',
16764                     type : 'button',
16765                     name : 'cancel',
16766                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16767                     //html : 'Cancel'
16768                     html: btn_text_cancel
16769                 }
16770             ]
16771         };
16772         
16773         if(this.editable){
16774             buttons.cn.unshift({
16775                 tag: 'input',
16776                 cls: 'roo-select2-search-field-input'
16777             });
16778         }
16779         
16780         var _this = this;
16781         
16782         Roo.each(buttons.cn, function(c){
16783             if (_this.size) {
16784                 c.cls += ' btn-' + _this.size;
16785             }
16786
16787             if (_this.disabled) {
16788                 c.disabled = true;
16789             }
16790         });
16791         
16792         var box = {
16793             tag: 'div',
16794             style : 'display: contents',
16795             cn: [
16796                 {
16797                     tag: 'input',
16798                     type : 'hidden',
16799                     cls: 'form-hidden-field'
16800                 },
16801                 {
16802                     tag: 'ul',
16803                     cls: 'roo-select2-choices',
16804                     cn:[
16805                         {
16806                             tag: 'li',
16807                             cls: 'roo-select2-search-field',
16808                             cn: [
16809                                 buttons
16810                             ]
16811                         }
16812                     ]
16813                 }
16814             ]
16815         };
16816         
16817         var combobox = {
16818             cls: 'roo-select2-container input-group roo-select2-container-multi',
16819             cn: [
16820                 
16821                 box
16822 //                {
16823 //                    tag: 'ul',
16824 //                    cls: 'typeahead typeahead-long dropdown-menu',
16825 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16826 //                }
16827             ]
16828         };
16829         
16830         if(this.hasFeedback && !this.allowBlank){
16831             
16832             var feedback = {
16833                 tag: 'span',
16834                 cls: 'glyphicon form-control-feedback'
16835             };
16836
16837             combobox.cn.push(feedback);
16838         }
16839         
16840         
16841         
16842         var indicator = {
16843             tag : 'i',
16844             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16845             tooltip : 'This field is required'
16846         };
16847         if (Roo.bootstrap.version == 4) {
16848             indicator = {
16849                 tag : 'i',
16850                 style : 'display:none'
16851             };
16852         }
16853         if (align ==='left' && this.fieldLabel.length) {
16854             
16855             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16856             
16857             cfg.cn = [
16858                 indicator,
16859                 {
16860                     tag: 'label',
16861                     'for' :  id,
16862                     cls : 'control-label col-form-label',
16863                     html : this.fieldLabel
16864
16865                 },
16866                 {
16867                     cls : "", 
16868                     cn: [
16869                         combobox
16870                     ]
16871                 }
16872
16873             ];
16874             
16875             var labelCfg = cfg.cn[1];
16876             var contentCfg = cfg.cn[2];
16877             
16878
16879             if(this.indicatorpos == 'right'){
16880                 
16881                 cfg.cn = [
16882                     {
16883                         tag: 'label',
16884                         'for' :  id,
16885                         cls : 'control-label col-form-label',
16886                         cn : [
16887                             {
16888                                 tag : 'span',
16889                                 html : this.fieldLabel
16890                             },
16891                             indicator
16892                         ]
16893                     },
16894                     {
16895                         cls : "",
16896                         cn: [
16897                             combobox
16898                         ]
16899                     }
16900
16901                 ];
16902                 
16903                 
16904                 
16905                 labelCfg = cfg.cn[0];
16906                 contentCfg = cfg.cn[1];
16907             
16908             }
16909             
16910             if(this.labelWidth > 12){
16911                 labelCfg.style = "width: " + this.labelWidth + 'px';
16912             }
16913             if(this.width * 1 > 0){
16914                 contentCfg.style = "width: " + this.width + 'px';
16915             }
16916             if(this.labelWidth < 13 && this.labelmd == 0){
16917                 this.labelmd = this.labelWidth;
16918             }
16919             
16920             if(this.labellg > 0){
16921                 labelCfg.cls += ' col-lg-' + this.labellg;
16922                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16923             }
16924             
16925             if(this.labelmd > 0){
16926                 labelCfg.cls += ' col-md-' + this.labelmd;
16927                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16928             }
16929             
16930             if(this.labelsm > 0){
16931                 labelCfg.cls += ' col-sm-' + this.labelsm;
16932                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16933             }
16934             
16935             if(this.labelxs > 0){
16936                 labelCfg.cls += ' col-xs-' + this.labelxs;
16937                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16938             }
16939                 
16940                 
16941         } else if ( this.fieldLabel.length) {
16942 //                Roo.log(" label");
16943                  cfg.cn = [
16944                    indicator,
16945                     {
16946                         tag: 'label',
16947                         //cls : 'input-group-addon',
16948                         html : this.fieldLabel
16949                     },
16950                     combobox
16951                 ];
16952                 
16953                 if(this.indicatorpos == 'right'){
16954                     cfg.cn = [
16955                         {
16956                             tag: 'label',
16957                             //cls : 'input-group-addon',
16958                             html : this.fieldLabel
16959                         },
16960                         indicator,
16961                         combobox
16962                     ];
16963                     
16964                 }
16965
16966         } else {
16967             
16968 //                Roo.log(" no label && no align");
16969                 cfg = combobox
16970                      
16971                 
16972         }
16973          
16974         var settings=this;
16975         ['xs','sm','md','lg'].map(function(size){
16976             if (settings[size]) {
16977                 cfg.cls += ' col-' + size + '-' + settings[size];
16978             }
16979         });
16980         
16981         return cfg;
16982         
16983     },
16984     
16985     _initEventsCalled : false,
16986     
16987     // private
16988     initEvents: function()
16989     {   
16990         if (this._initEventsCalled) { // as we call render... prevent looping...
16991             return;
16992         }
16993         this._initEventsCalled = true;
16994         
16995         if (!this.store) {
16996             throw "can not find store for combo";
16997         }
16998         
16999         this.indicator = this.indicatorEl();
17000         
17001         this.store = Roo.factory(this.store, Roo.data);
17002         this.store.parent = this;
17003         
17004         // if we are building from html. then this element is so complex, that we can not really
17005         // use the rendered HTML.
17006         // so we have to trash and replace the previous code.
17007         if (Roo.XComponent.build_from_html) {
17008             // remove this element....
17009             var e = this.el.dom, k=0;
17010             while (e ) { e = e.previousSibling;  ++k;}
17011
17012             this.el.remove();
17013             
17014             this.el=false;
17015             this.rendered = false;
17016             
17017             this.render(this.parent().getChildContainer(true), k);
17018         }
17019         
17020         if(Roo.isIOS && this.useNativeIOS){
17021             this.initIOSView();
17022             return;
17023         }
17024         
17025         /*
17026          * Touch Devices
17027          */
17028         
17029         if(Roo.isTouch && this.mobileTouchView){
17030             this.initTouchView();
17031             return;
17032         }
17033         
17034         if(this.tickable){
17035             this.initTickableEvents();
17036             return;
17037         }
17038         
17039         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17040         
17041         if(this.hiddenName){
17042             
17043             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17044             
17045             this.hiddenField.dom.value =
17046                 this.hiddenValue !== undefined ? this.hiddenValue :
17047                 this.value !== undefined ? this.value : '';
17048
17049             // prevent input submission
17050             this.el.dom.removeAttribute('name');
17051             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17052              
17053              
17054         }
17055         //if(Roo.isGecko){
17056         //    this.el.dom.setAttribute('autocomplete', 'off');
17057         //}
17058         
17059         var cls = 'x-combo-list';
17060         
17061         //this.list = new Roo.Layer({
17062         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17063         //});
17064         
17065         var _this = this;
17066         
17067         (function(){
17068             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17069             _this.list.setWidth(lw);
17070         }).defer(100);
17071         
17072         this.list.on('mouseover', this.onViewOver, this);
17073         this.list.on('mousemove', this.onViewMove, this);
17074         this.list.on('scroll', this.onViewScroll, this);
17075         
17076         /*
17077         this.list.swallowEvent('mousewheel');
17078         this.assetHeight = 0;
17079
17080         if(this.title){
17081             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17082             this.assetHeight += this.header.getHeight();
17083         }
17084
17085         this.innerList = this.list.createChild({cls:cls+'-inner'});
17086         this.innerList.on('mouseover', this.onViewOver, this);
17087         this.innerList.on('mousemove', this.onViewMove, this);
17088         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17089         
17090         if(this.allowBlank && !this.pageSize && !this.disableClear){
17091             this.footer = this.list.createChild({cls:cls+'-ft'});
17092             this.pageTb = new Roo.Toolbar(this.footer);
17093            
17094         }
17095         if(this.pageSize){
17096             this.footer = this.list.createChild({cls:cls+'-ft'});
17097             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17098                     {pageSize: this.pageSize});
17099             
17100         }
17101         
17102         if (this.pageTb && this.allowBlank && !this.disableClear) {
17103             var _this = this;
17104             this.pageTb.add(new Roo.Toolbar.Fill(), {
17105                 cls: 'x-btn-icon x-btn-clear',
17106                 text: '&#160;',
17107                 handler: function()
17108                 {
17109                     _this.collapse();
17110                     _this.clearValue();
17111                     _this.onSelect(false, -1);
17112                 }
17113             });
17114         }
17115         if (this.footer) {
17116             this.assetHeight += this.footer.getHeight();
17117         }
17118         */
17119             
17120         if(!this.tpl){
17121             this.tpl = Roo.bootstrap.version == 4 ?
17122                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17123                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17124         }
17125
17126         this.view = new Roo.View(this.list, this.tpl, {
17127             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17128         });
17129         //this.view.wrapEl.setDisplayed(false);
17130         this.view.on('click', this.onViewClick, this);
17131         
17132         
17133         this.store.on('beforeload', this.onBeforeLoad, this);
17134         this.store.on('load', this.onLoad, this);
17135         this.store.on('loadexception', this.onLoadException, this);
17136         /*
17137         if(this.resizable){
17138             this.resizer = new Roo.Resizable(this.list,  {
17139                pinned:true, handles:'se'
17140             });
17141             this.resizer.on('resize', function(r, w, h){
17142                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17143                 this.listWidth = w;
17144                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17145                 this.restrictHeight();
17146             }, this);
17147             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17148         }
17149         */
17150         if(!this.editable){
17151             this.editable = true;
17152             this.setEditable(false);
17153         }
17154         
17155         /*
17156         
17157         if (typeof(this.events.add.listeners) != 'undefined') {
17158             
17159             this.addicon = this.wrap.createChild(
17160                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17161        
17162             this.addicon.on('click', function(e) {
17163                 this.fireEvent('add', this);
17164             }, this);
17165         }
17166         if (typeof(this.events.edit.listeners) != 'undefined') {
17167             
17168             this.editicon = this.wrap.createChild(
17169                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17170             if (this.addicon) {
17171                 this.editicon.setStyle('margin-left', '40px');
17172             }
17173             this.editicon.on('click', function(e) {
17174                 
17175                 // we fire even  if inothing is selected..
17176                 this.fireEvent('edit', this, this.lastData );
17177                 
17178             }, this);
17179         }
17180         */
17181         
17182         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17183             "up" : function(e){
17184                 this.inKeyMode = true;
17185                 this.selectPrev();
17186             },
17187
17188             "down" : function(e){
17189                 if(!this.isExpanded()){
17190                     this.onTriggerClick();
17191                 }else{
17192                     this.inKeyMode = true;
17193                     this.selectNext();
17194                 }
17195             },
17196
17197             "enter" : function(e){
17198 //                this.onViewClick();
17199                 //return true;
17200                 this.collapse();
17201                 
17202                 if(this.fireEvent("specialkey", this, e)){
17203                     this.onViewClick(false);
17204                 }
17205                 
17206                 return true;
17207             },
17208
17209             "esc" : function(e){
17210                 this.collapse();
17211             },
17212
17213             "tab" : function(e){
17214                 this.collapse();
17215                 
17216                 if(this.fireEvent("specialkey", this, e)){
17217                     this.onViewClick(false);
17218                 }
17219                 
17220                 return true;
17221             },
17222
17223             scope : this,
17224
17225             doRelay : function(foo, bar, hname){
17226                 if(hname == 'down' || this.scope.isExpanded()){
17227                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17228                 }
17229                 return true;
17230             },
17231
17232             forceKeyDown: true
17233         });
17234         
17235         
17236         this.queryDelay = Math.max(this.queryDelay || 10,
17237                 this.mode == 'local' ? 10 : 250);
17238         
17239         
17240         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17241         
17242         if(this.typeAhead){
17243             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17244         }
17245         if(this.editable !== false){
17246             this.inputEl().on("keyup", this.onKeyUp, this);
17247         }
17248         if(this.forceSelection){
17249             this.inputEl().on('blur', this.doForce, this);
17250         }
17251         
17252         if(this.multiple){
17253             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17254             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17255         }
17256     },
17257     
17258     initTickableEvents: function()
17259     {   
17260         this.createList();
17261         
17262         if(this.hiddenName){
17263             
17264             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17265             
17266             this.hiddenField.dom.value =
17267                 this.hiddenValue !== undefined ? this.hiddenValue :
17268                 this.value !== undefined ? this.value : '';
17269
17270             // prevent input submission
17271             this.el.dom.removeAttribute('name');
17272             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17273              
17274              
17275         }
17276         
17277 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17278         
17279         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17280         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17281         if(this.triggerList){
17282             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17283         }
17284          
17285         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17286         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17287         
17288         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17289         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17290         
17291         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17292         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17293         
17294         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17295         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17296         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17297         
17298         this.okBtn.hide();
17299         this.cancelBtn.hide();
17300         
17301         var _this = this;
17302         
17303         (function(){
17304             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17305             _this.list.setWidth(lw);
17306         }).defer(100);
17307         
17308         this.list.on('mouseover', this.onViewOver, this);
17309         this.list.on('mousemove', this.onViewMove, this);
17310         
17311         this.list.on('scroll', this.onViewScroll, this);
17312         
17313         if(!this.tpl){
17314             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17315                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17316         }
17317
17318         this.view = new Roo.View(this.list, this.tpl, {
17319             singleSelect:true,
17320             tickable:true,
17321             parent:this,
17322             store: this.store,
17323             selectedClass: this.selectedClass
17324         });
17325         
17326         //this.view.wrapEl.setDisplayed(false);
17327         this.view.on('click', this.onViewClick, this);
17328         
17329         
17330         
17331         this.store.on('beforeload', this.onBeforeLoad, this);
17332         this.store.on('load', this.onLoad, this);
17333         this.store.on('loadexception', this.onLoadException, this);
17334         
17335         if(this.editable){
17336             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17337                 "up" : function(e){
17338                     this.inKeyMode = true;
17339                     this.selectPrev();
17340                 },
17341
17342                 "down" : function(e){
17343                     this.inKeyMode = true;
17344                     this.selectNext();
17345                 },
17346
17347                 "enter" : function(e){
17348                     if(this.fireEvent("specialkey", this, e)){
17349                         this.onViewClick(false);
17350                     }
17351                     
17352                     return true;
17353                 },
17354
17355                 "esc" : function(e){
17356                     this.onTickableFooterButtonClick(e, false, false);
17357                 },
17358
17359                 "tab" : function(e){
17360                     this.fireEvent("specialkey", this, e);
17361                     
17362                     this.onTickableFooterButtonClick(e, false, false);
17363                     
17364                     return true;
17365                 },
17366
17367                 scope : this,
17368
17369                 doRelay : function(e, fn, key){
17370                     if(this.scope.isExpanded()){
17371                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17372                     }
17373                     return true;
17374                 },
17375
17376                 forceKeyDown: true
17377             });
17378         }
17379         
17380         this.queryDelay = Math.max(this.queryDelay || 10,
17381                 this.mode == 'local' ? 10 : 250);
17382         
17383         
17384         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17385         
17386         if(this.typeAhead){
17387             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17388         }
17389         
17390         if(this.editable !== false){
17391             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17392         }
17393         
17394         this.indicator = this.indicatorEl();
17395         
17396         if(this.indicator){
17397             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17398             this.indicator.hide();
17399         }
17400         
17401     },
17402
17403     onDestroy : function(){
17404         if(this.view){
17405             this.view.setStore(null);
17406             this.view.el.removeAllListeners();
17407             this.view.el.remove();
17408             this.view.purgeListeners();
17409         }
17410         if(this.list){
17411             this.list.dom.innerHTML  = '';
17412         }
17413         
17414         if(this.store){
17415             this.store.un('beforeload', this.onBeforeLoad, this);
17416             this.store.un('load', this.onLoad, this);
17417             this.store.un('loadexception', this.onLoadException, this);
17418         }
17419         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17420     },
17421
17422     // private
17423     fireKey : function(e){
17424         if(e.isNavKeyPress() && !this.list.isVisible()){
17425             this.fireEvent("specialkey", this, e);
17426         }
17427     },
17428
17429     // private
17430     onResize: function(w, h)
17431     {
17432         
17433         
17434 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17435 //        
17436 //        if(typeof w != 'number'){
17437 //            // we do not handle it!?!?
17438 //            return;
17439 //        }
17440 //        var tw = this.trigger.getWidth();
17441 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17442 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17443 //        var x = w - tw;
17444 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17445 //            
17446 //        //this.trigger.setStyle('left', x+'px');
17447 //        
17448 //        if(this.list && this.listWidth === undefined){
17449 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17450 //            this.list.setWidth(lw);
17451 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17452 //        }
17453         
17454     
17455         
17456     },
17457
17458     /**
17459      * Allow or prevent the user from directly editing the field text.  If false is passed,
17460      * the user will only be able to select from the items defined in the dropdown list.  This method
17461      * is the runtime equivalent of setting the 'editable' config option at config time.
17462      * @param {Boolean} value True to allow the user to directly edit the field text
17463      */
17464     setEditable : function(value){
17465         if(value == this.editable){
17466             return;
17467         }
17468         this.editable = value;
17469         if(!value){
17470             this.inputEl().dom.setAttribute('readOnly', true);
17471             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17472             this.inputEl().addClass('x-combo-noedit');
17473         }else{
17474             this.inputEl().dom.removeAttribute('readOnly');
17475             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17476             this.inputEl().removeClass('x-combo-noedit');
17477         }
17478     },
17479
17480     // private
17481     
17482     onBeforeLoad : function(combo,opts){
17483         if(!this.hasFocus){
17484             return;
17485         }
17486          if (!opts.add) {
17487             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17488          }
17489         this.restrictHeight();
17490         this.selectedIndex = -1;
17491     },
17492
17493     // private
17494     onLoad : function(){
17495         
17496         this.hasQuery = false;
17497         
17498         if(!this.hasFocus){
17499             return;
17500         }
17501         
17502         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17503             this.loading.hide();
17504         }
17505         
17506         if(this.store.getCount() > 0){
17507             
17508             this.expand();
17509             this.restrictHeight();
17510             if(this.lastQuery == this.allQuery){
17511                 if(this.editable && !this.tickable){
17512                     this.inputEl().dom.select();
17513                 }
17514                 
17515                 if(
17516                     !this.selectByValue(this.value, true) &&
17517                     this.autoFocus && 
17518                     (
17519                         !this.store.lastOptions ||
17520                         typeof(this.store.lastOptions.add) == 'undefined' || 
17521                         this.store.lastOptions.add != true
17522                     )
17523                 ){
17524                     this.select(0, true);
17525                 }
17526             }else{
17527                 if(this.autoFocus){
17528                     this.selectNext();
17529                 }
17530                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17531                     this.taTask.delay(this.typeAheadDelay);
17532                 }
17533             }
17534         }else{
17535             this.onEmptyResults();
17536         }
17537         
17538         //this.el.focus();
17539     },
17540     // private
17541     onLoadException : function()
17542     {
17543         this.hasQuery = false;
17544         
17545         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17546             this.loading.hide();
17547         }
17548         
17549         if(this.tickable && this.editable){
17550             return;
17551         }
17552         
17553         this.collapse();
17554         // only causes errors at present
17555         //Roo.log(this.store.reader.jsonData);
17556         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17557             // fixme
17558             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17559         //}
17560         
17561         
17562     },
17563     // private
17564     onTypeAhead : function(){
17565         if(this.store.getCount() > 0){
17566             var r = this.store.getAt(0);
17567             var newValue = r.data[this.displayField];
17568             var len = newValue.length;
17569             var selStart = this.getRawValue().length;
17570             
17571             if(selStart != len){
17572                 this.setRawValue(newValue);
17573                 this.selectText(selStart, newValue.length);
17574             }
17575         }
17576     },
17577
17578     // private
17579     onSelect : function(record, index){
17580         
17581         if(this.fireEvent('beforeselect', this, record, index) !== false){
17582         
17583             this.setFromData(index > -1 ? record.data : false);
17584             
17585             this.collapse();
17586             this.fireEvent('select', this, record, index);
17587         }
17588     },
17589
17590     /**
17591      * Returns the currently selected field value or empty string if no value is set.
17592      * @return {String} value The selected value
17593      */
17594     getValue : function()
17595     {
17596         if(Roo.isIOS && this.useNativeIOS){
17597             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17598         }
17599         
17600         if(this.multiple){
17601             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17602         }
17603         
17604         if(this.valueField){
17605             return typeof this.value != 'undefined' ? this.value : '';
17606         }else{
17607             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17608         }
17609     },
17610     
17611     getRawValue : function()
17612     {
17613         if(Roo.isIOS && this.useNativeIOS){
17614             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17615         }
17616         
17617         var v = this.inputEl().getValue();
17618         
17619         return v;
17620     },
17621
17622     /**
17623      * Clears any text/value currently set in the field
17624      */
17625     clearValue : function(){
17626         
17627         if(this.hiddenField){
17628             this.hiddenField.dom.value = '';
17629         }
17630         this.value = '';
17631         this.setRawValue('');
17632         this.lastSelectionText = '';
17633         this.lastData = false;
17634         
17635         var close = this.closeTriggerEl();
17636         
17637         if(close){
17638             close.hide();
17639         }
17640         
17641         this.validate();
17642         
17643     },
17644
17645     /**
17646      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17647      * will be displayed in the field.  If the value does not match the data value of an existing item,
17648      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17649      * Otherwise the field will be blank (although the value will still be set).
17650      * @param {String} value The value to match
17651      */
17652     setValue : function(v)
17653     {
17654         if(Roo.isIOS && this.useNativeIOS){
17655             this.setIOSValue(v);
17656             return;
17657         }
17658         
17659         if(this.multiple){
17660             this.syncValue();
17661             return;
17662         }
17663         
17664         var text = v;
17665         if(this.valueField){
17666             var r = this.findRecord(this.valueField, v);
17667             if(r){
17668                 text = r.data[this.displayField];
17669             }else if(this.valueNotFoundText !== undefined){
17670                 text = this.valueNotFoundText;
17671             }
17672         }
17673         this.lastSelectionText = text;
17674         if(this.hiddenField){
17675             this.hiddenField.dom.value = v;
17676         }
17677         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17678         this.value = v;
17679         
17680         var close = this.closeTriggerEl();
17681         
17682         if(close){
17683             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17684         }
17685         
17686         this.validate();
17687     },
17688     /**
17689      * @property {Object} the last set data for the element
17690      */
17691     
17692     lastData : false,
17693     /**
17694      * Sets the value of the field based on a object which is related to the record format for the store.
17695      * @param {Object} value the value to set as. or false on reset?
17696      */
17697     setFromData : function(o){
17698         
17699         if(this.multiple){
17700             this.addItem(o);
17701             return;
17702         }
17703             
17704         var dv = ''; // display value
17705         var vv = ''; // value value..
17706         this.lastData = o;
17707         if (this.displayField) {
17708             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17709         } else {
17710             // this is an error condition!!!
17711             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17712         }
17713         
17714         if(this.valueField){
17715             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17716         }
17717         
17718         var close = this.closeTriggerEl();
17719         
17720         if(close){
17721             if(dv.length || vv * 1 > 0){
17722                 close.show() ;
17723                 this.blockFocus=true;
17724             } else {
17725                 close.hide();
17726             }             
17727         }
17728         
17729         if(this.hiddenField){
17730             this.hiddenField.dom.value = vv;
17731             
17732             this.lastSelectionText = dv;
17733             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17734             this.value = vv;
17735             return;
17736         }
17737         // no hidden field.. - we store the value in 'value', but still display
17738         // display field!!!!
17739         this.lastSelectionText = dv;
17740         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17741         this.value = vv;
17742         
17743         
17744         
17745     },
17746     // private
17747     reset : function(){
17748         // overridden so that last data is reset..
17749         
17750         if(this.multiple){
17751             this.clearItem();
17752             return;
17753         }
17754         
17755         this.setValue(this.originalValue);
17756         //this.clearInvalid();
17757         this.lastData = false;
17758         if (this.view) {
17759             this.view.clearSelections();
17760         }
17761         
17762         this.validate();
17763     },
17764     // private
17765     findRecord : function(prop, value){
17766         var record;
17767         if(this.store.getCount() > 0){
17768             this.store.each(function(r){
17769                 if(r.data[prop] == value){
17770                     record = r;
17771                     return false;
17772                 }
17773                 return true;
17774             });
17775         }
17776         return record;
17777     },
17778     
17779     getName: function()
17780     {
17781         // returns hidden if it's set..
17782         if (!this.rendered) {return ''};
17783         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17784         
17785     },
17786     // private
17787     onViewMove : function(e, t){
17788         this.inKeyMode = false;
17789     },
17790
17791     // private
17792     onViewOver : function(e, t){
17793         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17794             return;
17795         }
17796         var item = this.view.findItemFromChild(t);
17797         
17798         if(item){
17799             var index = this.view.indexOf(item);
17800             this.select(index, false);
17801         }
17802     },
17803
17804     // private
17805     onViewClick : function(view, doFocus, el, e)
17806     {
17807         var index = this.view.getSelectedIndexes()[0];
17808         
17809         var r = this.store.getAt(index);
17810         
17811         if(this.tickable){
17812             
17813             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17814                 return;
17815             }
17816             
17817             var rm = false;
17818             var _this = this;
17819             
17820             Roo.each(this.tickItems, function(v,k){
17821                 
17822                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17823                     Roo.log(v);
17824                     _this.tickItems.splice(k, 1);
17825                     
17826                     if(typeof(e) == 'undefined' && view == false){
17827                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17828                     }
17829                     
17830                     rm = true;
17831                     return;
17832                 }
17833             });
17834             
17835             if(rm){
17836                 return;
17837             }
17838             
17839             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17840                 this.tickItems.push(r.data);
17841             }
17842             
17843             if(typeof(e) == 'undefined' && view == false){
17844                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17845             }
17846                     
17847             return;
17848         }
17849         
17850         if(r){
17851             this.onSelect(r, index);
17852         }
17853         if(doFocus !== false && !this.blockFocus){
17854             this.inputEl().focus();
17855         }
17856     },
17857
17858     // private
17859     restrictHeight : function(){
17860         //this.innerList.dom.style.height = '';
17861         //var inner = this.innerList.dom;
17862         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17863         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17864         //this.list.beginUpdate();
17865         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17866         this.list.alignTo(this.inputEl(), this.listAlign);
17867         this.list.alignTo(this.inputEl(), this.listAlign);
17868         //this.list.endUpdate();
17869     },
17870
17871     // private
17872     onEmptyResults : function(){
17873         
17874         if(this.tickable && this.editable){
17875             this.hasFocus = false;
17876             this.restrictHeight();
17877             return;
17878         }
17879         
17880         this.collapse();
17881     },
17882
17883     /**
17884      * Returns true if the dropdown list is expanded, else false.
17885      */
17886     isExpanded : function(){
17887         return this.list.isVisible();
17888     },
17889
17890     /**
17891      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17892      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17893      * @param {String} value The data value of the item to select
17894      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17895      * selected item if it is not currently in view (defaults to true)
17896      * @return {Boolean} True if the value matched an item in the list, else false
17897      */
17898     selectByValue : function(v, scrollIntoView){
17899         if(v !== undefined && v !== null){
17900             var r = this.findRecord(this.valueField || this.displayField, v);
17901             if(r){
17902                 this.select(this.store.indexOf(r), scrollIntoView);
17903                 return true;
17904             }
17905         }
17906         return false;
17907     },
17908
17909     /**
17910      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17911      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17912      * @param {Number} index The zero-based index of the list item to select
17913      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17914      * selected item if it is not currently in view (defaults to true)
17915      */
17916     select : function(index, scrollIntoView){
17917         this.selectedIndex = index;
17918         this.view.select(index);
17919         if(scrollIntoView !== false){
17920             var el = this.view.getNode(index);
17921             /*
17922              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17923              */
17924             if(el){
17925                 this.list.scrollChildIntoView(el, false);
17926             }
17927         }
17928     },
17929
17930     // private
17931     selectNext : function(){
17932         var ct = this.store.getCount();
17933         if(ct > 0){
17934             if(this.selectedIndex == -1){
17935                 this.select(0);
17936             }else if(this.selectedIndex < ct-1){
17937                 this.select(this.selectedIndex+1);
17938             }
17939         }
17940     },
17941
17942     // private
17943     selectPrev : function(){
17944         var ct = this.store.getCount();
17945         if(ct > 0){
17946             if(this.selectedIndex == -1){
17947                 this.select(0);
17948             }else if(this.selectedIndex != 0){
17949                 this.select(this.selectedIndex-1);
17950             }
17951         }
17952     },
17953
17954     // private
17955     onKeyUp : function(e){
17956         if(this.editable !== false && !e.isSpecialKey()){
17957             this.lastKey = e.getKey();
17958             this.dqTask.delay(this.queryDelay);
17959         }
17960     },
17961
17962     // private
17963     validateBlur : function(){
17964         return !this.list || !this.list.isVisible();   
17965     },
17966
17967     // private
17968     initQuery : function(){
17969         
17970         var v = this.getRawValue();
17971         
17972         if(this.tickable && this.editable){
17973             v = this.tickableInputEl().getValue();
17974         }
17975         
17976         this.doQuery(v);
17977     },
17978
17979     // private
17980     doForce : function(){
17981         if(this.inputEl().dom.value.length > 0){
17982             this.inputEl().dom.value =
17983                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17984              
17985         }
17986     },
17987
17988     /**
17989      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17990      * query allowing the query action to be canceled if needed.
17991      * @param {String} query The SQL query to execute
17992      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17993      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17994      * saved in the current store (defaults to false)
17995      */
17996     doQuery : function(q, forceAll){
17997         
17998         if(q === undefined || q === null){
17999             q = '';
18000         }
18001         var qe = {
18002             query: q,
18003             forceAll: forceAll,
18004             combo: this,
18005             cancel:false
18006         };
18007         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18008             return false;
18009         }
18010         q = qe.query;
18011         
18012         forceAll = qe.forceAll;
18013         if(forceAll === true || (q.length >= this.minChars)){
18014             
18015             this.hasQuery = true;
18016             
18017             if(this.lastQuery != q || this.alwaysQuery){
18018                 this.lastQuery = q;
18019                 if(this.mode == 'local'){
18020                     this.selectedIndex = -1;
18021                     if(forceAll){
18022                         this.store.clearFilter();
18023                     }else{
18024                         
18025                         if(this.specialFilter){
18026                             this.fireEvent('specialfilter', this);
18027                             this.onLoad();
18028                             return;
18029                         }
18030                         
18031                         this.store.filter(this.displayField, q);
18032                     }
18033                     
18034                     this.store.fireEvent("datachanged", this.store);
18035                     
18036                     this.onLoad();
18037                     
18038                     
18039                 }else{
18040                     
18041                     this.store.baseParams[this.queryParam] = q;
18042                     
18043                     var options = {params : this.getParams(q)};
18044                     
18045                     if(this.loadNext){
18046                         options.add = true;
18047                         options.params.start = this.page * this.pageSize;
18048                     }
18049                     
18050                     this.store.load(options);
18051                     
18052                     /*
18053                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18054                      *  we should expand the list on onLoad
18055                      *  so command out it
18056                      */
18057 //                    this.expand();
18058                 }
18059             }else{
18060                 this.selectedIndex = -1;
18061                 this.onLoad();   
18062             }
18063         }
18064         
18065         this.loadNext = false;
18066     },
18067     
18068     // private
18069     getParams : function(q){
18070         var p = {};
18071         //p[this.queryParam] = q;
18072         
18073         if(this.pageSize){
18074             p.start = 0;
18075             p.limit = this.pageSize;
18076         }
18077         return p;
18078     },
18079
18080     /**
18081      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18082      */
18083     collapse : function(){
18084         if(!this.isExpanded()){
18085             return;
18086         }
18087         
18088         this.list.hide();
18089         
18090         this.hasFocus = false;
18091         
18092         if(this.tickable){
18093             this.okBtn.hide();
18094             this.cancelBtn.hide();
18095             this.trigger.show();
18096             
18097             if(this.editable){
18098                 this.tickableInputEl().dom.value = '';
18099                 this.tickableInputEl().blur();
18100             }
18101             
18102         }
18103         
18104         Roo.get(document).un('mousedown', this.collapseIf, this);
18105         Roo.get(document).un('mousewheel', this.collapseIf, this);
18106         if (!this.editable) {
18107             Roo.get(document).un('keydown', this.listKeyPress, this);
18108         }
18109         this.fireEvent('collapse', this);
18110         
18111         this.validate();
18112     },
18113
18114     // private
18115     collapseIf : function(e){
18116         var in_combo  = e.within(this.el);
18117         var in_list =  e.within(this.list);
18118         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18119         
18120         if (in_combo || in_list || is_list) {
18121             //e.stopPropagation();
18122             return;
18123         }
18124         
18125         if(this.tickable){
18126             this.onTickableFooterButtonClick(e, false, false);
18127         }
18128
18129         this.collapse();
18130         
18131     },
18132
18133     /**
18134      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18135      */
18136     expand : function(){
18137        
18138         if(this.isExpanded() || !this.hasFocus){
18139             return;
18140         }
18141         
18142         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18143         this.list.setWidth(lw);
18144         
18145         Roo.log('expand');
18146         
18147         this.list.show();
18148         
18149         this.restrictHeight();
18150         
18151         if(this.tickable){
18152             
18153             this.tickItems = Roo.apply([], this.item);
18154             
18155             this.okBtn.show();
18156             this.cancelBtn.show();
18157             this.trigger.hide();
18158             
18159             if(this.editable){
18160                 this.tickableInputEl().focus();
18161             }
18162             
18163         }
18164         
18165         Roo.get(document).on('mousedown', this.collapseIf, this);
18166         Roo.get(document).on('mousewheel', this.collapseIf, this);
18167         if (!this.editable) {
18168             Roo.get(document).on('keydown', this.listKeyPress, this);
18169         }
18170         
18171         this.fireEvent('expand', this);
18172     },
18173
18174     // private
18175     // Implements the default empty TriggerField.onTriggerClick function
18176     onTriggerClick : function(e)
18177     {
18178         Roo.log('trigger click');
18179         
18180         if(this.disabled || !this.triggerList){
18181             return;
18182         }
18183         
18184         this.page = 0;
18185         this.loadNext = false;
18186         
18187         if(this.isExpanded()){
18188             this.collapse();
18189             if (!this.blockFocus) {
18190                 this.inputEl().focus();
18191             }
18192             
18193         }else {
18194             this.hasFocus = true;
18195             if(this.triggerAction == 'all') {
18196                 this.doQuery(this.allQuery, true);
18197             } else {
18198                 this.doQuery(this.getRawValue());
18199             }
18200             if (!this.blockFocus) {
18201                 this.inputEl().focus();
18202             }
18203         }
18204     },
18205     
18206     onTickableTriggerClick : function(e)
18207     {
18208         if(this.disabled){
18209             return;
18210         }
18211         
18212         this.page = 0;
18213         this.loadNext = false;
18214         this.hasFocus = true;
18215         
18216         if(this.triggerAction == 'all') {
18217             this.doQuery(this.allQuery, true);
18218         } else {
18219             this.doQuery(this.getRawValue());
18220         }
18221     },
18222     
18223     onSearchFieldClick : function(e)
18224     {
18225         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18226             this.onTickableFooterButtonClick(e, false, false);
18227             return;
18228         }
18229         
18230         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18231             return;
18232         }
18233         
18234         this.page = 0;
18235         this.loadNext = false;
18236         this.hasFocus = true;
18237         
18238         if(this.triggerAction == 'all') {
18239             this.doQuery(this.allQuery, true);
18240         } else {
18241             this.doQuery(this.getRawValue());
18242         }
18243     },
18244     
18245     listKeyPress : function(e)
18246     {
18247         //Roo.log('listkeypress');
18248         // scroll to first matching element based on key pres..
18249         if (e.isSpecialKey()) {
18250             return false;
18251         }
18252         var k = String.fromCharCode(e.getKey()).toUpperCase();
18253         //Roo.log(k);
18254         var match  = false;
18255         var csel = this.view.getSelectedNodes();
18256         var cselitem = false;
18257         if (csel.length) {
18258             var ix = this.view.indexOf(csel[0]);
18259             cselitem  = this.store.getAt(ix);
18260             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18261                 cselitem = false;
18262             }
18263             
18264         }
18265         
18266         this.store.each(function(v) { 
18267             if (cselitem) {
18268                 // start at existing selection.
18269                 if (cselitem.id == v.id) {
18270                     cselitem = false;
18271                 }
18272                 return true;
18273             }
18274                 
18275             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18276                 match = this.store.indexOf(v);
18277                 return false;
18278             }
18279             return true;
18280         }, this);
18281         
18282         if (match === false) {
18283             return true; // no more action?
18284         }
18285         // scroll to?
18286         this.view.select(match);
18287         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18288         sn.scrollIntoView(sn.dom.parentNode, false);
18289     },
18290     
18291     onViewScroll : function(e, t){
18292         
18293         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){
18294             return;
18295         }
18296         
18297         this.hasQuery = true;
18298         
18299         this.loading = this.list.select('.loading', true).first();
18300         
18301         if(this.loading === null){
18302             this.list.createChild({
18303                 tag: 'div',
18304                 cls: 'loading roo-select2-more-results roo-select2-active',
18305                 html: 'Loading more results...'
18306             });
18307             
18308             this.loading = this.list.select('.loading', true).first();
18309             
18310             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18311             
18312             this.loading.hide();
18313         }
18314         
18315         this.loading.show();
18316         
18317         var _combo = this;
18318         
18319         this.page++;
18320         this.loadNext = true;
18321         
18322         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18323         
18324         return;
18325     },
18326     
18327     addItem : function(o)
18328     {   
18329         var dv = ''; // display value
18330         
18331         if (this.displayField) {
18332             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18333         } else {
18334             // this is an error condition!!!
18335             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18336         }
18337         
18338         if(!dv.length){
18339             return;
18340         }
18341         
18342         var choice = this.choices.createChild({
18343             tag: 'li',
18344             cls: 'roo-select2-search-choice',
18345             cn: [
18346                 {
18347                     tag: 'div',
18348                     html: dv
18349                 },
18350                 {
18351                     tag: 'a',
18352                     href: '#',
18353                     cls: 'roo-select2-search-choice-close fa fa-times',
18354                     tabindex: '-1'
18355                 }
18356             ]
18357             
18358         }, this.searchField);
18359         
18360         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18361         
18362         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18363         
18364         this.item.push(o);
18365         
18366         this.lastData = o;
18367         
18368         this.syncValue();
18369         
18370         this.inputEl().dom.value = '';
18371         
18372         this.validate();
18373     },
18374     
18375     onRemoveItem : function(e, _self, o)
18376     {
18377         e.preventDefault();
18378         
18379         this.lastItem = Roo.apply([], this.item);
18380         
18381         var index = this.item.indexOf(o.data) * 1;
18382         
18383         if( index < 0){
18384             Roo.log('not this item?!');
18385             return;
18386         }
18387         
18388         this.item.splice(index, 1);
18389         o.item.remove();
18390         
18391         this.syncValue();
18392         
18393         this.fireEvent('remove', this, e);
18394         
18395         this.validate();
18396         
18397     },
18398     
18399     syncValue : function()
18400     {
18401         if(!this.item.length){
18402             this.clearValue();
18403             return;
18404         }
18405             
18406         var value = [];
18407         var _this = this;
18408         Roo.each(this.item, function(i){
18409             if(_this.valueField){
18410                 value.push(i[_this.valueField]);
18411                 return;
18412             }
18413
18414             value.push(i);
18415         });
18416
18417         this.value = value.join(',');
18418
18419         if(this.hiddenField){
18420             this.hiddenField.dom.value = this.value;
18421         }
18422         
18423         this.store.fireEvent("datachanged", this.store);
18424         
18425         this.validate();
18426     },
18427     
18428     clearItem : function()
18429     {
18430         if(!this.multiple){
18431             return;
18432         }
18433         
18434         this.item = [];
18435         
18436         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18437            c.remove();
18438         });
18439         
18440         this.syncValue();
18441         
18442         this.validate();
18443         
18444         if(this.tickable && !Roo.isTouch){
18445             this.view.refresh();
18446         }
18447     },
18448     
18449     inputEl: function ()
18450     {
18451         if(Roo.isIOS && this.useNativeIOS){
18452             return this.el.select('select.roo-ios-select', true).first();
18453         }
18454         
18455         if(Roo.isTouch && this.mobileTouchView){
18456             return this.el.select('input.form-control',true).first();
18457         }
18458         
18459         if(this.tickable){
18460             return this.searchField;
18461         }
18462         
18463         return this.el.select('input.form-control',true).first();
18464     },
18465     
18466     onTickableFooterButtonClick : function(e, btn, el)
18467     {
18468         e.preventDefault();
18469         
18470         this.lastItem = Roo.apply([], this.item);
18471         
18472         if(btn && btn.name == 'cancel'){
18473             this.tickItems = Roo.apply([], this.item);
18474             this.collapse();
18475             return;
18476         }
18477         
18478         this.clearItem();
18479         
18480         var _this = this;
18481         
18482         Roo.each(this.tickItems, function(o){
18483             _this.addItem(o);
18484         });
18485         
18486         this.collapse();
18487         
18488     },
18489     
18490     validate : function()
18491     {
18492         if(this.getVisibilityEl().hasClass('hidden')){
18493             return true;
18494         }
18495         
18496         var v = this.getRawValue();
18497         
18498         if(this.multiple){
18499             v = this.getValue();
18500         }
18501         
18502         if(this.disabled || this.allowBlank || v.length){
18503             this.markValid();
18504             return true;
18505         }
18506         
18507         this.markInvalid();
18508         return false;
18509     },
18510     
18511     tickableInputEl : function()
18512     {
18513         if(!this.tickable || !this.editable){
18514             return this.inputEl();
18515         }
18516         
18517         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18518     },
18519     
18520     
18521     getAutoCreateTouchView : function()
18522     {
18523         var id = Roo.id();
18524         
18525         var cfg = {
18526             cls: 'form-group' //input-group
18527         };
18528         
18529         var input =  {
18530             tag: 'input',
18531             id : id,
18532             type : this.inputType,
18533             cls : 'form-control x-combo-noedit',
18534             autocomplete: 'new-password',
18535             placeholder : this.placeholder || '',
18536             readonly : true
18537         };
18538         
18539         if (this.name) {
18540             input.name = this.name;
18541         }
18542         
18543         if (this.size) {
18544             input.cls += ' input-' + this.size;
18545         }
18546         
18547         if (this.disabled) {
18548             input.disabled = true;
18549         }
18550         
18551         var inputblock = {
18552             cls : 'roo-combobox-wrap',
18553             cn : [
18554                 input
18555             ]
18556         };
18557         
18558         if(this.before){
18559             inputblock.cls += ' input-group';
18560             
18561             inputblock.cn.unshift({
18562                 tag :'span',
18563                 cls : 'input-group-addon input-group-prepend input-group-text',
18564                 html : this.before
18565             });
18566         }
18567         
18568         if(this.removable && !this.multiple){
18569             inputblock.cls += ' roo-removable';
18570             
18571             inputblock.cn.push({
18572                 tag: 'button',
18573                 html : 'x',
18574                 cls : 'roo-combo-removable-btn close'
18575             });
18576         }
18577
18578         if(this.hasFeedback && !this.allowBlank){
18579             
18580             inputblock.cls += ' has-feedback';
18581             
18582             inputblock.cn.push({
18583                 tag: 'span',
18584                 cls: 'glyphicon form-control-feedback'
18585             });
18586             
18587         }
18588         
18589         if (this.after) {
18590             
18591             inputblock.cls += (this.before) ? '' : ' input-group';
18592             
18593             inputblock.cn.push({
18594                 tag :'span',
18595                 cls : 'input-group-addon input-group-append input-group-text',
18596                 html : this.after
18597             });
18598         }
18599
18600         
18601         var ibwrap = inputblock;
18602         
18603         if(this.multiple){
18604             ibwrap = {
18605                 tag: 'ul',
18606                 cls: 'roo-select2-choices',
18607                 cn:[
18608                     {
18609                         tag: 'li',
18610                         cls: 'roo-select2-search-field',
18611                         cn: [
18612
18613                             inputblock
18614                         ]
18615                     }
18616                 ]
18617             };
18618         
18619             
18620         }
18621         
18622         var combobox = {
18623             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18624             cn: [
18625                 {
18626                     tag: 'input',
18627                     type : 'hidden',
18628                     cls: 'form-hidden-field'
18629                 },
18630                 ibwrap
18631             ]
18632         };
18633         
18634         if(!this.multiple && this.showToggleBtn){
18635             
18636             var caret = {
18637                 cls: 'caret'
18638             };
18639             
18640             if (this.caret != false) {
18641                 caret = {
18642                      tag: 'i',
18643                      cls: 'fa fa-' + this.caret
18644                 };
18645                 
18646             }
18647             
18648             combobox.cn.push({
18649                 tag :'span',
18650                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18651                 cn : [
18652                     Roo.bootstrap.version == 3 ? caret : '',
18653                     {
18654                         tag: 'span',
18655                         cls: 'combobox-clear',
18656                         cn  : [
18657                             {
18658                                 tag : 'i',
18659                                 cls: 'icon-remove'
18660                             }
18661                         ]
18662                     }
18663                 ]
18664
18665             })
18666         }
18667         
18668         if(this.multiple){
18669             combobox.cls += ' roo-select2-container-multi';
18670         }
18671         
18672         var required =  this.allowBlank ?  {
18673                     tag : 'i',
18674                     style: 'display: none'
18675                 } : {
18676                    tag : 'i',
18677                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18678                    tooltip : 'This field is required'
18679                 };
18680         
18681         var align = this.labelAlign || this.parentLabelAlign();
18682         
18683         if (align ==='left' && this.fieldLabel.length) {
18684
18685             cfg.cn = [
18686                 required,
18687                 {
18688                     tag: 'label',
18689                     cls : 'control-label col-form-label',
18690                     html : this.fieldLabel
18691
18692                 },
18693                 {
18694                     cls : 'roo-combobox-wrap ', 
18695                     cn: [
18696                         combobox
18697                     ]
18698                 }
18699             ];
18700             
18701             var labelCfg = cfg.cn[1];
18702             var contentCfg = cfg.cn[2];
18703             
18704
18705             if(this.indicatorpos == 'right'){
18706                 cfg.cn = [
18707                     {
18708                         tag: 'label',
18709                         'for' :  id,
18710                         cls : 'control-label col-form-label',
18711                         cn : [
18712                             {
18713                                 tag : 'span',
18714                                 html : this.fieldLabel
18715                             },
18716                             required
18717                         ]
18718                     },
18719                     {
18720                         cls : "roo-combobox-wrap ",
18721                         cn: [
18722                             combobox
18723                         ]
18724                     }
18725
18726                 ];
18727                 
18728                 labelCfg = cfg.cn[0];
18729                 contentCfg = cfg.cn[1];
18730             }
18731             
18732            
18733             
18734             if(this.labelWidth > 12){
18735                 labelCfg.style = "width: " + this.labelWidth + 'px';
18736             }
18737            
18738             if(this.labelWidth < 13 && this.labelmd == 0){
18739                 this.labelmd = this.labelWidth;
18740             }
18741             
18742             if(this.labellg > 0){
18743                 labelCfg.cls += ' col-lg-' + this.labellg;
18744                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18745             }
18746             
18747             if(this.labelmd > 0){
18748                 labelCfg.cls += ' col-md-' + this.labelmd;
18749                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18750             }
18751             
18752             if(this.labelsm > 0){
18753                 labelCfg.cls += ' col-sm-' + this.labelsm;
18754                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18755             }
18756             
18757             if(this.labelxs > 0){
18758                 labelCfg.cls += ' col-xs-' + this.labelxs;
18759                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18760             }
18761                 
18762                 
18763         } else if ( this.fieldLabel.length) {
18764             cfg.cn = [
18765                required,
18766                 {
18767                     tag: 'label',
18768                     cls : 'control-label',
18769                     html : this.fieldLabel
18770
18771                 },
18772                 {
18773                     cls : '', 
18774                     cn: [
18775                         combobox
18776                     ]
18777                 }
18778             ];
18779             
18780             if(this.indicatorpos == 'right'){
18781                 cfg.cn = [
18782                     {
18783                         tag: 'label',
18784                         cls : 'control-label',
18785                         html : this.fieldLabel,
18786                         cn : [
18787                             required
18788                         ]
18789                     },
18790                     {
18791                         cls : '', 
18792                         cn: [
18793                             combobox
18794                         ]
18795                     }
18796                 ];
18797             }
18798         } else {
18799             cfg.cn = combobox;    
18800         }
18801         
18802         
18803         var settings = this;
18804         
18805         ['xs','sm','md','lg'].map(function(size){
18806             if (settings[size]) {
18807                 cfg.cls += ' col-' + size + '-' + settings[size];
18808             }
18809         });
18810         
18811         return cfg;
18812     },
18813     
18814     initTouchView : function()
18815     {
18816         this.renderTouchView();
18817         
18818         this.touchViewEl.on('scroll', function(){
18819             this.el.dom.scrollTop = 0;
18820         }, this);
18821         
18822         this.originalValue = this.getValue();
18823         
18824         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18825         
18826         this.inputEl().on("click", this.showTouchView, this);
18827         if (this.triggerEl) {
18828             this.triggerEl.on("click", this.showTouchView, this);
18829         }
18830         
18831         
18832         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18833         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18834         
18835         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18836         
18837         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18838         this.store.on('load', this.onTouchViewLoad, this);
18839         this.store.on('loadexception', this.onTouchViewLoadException, this);
18840         
18841         if(this.hiddenName){
18842             
18843             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18844             
18845             this.hiddenField.dom.value =
18846                 this.hiddenValue !== undefined ? this.hiddenValue :
18847                 this.value !== undefined ? this.value : '';
18848         
18849             this.el.dom.removeAttribute('name');
18850             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18851         }
18852         
18853         if(this.multiple){
18854             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18855             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18856         }
18857         
18858         if(this.removable && !this.multiple){
18859             var close = this.closeTriggerEl();
18860             if(close){
18861                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18862                 close.on('click', this.removeBtnClick, this, close);
18863             }
18864         }
18865         /*
18866          * fix the bug in Safari iOS8
18867          */
18868         this.inputEl().on("focus", function(e){
18869             document.activeElement.blur();
18870         }, this);
18871         
18872         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18873         
18874         return;
18875         
18876         
18877     },
18878     
18879     renderTouchView : function()
18880     {
18881         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18882         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18883         
18884         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18885         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18886         
18887         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18888         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18889         this.touchViewBodyEl.setStyle('overflow', 'auto');
18890         
18891         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18892         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18893         
18894         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18895         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18896         
18897     },
18898     
18899     showTouchView : function()
18900     {
18901         if(this.disabled){
18902             return;
18903         }
18904         
18905         this.touchViewHeaderEl.hide();
18906
18907         if(this.modalTitle.length){
18908             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18909             this.touchViewHeaderEl.show();
18910         }
18911
18912         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18913         this.touchViewEl.show();
18914
18915         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18916         
18917         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18918         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18919
18920         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18921
18922         if(this.modalTitle.length){
18923             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18924         }
18925         
18926         this.touchViewBodyEl.setHeight(bodyHeight);
18927
18928         if(this.animate){
18929             var _this = this;
18930             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18931         }else{
18932             this.touchViewEl.addClass(['in','show']);
18933         }
18934         
18935         if(this._touchViewMask){
18936             Roo.get(document.body).addClass("x-body-masked");
18937             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18938             this._touchViewMask.setStyle('z-index', 10000);
18939             this._touchViewMask.addClass('show');
18940         }
18941         
18942         this.doTouchViewQuery();
18943         
18944     },
18945     
18946     hideTouchView : function()
18947     {
18948         this.touchViewEl.removeClass(['in','show']);
18949
18950         if(this.animate){
18951             var _this = this;
18952             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18953         }else{
18954             this.touchViewEl.setStyle('display', 'none');
18955         }
18956         
18957         if(this._touchViewMask){
18958             this._touchViewMask.removeClass('show');
18959             Roo.get(document.body).removeClass("x-body-masked");
18960         }
18961     },
18962     
18963     setTouchViewValue : function()
18964     {
18965         if(this.multiple){
18966             this.clearItem();
18967         
18968             var _this = this;
18969
18970             Roo.each(this.tickItems, function(o){
18971                 this.addItem(o);
18972             }, this);
18973         }
18974         
18975         this.hideTouchView();
18976     },
18977     
18978     doTouchViewQuery : function()
18979     {
18980         var qe = {
18981             query: '',
18982             forceAll: true,
18983             combo: this,
18984             cancel:false
18985         };
18986         
18987         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18988             return false;
18989         }
18990         
18991         if(!this.alwaysQuery || this.mode == 'local'){
18992             this.onTouchViewLoad();
18993             return;
18994         }
18995         
18996         this.store.load();
18997     },
18998     
18999     onTouchViewBeforeLoad : function(combo,opts)
19000     {
19001         return;
19002     },
19003
19004     // private
19005     onTouchViewLoad : function()
19006     {
19007         if(this.store.getCount() < 1){
19008             this.onTouchViewEmptyResults();
19009             return;
19010         }
19011         
19012         this.clearTouchView();
19013         
19014         var rawValue = this.getRawValue();
19015         
19016         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19017         
19018         this.tickItems = [];
19019         
19020         this.store.data.each(function(d, rowIndex){
19021             var row = this.touchViewListGroup.createChild(template);
19022             
19023             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19024                 row.addClass(d.data.cls);
19025             }
19026             
19027             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19028                 var cfg = {
19029                     data : d.data,
19030                     html : d.data[this.displayField]
19031                 };
19032                 
19033                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19034                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19035                 }
19036             }
19037             row.removeClass('selected');
19038             if(!this.multiple && this.valueField &&
19039                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19040             {
19041                 // radio buttons..
19042                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19043                 row.addClass('selected');
19044             }
19045             
19046             if(this.multiple && this.valueField &&
19047                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19048             {
19049                 
19050                 // checkboxes...
19051                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19052                 this.tickItems.push(d.data);
19053             }
19054             
19055             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19056             
19057         }, this);
19058         
19059         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19060         
19061         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19062
19063         if(this.modalTitle.length){
19064             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19065         }
19066
19067         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19068         
19069         if(this.mobile_restrict_height && listHeight < bodyHeight){
19070             this.touchViewBodyEl.setHeight(listHeight);
19071         }
19072         
19073         var _this = this;
19074         
19075         if(firstChecked && listHeight > bodyHeight){
19076             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19077         }
19078         
19079     },
19080     
19081     onTouchViewLoadException : function()
19082     {
19083         this.hideTouchView();
19084     },
19085     
19086     onTouchViewEmptyResults : function()
19087     {
19088         this.clearTouchView();
19089         
19090         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19091         
19092         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19093         
19094     },
19095     
19096     clearTouchView : function()
19097     {
19098         this.touchViewListGroup.dom.innerHTML = '';
19099     },
19100     
19101     onTouchViewClick : function(e, el, o)
19102     {
19103         e.preventDefault();
19104         
19105         var row = o.row;
19106         var rowIndex = o.rowIndex;
19107         
19108         var r = this.store.getAt(rowIndex);
19109         
19110         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19111             
19112             if(!this.multiple){
19113                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19114                     c.dom.removeAttribute('checked');
19115                 }, this);
19116
19117                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19118
19119                 this.setFromData(r.data);
19120
19121                 var close = this.closeTriggerEl();
19122
19123                 if(close){
19124                     close.show();
19125                 }
19126
19127                 this.hideTouchView();
19128
19129                 this.fireEvent('select', this, r, rowIndex);
19130
19131                 return;
19132             }
19133
19134             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19135                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19136                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19137                 return;
19138             }
19139
19140             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19141             this.addItem(r.data);
19142             this.tickItems.push(r.data);
19143         }
19144     },
19145     
19146     getAutoCreateNativeIOS : function()
19147     {
19148         var cfg = {
19149             cls: 'form-group' //input-group,
19150         };
19151         
19152         var combobox =  {
19153             tag: 'select',
19154             cls : 'roo-ios-select'
19155         };
19156         
19157         if (this.name) {
19158             combobox.name = this.name;
19159         }
19160         
19161         if (this.disabled) {
19162             combobox.disabled = true;
19163         }
19164         
19165         var settings = this;
19166         
19167         ['xs','sm','md','lg'].map(function(size){
19168             if (settings[size]) {
19169                 cfg.cls += ' col-' + size + '-' + settings[size];
19170             }
19171         });
19172         
19173         cfg.cn = combobox;
19174         
19175         return cfg;
19176         
19177     },
19178     
19179     initIOSView : function()
19180     {
19181         this.store.on('load', this.onIOSViewLoad, this);
19182         
19183         return;
19184     },
19185     
19186     onIOSViewLoad : function()
19187     {
19188         if(this.store.getCount() < 1){
19189             return;
19190         }
19191         
19192         this.clearIOSView();
19193         
19194         if(this.allowBlank) {
19195             
19196             var default_text = '-- SELECT --';
19197             
19198             if(this.placeholder.length){
19199                 default_text = this.placeholder;
19200             }
19201             
19202             if(this.emptyTitle.length){
19203                 default_text += ' - ' + this.emptyTitle + ' -';
19204             }
19205             
19206             var opt = this.inputEl().createChild({
19207                 tag: 'option',
19208                 value : 0,
19209                 html : default_text
19210             });
19211             
19212             var o = {};
19213             o[this.valueField] = 0;
19214             o[this.displayField] = default_text;
19215             
19216             this.ios_options.push({
19217                 data : o,
19218                 el : opt
19219             });
19220             
19221         }
19222         
19223         this.store.data.each(function(d, rowIndex){
19224             
19225             var html = '';
19226             
19227             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19228                 html = d.data[this.displayField];
19229             }
19230             
19231             var value = '';
19232             
19233             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19234                 value = d.data[this.valueField];
19235             }
19236             
19237             var option = {
19238                 tag: 'option',
19239                 value : value,
19240                 html : html
19241             };
19242             
19243             if(this.value == d.data[this.valueField]){
19244                 option['selected'] = true;
19245             }
19246             
19247             var opt = this.inputEl().createChild(option);
19248             
19249             this.ios_options.push({
19250                 data : d.data,
19251                 el : opt
19252             });
19253             
19254         }, this);
19255         
19256         this.inputEl().on('change', function(){
19257            this.fireEvent('select', this);
19258         }, this);
19259         
19260     },
19261     
19262     clearIOSView: function()
19263     {
19264         this.inputEl().dom.innerHTML = '';
19265         
19266         this.ios_options = [];
19267     },
19268     
19269     setIOSValue: function(v)
19270     {
19271         this.value = v;
19272         
19273         if(!this.ios_options){
19274             return;
19275         }
19276         
19277         Roo.each(this.ios_options, function(opts){
19278            
19279            opts.el.dom.removeAttribute('selected');
19280            
19281            if(opts.data[this.valueField] != v){
19282                return;
19283            }
19284            
19285            opts.el.dom.setAttribute('selected', true);
19286            
19287         }, this);
19288     }
19289
19290     /** 
19291     * @cfg {Boolean} grow 
19292     * @hide 
19293     */
19294     /** 
19295     * @cfg {Number} growMin 
19296     * @hide 
19297     */
19298     /** 
19299     * @cfg {Number} growMax 
19300     * @hide 
19301     */
19302     /**
19303      * @hide
19304      * @method autoSize
19305      */
19306 });
19307
19308 Roo.apply(Roo.bootstrap.ComboBox,  {
19309     
19310     header : {
19311         tag: 'div',
19312         cls: 'modal-header',
19313         cn: [
19314             {
19315                 tag: 'h4',
19316                 cls: 'modal-title'
19317             }
19318         ]
19319     },
19320     
19321     body : {
19322         tag: 'div',
19323         cls: 'modal-body',
19324         cn: [
19325             {
19326                 tag: 'ul',
19327                 cls: 'list-group'
19328             }
19329         ]
19330     },
19331     
19332     listItemRadio : {
19333         tag: 'li',
19334         cls: 'list-group-item',
19335         cn: [
19336             {
19337                 tag: 'span',
19338                 cls: 'roo-combobox-list-group-item-value'
19339             },
19340             {
19341                 tag: 'div',
19342                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19343                 cn: [
19344                     {
19345                         tag: 'input',
19346                         type: 'radio'
19347                     },
19348                     {
19349                         tag: 'label'
19350                     }
19351                 ]
19352             }
19353         ]
19354     },
19355     
19356     listItemCheckbox : {
19357         tag: 'li',
19358         cls: 'list-group-item',
19359         cn: [
19360             {
19361                 tag: 'span',
19362                 cls: 'roo-combobox-list-group-item-value'
19363             },
19364             {
19365                 tag: 'div',
19366                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19367                 cn: [
19368                     {
19369                         tag: 'input',
19370                         type: 'checkbox'
19371                     },
19372                     {
19373                         tag: 'label'
19374                     }
19375                 ]
19376             }
19377         ]
19378     },
19379     
19380     emptyResult : {
19381         tag: 'div',
19382         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19383     },
19384     
19385     footer : {
19386         tag: 'div',
19387         cls: 'modal-footer',
19388         cn: [
19389             {
19390                 tag: 'div',
19391                 cls: 'row',
19392                 cn: [
19393                     {
19394                         tag: 'div',
19395                         cls: 'col-xs-6 text-left',
19396                         cn: {
19397                             tag: 'button',
19398                             cls: 'btn btn-danger roo-touch-view-cancel',
19399                             html: 'Cancel'
19400                         }
19401                     },
19402                     {
19403                         tag: 'div',
19404                         cls: 'col-xs-6 text-right',
19405                         cn: {
19406                             tag: 'button',
19407                             cls: 'btn btn-success roo-touch-view-ok',
19408                             html: 'OK'
19409                         }
19410                     }
19411                 ]
19412             }
19413         ]
19414         
19415     }
19416 });
19417
19418 Roo.apply(Roo.bootstrap.ComboBox,  {
19419     
19420     touchViewTemplate : {
19421         tag: 'div',
19422         cls: 'modal fade roo-combobox-touch-view',
19423         cn: [
19424             {
19425                 tag: 'div',
19426                 cls: 'modal-dialog',
19427                 style : 'position:fixed', // we have to fix position....
19428                 cn: [
19429                     {
19430                         tag: 'div',
19431                         cls: 'modal-content',
19432                         cn: [
19433                             Roo.bootstrap.ComboBox.header,
19434                             Roo.bootstrap.ComboBox.body,
19435                             Roo.bootstrap.ComboBox.footer
19436                         ]
19437                     }
19438                 ]
19439             }
19440         ]
19441     }
19442 });/*
19443  * Based on:
19444  * Ext JS Library 1.1.1
19445  * Copyright(c) 2006-2007, Ext JS, LLC.
19446  *
19447  * Originally Released Under LGPL - original licence link has changed is not relivant.
19448  *
19449  * Fork - LGPL
19450  * <script type="text/javascript">
19451  */
19452
19453 /**
19454  * @class Roo.View
19455  * @extends Roo.util.Observable
19456  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19457  * This class also supports single and multi selection modes. <br>
19458  * Create a data model bound view:
19459  <pre><code>
19460  var store = new Roo.data.Store(...);
19461
19462  var view = new Roo.View({
19463     el : "my-element",
19464     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19465  
19466     singleSelect: true,
19467     selectedClass: "ydataview-selected",
19468     store: store
19469  });
19470
19471  // listen for node click?
19472  view.on("click", function(vw, index, node, e){
19473  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19474  });
19475
19476  // load XML data
19477  dataModel.load("foobar.xml");
19478  </code></pre>
19479  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19480  * <br><br>
19481  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19482  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19483  * 
19484  * Note: old style constructor is still suported (container, template, config)
19485  * 
19486  * @constructor
19487  * Create a new View
19488  * @param {Object} config The config object
19489  * 
19490  */
19491 Roo.View = function(config, depreciated_tpl, depreciated_config){
19492     
19493     this.parent = false;
19494     
19495     if (typeof(depreciated_tpl) == 'undefined') {
19496         // new way.. - universal constructor.
19497         Roo.apply(this, config);
19498         this.el  = Roo.get(this.el);
19499     } else {
19500         // old format..
19501         this.el  = Roo.get(config);
19502         this.tpl = depreciated_tpl;
19503         Roo.apply(this, depreciated_config);
19504     }
19505     this.wrapEl  = this.el.wrap().wrap();
19506     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19507     
19508     
19509     if(typeof(this.tpl) == "string"){
19510         this.tpl = new Roo.Template(this.tpl);
19511     } else {
19512         // support xtype ctors..
19513         this.tpl = new Roo.factory(this.tpl, Roo);
19514     }
19515     
19516     
19517     this.tpl.compile();
19518     
19519     /** @private */
19520     this.addEvents({
19521         /**
19522          * @event beforeclick
19523          * Fires before a click is processed. Returns false to cancel the default action.
19524          * @param {Roo.View} this
19525          * @param {Number} index The index of the target node
19526          * @param {HTMLElement} node The target node
19527          * @param {Roo.EventObject} e The raw event object
19528          */
19529             "beforeclick" : true,
19530         /**
19531          * @event click
19532          * Fires when a template node is clicked.
19533          * @param {Roo.View} this
19534          * @param {Number} index The index of the target node
19535          * @param {HTMLElement} node The target node
19536          * @param {Roo.EventObject} e The raw event object
19537          */
19538             "click" : true,
19539         /**
19540          * @event dblclick
19541          * Fires when a template node is double clicked.
19542          * @param {Roo.View} this
19543          * @param {Number} index The index of the target node
19544          * @param {HTMLElement} node The target node
19545          * @param {Roo.EventObject} e The raw event object
19546          */
19547             "dblclick" : true,
19548         /**
19549          * @event contextmenu
19550          * Fires when a template node is right clicked.
19551          * @param {Roo.View} this
19552          * @param {Number} index The index of the target node
19553          * @param {HTMLElement} node The target node
19554          * @param {Roo.EventObject} e The raw event object
19555          */
19556             "contextmenu" : true,
19557         /**
19558          * @event selectionchange
19559          * Fires when the selected nodes change.
19560          * @param {Roo.View} this
19561          * @param {Array} selections Array of the selected nodes
19562          */
19563             "selectionchange" : true,
19564     
19565         /**
19566          * @event beforeselect
19567          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19568          * @param {Roo.View} this
19569          * @param {HTMLElement} node The node to be selected
19570          * @param {Array} selections Array of currently selected nodes
19571          */
19572             "beforeselect" : true,
19573         /**
19574          * @event preparedata
19575          * Fires on every row to render, to allow you to change the data.
19576          * @param {Roo.View} this
19577          * @param {Object} data to be rendered (change this)
19578          */
19579           "preparedata" : true
19580           
19581           
19582         });
19583
19584
19585
19586     this.el.on({
19587         "click": this.onClick,
19588         "dblclick": this.onDblClick,
19589         "contextmenu": this.onContextMenu,
19590         scope:this
19591     });
19592
19593     this.selections = [];
19594     this.nodes = [];
19595     this.cmp = new Roo.CompositeElementLite([]);
19596     if(this.store){
19597         this.store = Roo.factory(this.store, Roo.data);
19598         this.setStore(this.store, true);
19599     }
19600     
19601     if ( this.footer && this.footer.xtype) {
19602            
19603          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19604         
19605         this.footer.dataSource = this.store;
19606         this.footer.container = fctr;
19607         this.footer = Roo.factory(this.footer, Roo);
19608         fctr.insertFirst(this.el);
19609         
19610         // this is a bit insane - as the paging toolbar seems to detach the el..
19611 //        dom.parentNode.parentNode.parentNode
19612          // they get detached?
19613     }
19614     
19615     
19616     Roo.View.superclass.constructor.call(this);
19617     
19618     
19619 };
19620
19621 Roo.extend(Roo.View, Roo.util.Observable, {
19622     
19623      /**
19624      * @cfg {Roo.data.Store} store Data store to load data from.
19625      */
19626     store : false,
19627     
19628     /**
19629      * @cfg {String|Roo.Element} el The container element.
19630      */
19631     el : '',
19632     
19633     /**
19634      * @cfg {String|Roo.Template} tpl The template used by this View 
19635      */
19636     tpl : false,
19637     /**
19638      * @cfg {String} dataName the named area of the template to use as the data area
19639      *                          Works with domtemplates roo-name="name"
19640      */
19641     dataName: false,
19642     /**
19643      * @cfg {String} selectedClass The css class to add to selected nodes
19644      */
19645     selectedClass : "x-view-selected",
19646      /**
19647      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19648      */
19649     emptyText : "",
19650     
19651     /**
19652      * @cfg {String} text to display on mask (default Loading)
19653      */
19654     mask : false,
19655     /**
19656      * @cfg {Boolean} multiSelect Allow multiple selection
19657      */
19658     multiSelect : false,
19659     /**
19660      * @cfg {Boolean} singleSelect Allow single selection
19661      */
19662     singleSelect:  false,
19663     
19664     /**
19665      * @cfg {Boolean} toggleSelect - selecting 
19666      */
19667     toggleSelect : false,
19668     
19669     /**
19670      * @cfg {Boolean} tickable - selecting 
19671      */
19672     tickable : false,
19673     
19674     /**
19675      * Returns the element this view is bound to.
19676      * @return {Roo.Element}
19677      */
19678     getEl : function(){
19679         return this.wrapEl;
19680     },
19681     
19682     
19683
19684     /**
19685      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19686      */
19687     refresh : function(){
19688         //Roo.log('refresh');
19689         var t = this.tpl;
19690         
19691         // if we are using something like 'domtemplate', then
19692         // the what gets used is:
19693         // t.applySubtemplate(NAME, data, wrapping data..)
19694         // the outer template then get' applied with
19695         //     the store 'extra data'
19696         // and the body get's added to the
19697         //      roo-name="data" node?
19698         //      <span class='roo-tpl-{name}'></span> ?????
19699         
19700         
19701         
19702         this.clearSelections();
19703         this.el.update("");
19704         var html = [];
19705         var records = this.store.getRange();
19706         if(records.length < 1) {
19707             
19708             // is this valid??  = should it render a template??
19709             
19710             this.el.update(this.emptyText);
19711             return;
19712         }
19713         var el = this.el;
19714         if (this.dataName) {
19715             this.el.update(t.apply(this.store.meta)); //????
19716             el = this.el.child('.roo-tpl-' + this.dataName);
19717         }
19718         
19719         for(var i = 0, len = records.length; i < len; i++){
19720             var data = this.prepareData(records[i].data, i, records[i]);
19721             this.fireEvent("preparedata", this, data, i, records[i]);
19722             
19723             var d = Roo.apply({}, data);
19724             
19725             if(this.tickable){
19726                 Roo.apply(d, {'roo-id' : Roo.id()});
19727                 
19728                 var _this = this;
19729             
19730                 Roo.each(this.parent.item, function(item){
19731                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19732                         return;
19733                     }
19734                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19735                 });
19736             }
19737             
19738             html[html.length] = Roo.util.Format.trim(
19739                 this.dataName ?
19740                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19741                     t.apply(d)
19742             );
19743         }
19744         
19745         
19746         
19747         el.update(html.join(""));
19748         this.nodes = el.dom.childNodes;
19749         this.updateIndexes(0);
19750     },
19751     
19752
19753     /**
19754      * Function to override to reformat the data that is sent to
19755      * the template for each node.
19756      * DEPRICATED - use the preparedata event handler.
19757      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19758      * a JSON object for an UpdateManager bound view).
19759      */
19760     prepareData : function(data, index, record)
19761     {
19762         this.fireEvent("preparedata", this, data, index, record);
19763         return data;
19764     },
19765
19766     onUpdate : function(ds, record){
19767         // Roo.log('on update');   
19768         this.clearSelections();
19769         var index = this.store.indexOf(record);
19770         var n = this.nodes[index];
19771         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19772         n.parentNode.removeChild(n);
19773         this.updateIndexes(index, index);
19774     },
19775
19776     
19777     
19778 // --------- FIXME     
19779     onAdd : function(ds, records, index)
19780     {
19781         //Roo.log(['on Add', ds, records, index] );        
19782         this.clearSelections();
19783         if(this.nodes.length == 0){
19784             this.refresh();
19785             return;
19786         }
19787         var n = this.nodes[index];
19788         for(var i = 0, len = records.length; i < len; i++){
19789             var d = this.prepareData(records[i].data, i, records[i]);
19790             if(n){
19791                 this.tpl.insertBefore(n, d);
19792             }else{
19793                 
19794                 this.tpl.append(this.el, d);
19795             }
19796         }
19797         this.updateIndexes(index);
19798     },
19799
19800     onRemove : function(ds, record, index){
19801        // Roo.log('onRemove');
19802         this.clearSelections();
19803         var el = this.dataName  ?
19804             this.el.child('.roo-tpl-' + this.dataName) :
19805             this.el; 
19806         
19807         el.dom.removeChild(this.nodes[index]);
19808         this.updateIndexes(index);
19809     },
19810
19811     /**
19812      * Refresh an individual node.
19813      * @param {Number} index
19814      */
19815     refreshNode : function(index){
19816         this.onUpdate(this.store, this.store.getAt(index));
19817     },
19818
19819     updateIndexes : function(startIndex, endIndex){
19820         var ns = this.nodes;
19821         startIndex = startIndex || 0;
19822         endIndex = endIndex || ns.length - 1;
19823         for(var i = startIndex; i <= endIndex; i++){
19824             ns[i].nodeIndex = i;
19825         }
19826     },
19827
19828     /**
19829      * Changes the data store this view uses and refresh the view.
19830      * @param {Store} store
19831      */
19832     setStore : function(store, initial){
19833         if(!initial && this.store){
19834             this.store.un("datachanged", this.refresh);
19835             this.store.un("add", this.onAdd);
19836             this.store.un("remove", this.onRemove);
19837             this.store.un("update", this.onUpdate);
19838             this.store.un("clear", this.refresh);
19839             this.store.un("beforeload", this.onBeforeLoad);
19840             this.store.un("load", this.onLoad);
19841             this.store.un("loadexception", this.onLoad);
19842         }
19843         if(store){
19844           
19845             store.on("datachanged", this.refresh, this);
19846             store.on("add", this.onAdd, this);
19847             store.on("remove", this.onRemove, this);
19848             store.on("update", this.onUpdate, this);
19849             store.on("clear", this.refresh, this);
19850             store.on("beforeload", this.onBeforeLoad, this);
19851             store.on("load", this.onLoad, this);
19852             store.on("loadexception", this.onLoad, this);
19853         }
19854         
19855         if(store){
19856             this.refresh();
19857         }
19858     },
19859     /**
19860      * onbeforeLoad - masks the loading area.
19861      *
19862      */
19863     onBeforeLoad : function(store,opts)
19864     {
19865          //Roo.log('onBeforeLoad');   
19866         if (!opts.add) {
19867             this.el.update("");
19868         }
19869         this.el.mask(this.mask ? this.mask : "Loading" ); 
19870     },
19871     onLoad : function ()
19872     {
19873         this.el.unmask();
19874     },
19875     
19876
19877     /**
19878      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19879      * @param {HTMLElement} node
19880      * @return {HTMLElement} The template node
19881      */
19882     findItemFromChild : function(node){
19883         var el = this.dataName  ?
19884             this.el.child('.roo-tpl-' + this.dataName,true) :
19885             this.el.dom; 
19886         
19887         if(!node || node.parentNode == el){
19888                     return node;
19889             }
19890             var p = node.parentNode;
19891             while(p && p != el){
19892             if(p.parentNode == el){
19893                 return p;
19894             }
19895             p = p.parentNode;
19896         }
19897             return null;
19898     },
19899
19900     /** @ignore */
19901     onClick : function(e){
19902         var item = this.findItemFromChild(e.getTarget());
19903         if(item){
19904             var index = this.indexOf(item);
19905             if(this.onItemClick(item, index, e) !== false){
19906                 this.fireEvent("click", this, index, item, e);
19907             }
19908         }else{
19909             this.clearSelections();
19910         }
19911     },
19912
19913     /** @ignore */
19914     onContextMenu : function(e){
19915         var item = this.findItemFromChild(e.getTarget());
19916         if(item){
19917             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19918         }
19919     },
19920
19921     /** @ignore */
19922     onDblClick : function(e){
19923         var item = this.findItemFromChild(e.getTarget());
19924         if(item){
19925             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19926         }
19927     },
19928
19929     onItemClick : function(item, index, e)
19930     {
19931         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19932             return false;
19933         }
19934         if (this.toggleSelect) {
19935             var m = this.isSelected(item) ? 'unselect' : 'select';
19936             //Roo.log(m);
19937             var _t = this;
19938             _t[m](item, true, false);
19939             return true;
19940         }
19941         if(this.multiSelect || this.singleSelect){
19942             if(this.multiSelect && e.shiftKey && this.lastSelection){
19943                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19944             }else{
19945                 this.select(item, this.multiSelect && e.ctrlKey);
19946                 this.lastSelection = item;
19947             }
19948             
19949             if(!this.tickable){
19950                 e.preventDefault();
19951             }
19952             
19953         }
19954         return true;
19955     },
19956
19957     /**
19958      * Get the number of selected nodes.
19959      * @return {Number}
19960      */
19961     getSelectionCount : function(){
19962         return this.selections.length;
19963     },
19964
19965     /**
19966      * Get the currently selected nodes.
19967      * @return {Array} An array of HTMLElements
19968      */
19969     getSelectedNodes : function(){
19970         return this.selections;
19971     },
19972
19973     /**
19974      * Get the indexes of the selected nodes.
19975      * @return {Array}
19976      */
19977     getSelectedIndexes : function(){
19978         var indexes = [], s = this.selections;
19979         for(var i = 0, len = s.length; i < len; i++){
19980             indexes.push(s[i].nodeIndex);
19981         }
19982         return indexes;
19983     },
19984
19985     /**
19986      * Clear all selections
19987      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19988      */
19989     clearSelections : function(suppressEvent){
19990         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19991             this.cmp.elements = this.selections;
19992             this.cmp.removeClass(this.selectedClass);
19993             this.selections = [];
19994             if(!suppressEvent){
19995                 this.fireEvent("selectionchange", this, this.selections);
19996             }
19997         }
19998     },
19999
20000     /**
20001      * Returns true if the passed node is selected
20002      * @param {HTMLElement/Number} node The node or node index
20003      * @return {Boolean}
20004      */
20005     isSelected : function(node){
20006         var s = this.selections;
20007         if(s.length < 1){
20008             return false;
20009         }
20010         node = this.getNode(node);
20011         return s.indexOf(node) !== -1;
20012     },
20013
20014     /**
20015      * Selects nodes.
20016      * @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
20017      * @param {Boolean} keepExisting (optional) true to keep existing selections
20018      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20019      */
20020     select : function(nodeInfo, keepExisting, suppressEvent){
20021         if(nodeInfo instanceof Array){
20022             if(!keepExisting){
20023                 this.clearSelections(true);
20024             }
20025             for(var i = 0, len = nodeInfo.length; i < len; i++){
20026                 this.select(nodeInfo[i], true, true);
20027             }
20028             return;
20029         } 
20030         var node = this.getNode(nodeInfo);
20031         if(!node || this.isSelected(node)){
20032             return; // already selected.
20033         }
20034         if(!keepExisting){
20035             this.clearSelections(true);
20036         }
20037         
20038         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20039             Roo.fly(node).addClass(this.selectedClass);
20040             this.selections.push(node);
20041             if(!suppressEvent){
20042                 this.fireEvent("selectionchange", this, this.selections);
20043             }
20044         }
20045         
20046         
20047     },
20048       /**
20049      * Unselects nodes.
20050      * @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
20051      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20052      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20053      */
20054     unselect : function(nodeInfo, keepExisting, suppressEvent)
20055     {
20056         if(nodeInfo instanceof Array){
20057             Roo.each(this.selections, function(s) {
20058                 this.unselect(s, nodeInfo);
20059             }, this);
20060             return;
20061         }
20062         var node = this.getNode(nodeInfo);
20063         if(!node || !this.isSelected(node)){
20064             //Roo.log("not selected");
20065             return; // not selected.
20066         }
20067         // fireevent???
20068         var ns = [];
20069         Roo.each(this.selections, function(s) {
20070             if (s == node ) {
20071                 Roo.fly(node).removeClass(this.selectedClass);
20072
20073                 return;
20074             }
20075             ns.push(s);
20076         },this);
20077         
20078         this.selections= ns;
20079         this.fireEvent("selectionchange", this, this.selections);
20080     },
20081
20082     /**
20083      * Gets a template node.
20084      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20085      * @return {HTMLElement} The node or null if it wasn't found
20086      */
20087     getNode : function(nodeInfo){
20088         if(typeof nodeInfo == "string"){
20089             return document.getElementById(nodeInfo);
20090         }else if(typeof nodeInfo == "number"){
20091             return this.nodes[nodeInfo];
20092         }
20093         return nodeInfo;
20094     },
20095
20096     /**
20097      * Gets a range template nodes.
20098      * @param {Number} startIndex
20099      * @param {Number} endIndex
20100      * @return {Array} An array of nodes
20101      */
20102     getNodes : function(start, end){
20103         var ns = this.nodes;
20104         start = start || 0;
20105         end = typeof end == "undefined" ? ns.length - 1 : end;
20106         var nodes = [];
20107         if(start <= end){
20108             for(var i = start; i <= end; i++){
20109                 nodes.push(ns[i]);
20110             }
20111         } else{
20112             for(var i = start; i >= end; i--){
20113                 nodes.push(ns[i]);
20114             }
20115         }
20116         return nodes;
20117     },
20118
20119     /**
20120      * Finds the index of the passed node
20121      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20122      * @return {Number} The index of the node or -1
20123      */
20124     indexOf : function(node){
20125         node = this.getNode(node);
20126         if(typeof node.nodeIndex == "number"){
20127             return node.nodeIndex;
20128         }
20129         var ns = this.nodes;
20130         for(var i = 0, len = ns.length; i < len; i++){
20131             if(ns[i] == node){
20132                 return i;
20133             }
20134         }
20135         return -1;
20136     }
20137 });
20138 /*
20139  * - LGPL
20140  *
20141  * based on jquery fullcalendar
20142  * 
20143  */
20144
20145 Roo.bootstrap = Roo.bootstrap || {};
20146 /**
20147  * @class Roo.bootstrap.Calendar
20148  * @extends Roo.bootstrap.Component
20149  * Bootstrap Calendar class
20150  * @cfg {Boolean} loadMask (true|false) default false
20151  * @cfg {Object} header generate the user specific header of the calendar, default false
20152
20153  * @constructor
20154  * Create a new Container
20155  * @param {Object} config The config object
20156  */
20157
20158
20159
20160 Roo.bootstrap.Calendar = function(config){
20161     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20162      this.addEvents({
20163         /**
20164              * @event select
20165              * Fires when a date is selected
20166              * @param {DatePicker} this
20167              * @param {Date} date The selected date
20168              */
20169         'select': true,
20170         /**
20171              * @event monthchange
20172              * Fires when the displayed month changes 
20173              * @param {DatePicker} this
20174              * @param {Date} date The selected month
20175              */
20176         'monthchange': true,
20177         /**
20178              * @event evententer
20179              * Fires when mouse over an event
20180              * @param {Calendar} this
20181              * @param {event} Event
20182              */
20183         'evententer': true,
20184         /**
20185              * @event eventleave
20186              * Fires when the mouse leaves an
20187              * @param {Calendar} this
20188              * @param {event}
20189              */
20190         'eventleave': true,
20191         /**
20192              * @event eventclick
20193              * Fires when the mouse click an
20194              * @param {Calendar} this
20195              * @param {event}
20196              */
20197         'eventclick': true
20198         
20199     });
20200
20201 };
20202
20203 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20204     
20205           /**
20206      * @cfg {Roo.data.Store} store
20207      * The data source for the calendar
20208      */
20209         store : false,
20210      /**
20211      * @cfg {Number} startDay
20212      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20213      */
20214     startDay : 0,
20215     
20216     loadMask : false,
20217     
20218     header : false,
20219       
20220     getAutoCreate : function(){
20221         
20222         
20223         var fc_button = function(name, corner, style, content ) {
20224             return Roo.apply({},{
20225                 tag : 'span',
20226                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20227                          (corner.length ?
20228                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20229                             ''
20230                         ),
20231                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20232                 unselectable: 'on'
20233             });
20234         };
20235         
20236         var header = {};
20237         
20238         if(!this.header){
20239             header = {
20240                 tag : 'table',
20241                 cls : 'fc-header',
20242                 style : 'width:100%',
20243                 cn : [
20244                     {
20245                         tag: 'tr',
20246                         cn : [
20247                             {
20248                                 tag : 'td',
20249                                 cls : 'fc-header-left',
20250                                 cn : [
20251                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20252                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20253                                     { tag: 'span', cls: 'fc-header-space' },
20254                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20255
20256
20257                                 ]
20258                             },
20259
20260                             {
20261                                 tag : 'td',
20262                                 cls : 'fc-header-center',
20263                                 cn : [
20264                                     {
20265                                         tag: 'span',
20266                                         cls: 'fc-header-title',
20267                                         cn : {
20268                                             tag: 'H2',
20269                                             html : 'month / year'
20270                                         }
20271                                     }
20272
20273                                 ]
20274                             },
20275                             {
20276                                 tag : 'td',
20277                                 cls : 'fc-header-right',
20278                                 cn : [
20279                               /*      fc_button('month', 'left', '', 'month' ),
20280                                     fc_button('week', '', '', 'week' ),
20281                                     fc_button('day', 'right', '', 'day' )
20282                                 */    
20283
20284                                 ]
20285                             }
20286
20287                         ]
20288                     }
20289                 ]
20290             };
20291         }
20292         
20293         header = this.header;
20294         
20295        
20296         var cal_heads = function() {
20297             var ret = [];
20298             // fixme - handle this.
20299             
20300             for (var i =0; i < Date.dayNames.length; i++) {
20301                 var d = Date.dayNames[i];
20302                 ret.push({
20303                     tag: 'th',
20304                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20305                     html : d.substring(0,3)
20306                 });
20307                 
20308             }
20309             ret[0].cls += ' fc-first';
20310             ret[6].cls += ' fc-last';
20311             return ret;
20312         };
20313         var cal_cell = function(n) {
20314             return  {
20315                 tag: 'td',
20316                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20317                 cn : [
20318                     {
20319                         cn : [
20320                             {
20321                                 cls: 'fc-day-number',
20322                                 html: 'D'
20323                             },
20324                             {
20325                                 cls: 'fc-day-content',
20326                              
20327                                 cn : [
20328                                      {
20329                                         style: 'position: relative;' // height: 17px;
20330                                     }
20331                                 ]
20332                             }
20333                             
20334                             
20335                         ]
20336                     }
20337                 ]
20338                 
20339             }
20340         };
20341         var cal_rows = function() {
20342             
20343             var ret = [];
20344             for (var r = 0; r < 6; r++) {
20345                 var row= {
20346                     tag : 'tr',
20347                     cls : 'fc-week',
20348                     cn : []
20349                 };
20350                 
20351                 for (var i =0; i < Date.dayNames.length; i++) {
20352                     var d = Date.dayNames[i];
20353                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20354
20355                 }
20356                 row.cn[0].cls+=' fc-first';
20357                 row.cn[0].cn[0].style = 'min-height:90px';
20358                 row.cn[6].cls+=' fc-last';
20359                 ret.push(row);
20360                 
20361             }
20362             ret[0].cls += ' fc-first';
20363             ret[4].cls += ' fc-prev-last';
20364             ret[5].cls += ' fc-last';
20365             return ret;
20366             
20367         };
20368         
20369         var cal_table = {
20370             tag: 'table',
20371             cls: 'fc-border-separate',
20372             style : 'width:100%',
20373             cellspacing  : 0,
20374             cn : [
20375                 { 
20376                     tag: 'thead',
20377                     cn : [
20378                         { 
20379                             tag: 'tr',
20380                             cls : 'fc-first fc-last',
20381                             cn : cal_heads()
20382                         }
20383                     ]
20384                 },
20385                 { 
20386                     tag: 'tbody',
20387                     cn : cal_rows()
20388                 }
20389                   
20390             ]
20391         };
20392          
20393          var cfg = {
20394             cls : 'fc fc-ltr',
20395             cn : [
20396                 header,
20397                 {
20398                     cls : 'fc-content',
20399                     style : "position: relative;",
20400                     cn : [
20401                         {
20402                             cls : 'fc-view fc-view-month fc-grid',
20403                             style : 'position: relative',
20404                             unselectable : 'on',
20405                             cn : [
20406                                 {
20407                                     cls : 'fc-event-container',
20408                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20409                                 },
20410                                 cal_table
20411                             ]
20412                         }
20413                     ]
20414     
20415                 }
20416            ] 
20417             
20418         };
20419         
20420          
20421         
20422         return cfg;
20423     },
20424     
20425     
20426     initEvents : function()
20427     {
20428         if(!this.store){
20429             throw "can not find store for calendar";
20430         }
20431         
20432         var mark = {
20433             tag: "div",
20434             cls:"x-dlg-mask",
20435             style: "text-align:center",
20436             cn: [
20437                 {
20438                     tag: "div",
20439                     style: "background-color:white;width:50%;margin:250 auto",
20440                     cn: [
20441                         {
20442                             tag: "img",
20443                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20444                         },
20445                         {
20446                             tag: "span",
20447                             html: "Loading"
20448                         }
20449                         
20450                     ]
20451                 }
20452             ]
20453         };
20454         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20455         
20456         var size = this.el.select('.fc-content', true).first().getSize();
20457         this.maskEl.setSize(size.width, size.height);
20458         this.maskEl.enableDisplayMode("block");
20459         if(!this.loadMask){
20460             this.maskEl.hide();
20461         }
20462         
20463         this.store = Roo.factory(this.store, Roo.data);
20464         this.store.on('load', this.onLoad, this);
20465         this.store.on('beforeload', this.onBeforeLoad, this);
20466         
20467         this.resize();
20468         
20469         this.cells = this.el.select('.fc-day',true);
20470         //Roo.log(this.cells);
20471         this.textNodes = this.el.query('.fc-day-number');
20472         this.cells.addClassOnOver('fc-state-hover');
20473         
20474         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20475         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20476         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20477         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20478         
20479         this.on('monthchange', this.onMonthChange, this);
20480         
20481         this.update(new Date().clearTime());
20482     },
20483     
20484     resize : function() {
20485         var sz  = this.el.getSize();
20486         
20487         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20488         this.el.select('.fc-day-content div',true).setHeight(34);
20489     },
20490     
20491     
20492     // private
20493     showPrevMonth : function(e){
20494         this.update(this.activeDate.add("mo", -1));
20495     },
20496     showToday : function(e){
20497         this.update(new Date().clearTime());
20498     },
20499     // private
20500     showNextMonth : function(e){
20501         this.update(this.activeDate.add("mo", 1));
20502     },
20503
20504     // private
20505     showPrevYear : function(){
20506         this.update(this.activeDate.add("y", -1));
20507     },
20508
20509     // private
20510     showNextYear : function(){
20511         this.update(this.activeDate.add("y", 1));
20512     },
20513
20514     
20515    // private
20516     update : function(date)
20517     {
20518         var vd = this.activeDate;
20519         this.activeDate = date;
20520 //        if(vd && this.el){
20521 //            var t = date.getTime();
20522 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20523 //                Roo.log('using add remove');
20524 //                
20525 //                this.fireEvent('monthchange', this, date);
20526 //                
20527 //                this.cells.removeClass("fc-state-highlight");
20528 //                this.cells.each(function(c){
20529 //                   if(c.dateValue == t){
20530 //                       c.addClass("fc-state-highlight");
20531 //                       setTimeout(function(){
20532 //                            try{c.dom.firstChild.focus();}catch(e){}
20533 //                       }, 50);
20534 //                       return false;
20535 //                   }
20536 //                   return true;
20537 //                });
20538 //                return;
20539 //            }
20540 //        }
20541         
20542         var days = date.getDaysInMonth();
20543         
20544         var firstOfMonth = date.getFirstDateOfMonth();
20545         var startingPos = firstOfMonth.getDay()-this.startDay;
20546         
20547         if(startingPos < this.startDay){
20548             startingPos += 7;
20549         }
20550         
20551         var pm = date.add(Date.MONTH, -1);
20552         var prevStart = pm.getDaysInMonth()-startingPos;
20553 //        
20554         this.cells = this.el.select('.fc-day',true);
20555         this.textNodes = this.el.query('.fc-day-number');
20556         this.cells.addClassOnOver('fc-state-hover');
20557         
20558         var cells = this.cells.elements;
20559         var textEls = this.textNodes;
20560         
20561         Roo.each(cells, function(cell){
20562             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20563         });
20564         
20565         days += startingPos;
20566
20567         // convert everything to numbers so it's fast
20568         var day = 86400000;
20569         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20570         //Roo.log(d);
20571         //Roo.log(pm);
20572         //Roo.log(prevStart);
20573         
20574         var today = new Date().clearTime().getTime();
20575         var sel = date.clearTime().getTime();
20576         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20577         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20578         var ddMatch = this.disabledDatesRE;
20579         var ddText = this.disabledDatesText;
20580         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20581         var ddaysText = this.disabledDaysText;
20582         var format = this.format;
20583         
20584         var setCellClass = function(cal, cell){
20585             cell.row = 0;
20586             cell.events = [];
20587             cell.more = [];
20588             //Roo.log('set Cell Class');
20589             cell.title = "";
20590             var t = d.getTime();
20591             
20592             //Roo.log(d);
20593             
20594             cell.dateValue = t;
20595             if(t == today){
20596                 cell.className += " fc-today";
20597                 cell.className += " fc-state-highlight";
20598                 cell.title = cal.todayText;
20599             }
20600             if(t == sel){
20601                 // disable highlight in other month..
20602                 //cell.className += " fc-state-highlight";
20603                 
20604             }
20605             // disabling
20606             if(t < min) {
20607                 cell.className = " fc-state-disabled";
20608                 cell.title = cal.minText;
20609                 return;
20610             }
20611             if(t > max) {
20612                 cell.className = " fc-state-disabled";
20613                 cell.title = cal.maxText;
20614                 return;
20615             }
20616             if(ddays){
20617                 if(ddays.indexOf(d.getDay()) != -1){
20618                     cell.title = ddaysText;
20619                     cell.className = " fc-state-disabled";
20620                 }
20621             }
20622             if(ddMatch && format){
20623                 var fvalue = d.dateFormat(format);
20624                 if(ddMatch.test(fvalue)){
20625                     cell.title = ddText.replace("%0", fvalue);
20626                     cell.className = " fc-state-disabled";
20627                 }
20628             }
20629             
20630             if (!cell.initialClassName) {
20631                 cell.initialClassName = cell.dom.className;
20632             }
20633             
20634             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20635         };
20636
20637         var i = 0;
20638         
20639         for(; i < startingPos; i++) {
20640             textEls[i].innerHTML = (++prevStart);
20641             d.setDate(d.getDate()+1);
20642             
20643             cells[i].className = "fc-past fc-other-month";
20644             setCellClass(this, cells[i]);
20645         }
20646         
20647         var intDay = 0;
20648         
20649         for(; i < days; i++){
20650             intDay = i - startingPos + 1;
20651             textEls[i].innerHTML = (intDay);
20652             d.setDate(d.getDate()+1);
20653             
20654             cells[i].className = ''; // "x-date-active";
20655             setCellClass(this, cells[i]);
20656         }
20657         var extraDays = 0;
20658         
20659         for(; i < 42; i++) {
20660             textEls[i].innerHTML = (++extraDays);
20661             d.setDate(d.getDate()+1);
20662             
20663             cells[i].className = "fc-future fc-other-month";
20664             setCellClass(this, cells[i]);
20665         }
20666         
20667         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20668         
20669         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20670         
20671         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20672         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20673         
20674         if(totalRows != 6){
20675             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20676             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20677         }
20678         
20679         this.fireEvent('monthchange', this, date);
20680         
20681         
20682         /*
20683         if(!this.internalRender){
20684             var main = this.el.dom.firstChild;
20685             var w = main.offsetWidth;
20686             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20687             Roo.fly(main).setWidth(w);
20688             this.internalRender = true;
20689             // opera does not respect the auto grow header center column
20690             // then, after it gets a width opera refuses to recalculate
20691             // without a second pass
20692             if(Roo.isOpera && !this.secondPass){
20693                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20694                 this.secondPass = true;
20695                 this.update.defer(10, this, [date]);
20696             }
20697         }
20698         */
20699         
20700     },
20701     
20702     findCell : function(dt) {
20703         dt = dt.clearTime().getTime();
20704         var ret = false;
20705         this.cells.each(function(c){
20706             //Roo.log("check " +c.dateValue + '?=' + dt);
20707             if(c.dateValue == dt){
20708                 ret = c;
20709                 return false;
20710             }
20711             return true;
20712         });
20713         
20714         return ret;
20715     },
20716     
20717     findCells : function(ev) {
20718         var s = ev.start.clone().clearTime().getTime();
20719        // Roo.log(s);
20720         var e= ev.end.clone().clearTime().getTime();
20721        // Roo.log(e);
20722         var ret = [];
20723         this.cells.each(function(c){
20724              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20725             
20726             if(c.dateValue > e){
20727                 return ;
20728             }
20729             if(c.dateValue < s){
20730                 return ;
20731             }
20732             ret.push(c);
20733         });
20734         
20735         return ret;    
20736     },
20737     
20738 //    findBestRow: function(cells)
20739 //    {
20740 //        var ret = 0;
20741 //        
20742 //        for (var i =0 ; i < cells.length;i++) {
20743 //            ret  = Math.max(cells[i].rows || 0,ret);
20744 //        }
20745 //        return ret;
20746 //        
20747 //    },
20748     
20749     
20750     addItem : function(ev)
20751     {
20752         // look for vertical location slot in
20753         var cells = this.findCells(ev);
20754         
20755 //        ev.row = this.findBestRow(cells);
20756         
20757         // work out the location.
20758         
20759         var crow = false;
20760         var rows = [];
20761         for(var i =0; i < cells.length; i++) {
20762             
20763             cells[i].row = cells[0].row;
20764             
20765             if(i == 0){
20766                 cells[i].row = cells[i].row + 1;
20767             }
20768             
20769             if (!crow) {
20770                 crow = {
20771                     start : cells[i],
20772                     end :  cells[i]
20773                 };
20774                 continue;
20775             }
20776             if (crow.start.getY() == cells[i].getY()) {
20777                 // on same row.
20778                 crow.end = cells[i];
20779                 continue;
20780             }
20781             // different row.
20782             rows.push(crow);
20783             crow = {
20784                 start: cells[i],
20785                 end : cells[i]
20786             };
20787             
20788         }
20789         
20790         rows.push(crow);
20791         ev.els = [];
20792         ev.rows = rows;
20793         ev.cells = cells;
20794         
20795         cells[0].events.push(ev);
20796         
20797         this.calevents.push(ev);
20798     },
20799     
20800     clearEvents: function() {
20801         
20802         if(!this.calevents){
20803             return;
20804         }
20805         
20806         Roo.each(this.cells.elements, function(c){
20807             c.row = 0;
20808             c.events = [];
20809             c.more = [];
20810         });
20811         
20812         Roo.each(this.calevents, function(e) {
20813             Roo.each(e.els, function(el) {
20814                 el.un('mouseenter' ,this.onEventEnter, this);
20815                 el.un('mouseleave' ,this.onEventLeave, this);
20816                 el.remove();
20817             },this);
20818         },this);
20819         
20820         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20821             e.remove();
20822         });
20823         
20824     },
20825     
20826     renderEvents: function()
20827     {   
20828         var _this = this;
20829         
20830         this.cells.each(function(c) {
20831             
20832             if(c.row < 5){
20833                 return;
20834             }
20835             
20836             var ev = c.events;
20837             
20838             var r = 4;
20839             if(c.row != c.events.length){
20840                 r = 4 - (4 - (c.row - c.events.length));
20841             }
20842             
20843             c.events = ev.slice(0, r);
20844             c.more = ev.slice(r);
20845             
20846             if(c.more.length && c.more.length == 1){
20847                 c.events.push(c.more.pop());
20848             }
20849             
20850             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20851             
20852         });
20853             
20854         this.cells.each(function(c) {
20855             
20856             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20857             
20858             
20859             for (var e = 0; e < c.events.length; e++){
20860                 var ev = c.events[e];
20861                 var rows = ev.rows;
20862                 
20863                 for(var i = 0; i < rows.length; i++) {
20864                 
20865                     // how many rows should it span..
20866
20867                     var  cfg = {
20868                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20869                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20870
20871                         unselectable : "on",
20872                         cn : [
20873                             {
20874                                 cls: 'fc-event-inner',
20875                                 cn : [
20876     //                                {
20877     //                                  tag:'span',
20878     //                                  cls: 'fc-event-time',
20879     //                                  html : cells.length > 1 ? '' : ev.time
20880     //                                },
20881                                     {
20882                                       tag:'span',
20883                                       cls: 'fc-event-title',
20884                                       html : String.format('{0}', ev.title)
20885                                     }
20886
20887
20888                                 ]
20889                             },
20890                             {
20891                                 cls: 'ui-resizable-handle ui-resizable-e',
20892                                 html : '&nbsp;&nbsp;&nbsp'
20893                             }
20894
20895                         ]
20896                     };
20897
20898                     if (i == 0) {
20899                         cfg.cls += ' fc-event-start';
20900                     }
20901                     if ((i+1) == rows.length) {
20902                         cfg.cls += ' fc-event-end';
20903                     }
20904
20905                     var ctr = _this.el.select('.fc-event-container',true).first();
20906                     var cg = ctr.createChild(cfg);
20907
20908                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20909                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20910
20911                     var r = (c.more.length) ? 1 : 0;
20912                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20913                     cg.setWidth(ebox.right - sbox.x -2);
20914
20915                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20916                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20917                     cg.on('click', _this.onEventClick, _this, ev);
20918
20919                     ev.els.push(cg);
20920                     
20921                 }
20922                 
20923             }
20924             
20925             
20926             if(c.more.length){
20927                 var  cfg = {
20928                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20929                     style : 'position: absolute',
20930                     unselectable : "on",
20931                     cn : [
20932                         {
20933                             cls: 'fc-event-inner',
20934                             cn : [
20935                                 {
20936                                   tag:'span',
20937                                   cls: 'fc-event-title',
20938                                   html : 'More'
20939                                 }
20940
20941
20942                             ]
20943                         },
20944                         {
20945                             cls: 'ui-resizable-handle ui-resizable-e',
20946                             html : '&nbsp;&nbsp;&nbsp'
20947                         }
20948
20949                     ]
20950                 };
20951
20952                 var ctr = _this.el.select('.fc-event-container',true).first();
20953                 var cg = ctr.createChild(cfg);
20954
20955                 var sbox = c.select('.fc-day-content',true).first().getBox();
20956                 var ebox = c.select('.fc-day-content',true).first().getBox();
20957                 //Roo.log(cg);
20958                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20959                 cg.setWidth(ebox.right - sbox.x -2);
20960
20961                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20962                 
20963             }
20964             
20965         });
20966         
20967         
20968         
20969     },
20970     
20971     onEventEnter: function (e, el,event,d) {
20972         this.fireEvent('evententer', this, el, event);
20973     },
20974     
20975     onEventLeave: function (e, el,event,d) {
20976         this.fireEvent('eventleave', this, el, event);
20977     },
20978     
20979     onEventClick: function (e, el,event,d) {
20980         this.fireEvent('eventclick', this, el, event);
20981     },
20982     
20983     onMonthChange: function () {
20984         this.store.load();
20985     },
20986     
20987     onMoreEventClick: function(e, el, more)
20988     {
20989         var _this = this;
20990         
20991         this.calpopover.placement = 'right';
20992         this.calpopover.setTitle('More');
20993         
20994         this.calpopover.setContent('');
20995         
20996         var ctr = this.calpopover.el.select('.popover-content', true).first();
20997         
20998         Roo.each(more, function(m){
20999             var cfg = {
21000                 cls : 'fc-event-hori fc-event-draggable',
21001                 html : m.title
21002             };
21003             var cg = ctr.createChild(cfg);
21004             
21005             cg.on('click', _this.onEventClick, _this, m);
21006         });
21007         
21008         this.calpopover.show(el);
21009         
21010         
21011     },
21012     
21013     onLoad: function () 
21014     {   
21015         this.calevents = [];
21016         var cal = this;
21017         
21018         if(this.store.getCount() > 0){
21019             this.store.data.each(function(d){
21020                cal.addItem({
21021                     id : d.data.id,
21022                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21023                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21024                     time : d.data.start_time,
21025                     title : d.data.title,
21026                     description : d.data.description,
21027                     venue : d.data.venue
21028                 });
21029             });
21030         }
21031         
21032         this.renderEvents();
21033         
21034         if(this.calevents.length && this.loadMask){
21035             this.maskEl.hide();
21036         }
21037     },
21038     
21039     onBeforeLoad: function()
21040     {
21041         this.clearEvents();
21042         if(this.loadMask){
21043             this.maskEl.show();
21044         }
21045     }
21046 });
21047
21048  
21049  /*
21050  * - LGPL
21051  *
21052  * element
21053  * 
21054  */
21055
21056 /**
21057  * @class Roo.bootstrap.Popover
21058  * @extends Roo.bootstrap.Component
21059  * Bootstrap Popover class
21060  * @cfg {String} html contents of the popover   (or false to use children..)
21061  * @cfg {String} title of popover (or false to hide)
21062  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21063  * @cfg {String} trigger click || hover (or false to trigger manually)
21064  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21065  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21066  *      - if false and it has a 'parent' then it will be automatically added to that element
21067  *      - if string - Roo.get  will be called 
21068  * @cfg {Number} delay - delay before showing
21069  
21070  * @constructor
21071  * Create a new Popover
21072  * @param {Object} config The config object
21073  */
21074
21075 Roo.bootstrap.Popover = function(config){
21076     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21077     
21078     this.addEvents({
21079         // raw events
21080          /**
21081          * @event show
21082          * After the popover show
21083          * 
21084          * @param {Roo.bootstrap.Popover} this
21085          */
21086         "show" : true,
21087         /**
21088          * @event hide
21089          * After the popover hide
21090          * 
21091          * @param {Roo.bootstrap.Popover} this
21092          */
21093         "hide" : true
21094     });
21095 };
21096
21097 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21098     
21099     title: false,
21100     html: false,
21101     
21102     placement : 'right',
21103     trigger : 'hover', // hover
21104     modal : false,
21105     delay : 0,
21106     
21107     over: false,
21108     
21109     can_build_overlaid : false,
21110     
21111     maskEl : false, // the mask element
21112     headerEl : false,
21113     contentEl : false,
21114     alignEl : false, // when show is called with an element - this get's stored.
21115     
21116     getChildContainer : function()
21117     {
21118         return this.contentEl;
21119         
21120     },
21121     getPopoverHeader : function()
21122     {
21123         this.title = true; // flag not to hide it..
21124         this.headerEl.addClass('p-0');
21125         return this.headerEl
21126     },
21127     
21128     
21129     getAutoCreate : function(){
21130          
21131         var cfg = {
21132            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21133            style: 'display:block',
21134            cn : [
21135                 {
21136                     cls : 'arrow'
21137                 },
21138                 {
21139                     cls : 'popover-inner ',
21140                     cn : [
21141                         {
21142                             tag: 'h3',
21143                             cls: 'popover-title popover-header',
21144                             html : this.title === false ? '' : this.title
21145                         },
21146                         {
21147                             cls : 'popover-content popover-body '  + (this.cls || ''),
21148                             html : this.html || ''
21149                         }
21150                     ]
21151                     
21152                 }
21153            ]
21154         };
21155         
21156         return cfg;
21157     },
21158     /**
21159      * @param {string} the title
21160      */
21161     setTitle: function(str)
21162     {
21163         this.title = str;
21164         if (this.el) {
21165             this.headerEl.dom.innerHTML = str;
21166         }
21167         
21168     },
21169     /**
21170      * @param {string} the body content
21171      */
21172     setContent: function(str)
21173     {
21174         this.html = str;
21175         if (this.contentEl) {
21176             this.contentEl.dom.innerHTML = str;
21177         }
21178         
21179     },
21180     // as it get's added to the bottom of the page.
21181     onRender : function(ct, position)
21182     {
21183         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21184         
21185         
21186         
21187         if(!this.el){
21188             var cfg = Roo.apply({},  this.getAutoCreate());
21189             cfg.id = Roo.id();
21190             
21191             if (this.cls) {
21192                 cfg.cls += ' ' + this.cls;
21193             }
21194             if (this.style) {
21195                 cfg.style = this.style;
21196             }
21197             //Roo.log("adding to ");
21198             this.el = Roo.get(document.body).createChild(cfg, position);
21199 //            Roo.log(this.el);
21200         }
21201         
21202         this.contentEl = this.el.select('.popover-content',true).first();
21203         this.headerEl =  this.el.select('.popover-title',true).first();
21204         
21205         var nitems = [];
21206         if(typeof(this.items) != 'undefined'){
21207             var items = this.items;
21208             delete this.items;
21209
21210             for(var i =0;i < items.length;i++) {
21211                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21212             }
21213         }
21214
21215         this.items = nitems;
21216         
21217         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21218         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21219         
21220         
21221         
21222         this.initEvents();
21223     },
21224     
21225     resizeMask : function()
21226     {
21227         this.maskEl.setSize(
21228             Roo.lib.Dom.getViewWidth(true),
21229             Roo.lib.Dom.getViewHeight(true)
21230         );
21231     },
21232     
21233     initEvents : function()
21234     {
21235         
21236         if (!this.modal) { 
21237             Roo.bootstrap.Popover.register(this);
21238         }
21239          
21240         this.arrowEl = this.el.select('.arrow',true).first();
21241         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21242         this.el.enableDisplayMode('block');
21243         this.el.hide();
21244  
21245         
21246         if (this.over === false && !this.parent()) {
21247             return; 
21248         }
21249         if (this.triggers === false) {
21250             return;
21251         }
21252          
21253         // support parent
21254         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21255         var triggers = this.trigger ? this.trigger.split(' ') : [];
21256         Roo.each(triggers, function(trigger) {
21257         
21258             if (trigger == 'click') {
21259                 on_el.on('click', this.toggle, this);
21260             } else if (trigger != 'manual') {
21261                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21262                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21263       
21264                 on_el.on(eventIn  ,this.enter, this);
21265                 on_el.on(eventOut, this.leave, this);
21266             }
21267         }, this);
21268     },
21269     
21270     
21271     // private
21272     timeout : null,
21273     hoverState : null,
21274     
21275     toggle : function () {
21276         this.hoverState == 'in' ? this.leave() : this.enter();
21277     },
21278     
21279     enter : function () {
21280         
21281         clearTimeout(this.timeout);
21282     
21283         this.hoverState = 'in';
21284     
21285         if (!this.delay || !this.delay.show) {
21286             this.show();
21287             return;
21288         }
21289         var _t = this;
21290         this.timeout = setTimeout(function () {
21291             if (_t.hoverState == 'in') {
21292                 _t.show();
21293             }
21294         }, this.delay.show)
21295     },
21296     
21297     leave : function() {
21298         clearTimeout(this.timeout);
21299     
21300         this.hoverState = 'out';
21301     
21302         if (!this.delay || !this.delay.hide) {
21303             this.hide();
21304             return;
21305         }
21306         var _t = this;
21307         this.timeout = setTimeout(function () {
21308             if (_t.hoverState == 'out') {
21309                 _t.hide();
21310             }
21311         }, this.delay.hide)
21312     },
21313     /**
21314      * Show the popover
21315      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21316      * @param {string} (left|right|top|bottom) position
21317      */
21318     show : function (on_el, placement)
21319     {
21320         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21321         on_el = on_el || false; // default to false
21322          
21323         if (!on_el) {
21324             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21325                 on_el = this.parent().el;
21326             } else if (this.over) {
21327                 on_el = Roo.get(this.over);
21328             }
21329             
21330         }
21331         
21332         this.alignEl = Roo.get( on_el );
21333
21334         if (!this.el) {
21335             this.render(document.body);
21336         }
21337         
21338         
21339          
21340         
21341         if (this.title === false) {
21342             this.headerEl.hide();
21343         }
21344         
21345        
21346         this.el.show();
21347         this.el.dom.style.display = 'block';
21348          
21349  
21350         if (this.alignEl) {
21351             this.updatePosition(this.placement, true);
21352              
21353         } else {
21354             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21355             var es = this.el.getSize();
21356             var x = Roo.lib.Dom.getViewWidth()/2;
21357             var y = Roo.lib.Dom.getViewHeight()/2;
21358             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21359             
21360         }
21361
21362         
21363         //var arrow = this.el.select('.arrow',true).first();
21364         //arrow.set(align[2], 
21365         
21366         this.el.addClass('in');
21367         
21368          
21369         
21370         this.hoverState = 'in';
21371         
21372         if (this.modal) {
21373             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21374             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21375             this.maskEl.dom.style.display = 'block';
21376             this.maskEl.addClass('show');
21377         }
21378         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21379  
21380         this.fireEvent('show', this);
21381         
21382     },
21383     /**
21384      * fire this manually after loading a grid in the table for example
21385      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21386      * @param {Boolean} try and move it if we cant get right position.
21387      */
21388     updatePosition : function(placement, try_move)
21389     {
21390         // allow for calling with no parameters
21391         placement = placement   ? placement :  this.placement;
21392         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21393         
21394         this.el.removeClass([
21395             'fade','top','bottom', 'left', 'right','in',
21396             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21397         ]);
21398         this.el.addClass(placement + ' bs-popover-' + placement);
21399         
21400         if (!this.alignEl ) {
21401             return false;
21402         }
21403         
21404         switch (placement) {
21405             case 'right':
21406                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21407                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21408                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21409                     //normal display... or moved up/down.
21410                     this.el.setXY(offset);
21411                     var xy = this.alignEl.getAnchorXY('tr', false);
21412                     xy[0]+=2;xy[1]+=5;
21413                     this.arrowEl.setXY(xy);
21414                     return true;
21415                 }
21416                 // continue through...
21417                 return this.updatePosition('left', false);
21418                 
21419             
21420             case 'left':
21421                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21422                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21423                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21424                     //normal display... or moved up/down.
21425                     this.el.setXY(offset);
21426                     var xy = this.alignEl.getAnchorXY('tl', false);
21427                     xy[0]-=10;xy[1]+=5; // << fix me
21428                     this.arrowEl.setXY(xy);
21429                     return true;
21430                 }
21431                 // call self...
21432                 return this.updatePosition('right', false);
21433             
21434             case 'top':
21435                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21436                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21437                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21438                     //normal display... or moved up/down.
21439                     this.el.setXY(offset);
21440                     var xy = this.alignEl.getAnchorXY('t', false);
21441                     xy[1]-=10; // << fix me
21442                     this.arrowEl.setXY(xy);
21443                     return true;
21444                 }
21445                 // fall through
21446                return this.updatePosition('bottom', false);
21447             
21448             case 'bottom':
21449                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21450                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21451                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21452                     //normal display... or moved up/down.
21453                     this.el.setXY(offset);
21454                     var xy = this.alignEl.getAnchorXY('b', false);
21455                      xy[1]+=2; // << fix me
21456                     this.arrowEl.setXY(xy);
21457                     return true;
21458                 }
21459                 // fall through
21460                 return this.updatePosition('top', false);
21461                 
21462             
21463         }
21464         
21465         
21466         return false;
21467     },
21468     
21469     hide : function()
21470     {
21471         this.el.setXY([0,0]);
21472         this.el.removeClass('in');
21473         this.el.hide();
21474         this.hoverState = null;
21475         this.maskEl.hide(); // always..
21476         this.fireEvent('hide', this);
21477     }
21478     
21479 });
21480
21481
21482 Roo.apply(Roo.bootstrap.Popover, {
21483
21484     alignment : {
21485         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21486         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21487         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21488         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21489     },
21490     
21491     zIndex : 20001,
21492
21493     clickHander : false,
21494     
21495     
21496
21497     onMouseDown : function(e)
21498     {
21499         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21500             /// what is nothing is showing..
21501             this.hideAll();
21502         }
21503          
21504     },
21505     
21506     
21507     popups : [],
21508     
21509     register : function(popup)
21510     {
21511         if (!Roo.bootstrap.Popover.clickHandler) {
21512             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21513         }
21514         // hide other popups.
21515         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21516         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21517         this.hideAll(); //<< why?
21518         //this.popups.push(popup);
21519     },
21520     hideAll : function()
21521     {
21522         this.popups.forEach(function(p) {
21523             p.hide();
21524         });
21525     },
21526     onShow : function() {
21527         Roo.bootstrap.Popover.popups.push(this);
21528     },
21529     onHide : function() {
21530         Roo.bootstrap.Popover.popups.remove(this);
21531     } 
21532
21533 });/*
21534  * - LGPL
21535  *
21536  * Card header - holder for the card header elements.
21537  * 
21538  */
21539
21540 /**
21541  * @class Roo.bootstrap.PopoverNav
21542  * @extends Roo.bootstrap.NavGroup
21543  * Bootstrap Popover header navigation class
21544  * @constructor
21545  * Create a new Popover Header Navigation 
21546  * @param {Object} config The config object
21547  */
21548
21549 Roo.bootstrap.PopoverNav = function(config){
21550     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21551 };
21552
21553 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21554     
21555     
21556     container_method : 'getPopoverHeader' 
21557     
21558      
21559     
21560     
21561    
21562 });
21563
21564  
21565
21566  /*
21567  * - LGPL
21568  *
21569  * Progress
21570  * 
21571  */
21572
21573 /**
21574  * @class Roo.bootstrap.Progress
21575  * @extends Roo.bootstrap.Component
21576  * Bootstrap Progress class
21577  * @cfg {Boolean} striped striped of the progress bar
21578  * @cfg {Boolean} active animated of the progress bar
21579  * 
21580  * 
21581  * @constructor
21582  * Create a new Progress
21583  * @param {Object} config The config object
21584  */
21585
21586 Roo.bootstrap.Progress = function(config){
21587     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21588 };
21589
21590 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21591     
21592     striped : false,
21593     active: false,
21594     
21595     getAutoCreate : function(){
21596         var cfg = {
21597             tag: 'div',
21598             cls: 'progress'
21599         };
21600         
21601         
21602         if(this.striped){
21603             cfg.cls += ' progress-striped';
21604         }
21605       
21606         if(this.active){
21607             cfg.cls += ' active';
21608         }
21609         
21610         
21611         return cfg;
21612     }
21613    
21614 });
21615
21616  
21617
21618  /*
21619  * - LGPL
21620  *
21621  * ProgressBar
21622  * 
21623  */
21624
21625 /**
21626  * @class Roo.bootstrap.ProgressBar
21627  * @extends Roo.bootstrap.Component
21628  * Bootstrap ProgressBar class
21629  * @cfg {Number} aria_valuenow aria-value now
21630  * @cfg {Number} aria_valuemin aria-value min
21631  * @cfg {Number} aria_valuemax aria-value max
21632  * @cfg {String} label label for the progress bar
21633  * @cfg {String} panel (success | info | warning | danger )
21634  * @cfg {String} role role of the progress bar
21635  * @cfg {String} sr_only text
21636  * 
21637  * 
21638  * @constructor
21639  * Create a new ProgressBar
21640  * @param {Object} config The config object
21641  */
21642
21643 Roo.bootstrap.ProgressBar = function(config){
21644     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21645 };
21646
21647 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21648     
21649     aria_valuenow : 0,
21650     aria_valuemin : 0,
21651     aria_valuemax : 100,
21652     label : false,
21653     panel : false,
21654     role : false,
21655     sr_only: false,
21656     
21657     getAutoCreate : function()
21658     {
21659         
21660         var cfg = {
21661             tag: 'div',
21662             cls: 'progress-bar',
21663             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21664         };
21665         
21666         if(this.sr_only){
21667             cfg.cn = {
21668                 tag: 'span',
21669                 cls: 'sr-only',
21670                 html: this.sr_only
21671             }
21672         }
21673         
21674         if(this.role){
21675             cfg.role = this.role;
21676         }
21677         
21678         if(this.aria_valuenow){
21679             cfg['aria-valuenow'] = this.aria_valuenow;
21680         }
21681         
21682         if(this.aria_valuemin){
21683             cfg['aria-valuemin'] = this.aria_valuemin;
21684         }
21685         
21686         if(this.aria_valuemax){
21687             cfg['aria-valuemax'] = this.aria_valuemax;
21688         }
21689         
21690         if(this.label && !this.sr_only){
21691             cfg.html = this.label;
21692         }
21693         
21694         if(this.panel){
21695             cfg.cls += ' progress-bar-' + this.panel;
21696         }
21697         
21698         return cfg;
21699     },
21700     
21701     update : function(aria_valuenow)
21702     {
21703         this.aria_valuenow = aria_valuenow;
21704         
21705         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21706     }
21707    
21708 });
21709
21710  
21711
21712  /*
21713  * - LGPL
21714  *
21715  * column
21716  * 
21717  */
21718
21719 /**
21720  * @class Roo.bootstrap.TabGroup
21721  * @extends Roo.bootstrap.Column
21722  * Bootstrap Column class
21723  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21724  * @cfg {Boolean} carousel true to make the group behave like a carousel
21725  * @cfg {Boolean} bullets show bullets for the panels
21726  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21727  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21728  * @cfg {Boolean} showarrow (true|false) show arrow default true
21729  * 
21730  * @constructor
21731  * Create a new TabGroup
21732  * @param {Object} config The config object
21733  */
21734
21735 Roo.bootstrap.TabGroup = function(config){
21736     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21737     if (!this.navId) {
21738         this.navId = Roo.id();
21739     }
21740     this.tabs = [];
21741     Roo.bootstrap.TabGroup.register(this);
21742     
21743 };
21744
21745 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21746     
21747     carousel : false,
21748     transition : false,
21749     bullets : 0,
21750     timer : 0,
21751     autoslide : false,
21752     slideFn : false,
21753     slideOnTouch : false,
21754     showarrow : true,
21755     
21756     getAutoCreate : function()
21757     {
21758         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21759         
21760         cfg.cls += ' tab-content';
21761         
21762         if (this.carousel) {
21763             cfg.cls += ' carousel slide';
21764             
21765             cfg.cn = [{
21766                cls : 'carousel-inner',
21767                cn : []
21768             }];
21769         
21770             if(this.bullets  && !Roo.isTouch){
21771                 
21772                 var bullets = {
21773                     cls : 'carousel-bullets',
21774                     cn : []
21775                 };
21776                
21777                 if(this.bullets_cls){
21778                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21779                 }
21780                 
21781                 bullets.cn.push({
21782                     cls : 'clear'
21783                 });
21784                 
21785                 cfg.cn[0].cn.push(bullets);
21786             }
21787             
21788             if(this.showarrow){
21789                 cfg.cn[0].cn.push({
21790                     tag : 'div',
21791                     class : 'carousel-arrow',
21792                     cn : [
21793                         {
21794                             tag : 'div',
21795                             class : 'carousel-prev',
21796                             cn : [
21797                                 {
21798                                     tag : 'i',
21799                                     class : 'fa fa-chevron-left'
21800                                 }
21801                             ]
21802                         },
21803                         {
21804                             tag : 'div',
21805                             class : 'carousel-next',
21806                             cn : [
21807                                 {
21808                                     tag : 'i',
21809                                     class : 'fa fa-chevron-right'
21810                                 }
21811                             ]
21812                         }
21813                     ]
21814                 });
21815             }
21816             
21817         }
21818         
21819         return cfg;
21820     },
21821     
21822     initEvents:  function()
21823     {
21824 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21825 //            this.el.on("touchstart", this.onTouchStart, this);
21826 //        }
21827         
21828         if(this.autoslide){
21829             var _this = this;
21830             
21831             this.slideFn = window.setInterval(function() {
21832                 _this.showPanelNext();
21833             }, this.timer);
21834         }
21835         
21836         if(this.showarrow){
21837             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21838             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21839         }
21840         
21841         
21842     },
21843     
21844 //    onTouchStart : function(e, el, o)
21845 //    {
21846 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21847 //            return;
21848 //        }
21849 //        
21850 //        this.showPanelNext();
21851 //    },
21852     
21853     
21854     getChildContainer : function()
21855     {
21856         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21857     },
21858     
21859     /**
21860     * register a Navigation item
21861     * @param {Roo.bootstrap.NavItem} the navitem to add
21862     */
21863     register : function(item)
21864     {
21865         this.tabs.push( item);
21866         item.navId = this.navId; // not really needed..
21867         this.addBullet();
21868     
21869     },
21870     
21871     getActivePanel : function()
21872     {
21873         var r = false;
21874         Roo.each(this.tabs, function(t) {
21875             if (t.active) {
21876                 r = t;
21877                 return false;
21878             }
21879             return null;
21880         });
21881         return r;
21882         
21883     },
21884     getPanelByName : function(n)
21885     {
21886         var r = false;
21887         Roo.each(this.tabs, function(t) {
21888             if (t.tabId == n) {
21889                 r = t;
21890                 return false;
21891             }
21892             return null;
21893         });
21894         return r;
21895     },
21896     indexOfPanel : function(p)
21897     {
21898         var r = false;
21899         Roo.each(this.tabs, function(t,i) {
21900             if (t.tabId == p.tabId) {
21901                 r = i;
21902                 return false;
21903             }
21904             return null;
21905         });
21906         return r;
21907     },
21908     /**
21909      * show a specific panel
21910      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21911      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21912      */
21913     showPanel : function (pan)
21914     {
21915         if(this.transition || typeof(pan) == 'undefined'){
21916             Roo.log("waiting for the transitionend");
21917             return false;
21918         }
21919         
21920         if (typeof(pan) == 'number') {
21921             pan = this.tabs[pan];
21922         }
21923         
21924         if (typeof(pan) == 'string') {
21925             pan = this.getPanelByName(pan);
21926         }
21927         
21928         var cur = this.getActivePanel();
21929         
21930         if(!pan || !cur){
21931             Roo.log('pan or acitve pan is undefined');
21932             return false;
21933         }
21934         
21935         if (pan.tabId == this.getActivePanel().tabId) {
21936             return true;
21937         }
21938         
21939         if (false === cur.fireEvent('beforedeactivate')) {
21940             return false;
21941         }
21942         
21943         if(this.bullets > 0 && !Roo.isTouch){
21944             this.setActiveBullet(this.indexOfPanel(pan));
21945         }
21946         
21947         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21948             
21949             //class="carousel-item carousel-item-next carousel-item-left"
21950             
21951             this.transition = true;
21952             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21953             var lr = dir == 'next' ? 'left' : 'right';
21954             pan.el.addClass(dir); // or prev
21955             pan.el.addClass('carousel-item-' + dir); // or prev
21956             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21957             cur.el.addClass(lr); // or right
21958             pan.el.addClass(lr);
21959             cur.el.addClass('carousel-item-' +lr); // or right
21960             pan.el.addClass('carousel-item-' +lr);
21961             
21962             
21963             var _this = this;
21964             cur.el.on('transitionend', function() {
21965                 Roo.log("trans end?");
21966                 
21967                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21968                 pan.setActive(true);
21969                 
21970                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21971                 cur.setActive(false);
21972                 
21973                 _this.transition = false;
21974                 
21975             }, this, { single:  true } );
21976             
21977             return true;
21978         }
21979         
21980         cur.setActive(false);
21981         pan.setActive(true);
21982         
21983         return true;
21984         
21985     },
21986     showPanelNext : function()
21987     {
21988         var i = this.indexOfPanel(this.getActivePanel());
21989         
21990         if (i >= this.tabs.length - 1 && !this.autoslide) {
21991             return;
21992         }
21993         
21994         if (i >= this.tabs.length - 1 && this.autoslide) {
21995             i = -1;
21996         }
21997         
21998         this.showPanel(this.tabs[i+1]);
21999     },
22000     
22001     showPanelPrev : function()
22002     {
22003         var i = this.indexOfPanel(this.getActivePanel());
22004         
22005         if (i  < 1 && !this.autoslide) {
22006             return;
22007         }
22008         
22009         if (i < 1 && this.autoslide) {
22010             i = this.tabs.length;
22011         }
22012         
22013         this.showPanel(this.tabs[i-1]);
22014     },
22015     
22016     
22017     addBullet: function()
22018     {
22019         if(!this.bullets || Roo.isTouch){
22020             return;
22021         }
22022         var ctr = this.el.select('.carousel-bullets',true).first();
22023         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22024         var bullet = ctr.createChild({
22025             cls : 'bullet bullet-' + i
22026         },ctr.dom.lastChild);
22027         
22028         
22029         var _this = this;
22030         
22031         bullet.on('click', (function(e, el, o, ii, t){
22032
22033             e.preventDefault();
22034
22035             this.showPanel(ii);
22036
22037             if(this.autoslide && this.slideFn){
22038                 clearInterval(this.slideFn);
22039                 this.slideFn = window.setInterval(function() {
22040                     _this.showPanelNext();
22041                 }, this.timer);
22042             }
22043
22044         }).createDelegate(this, [i, bullet], true));
22045                 
22046         
22047     },
22048      
22049     setActiveBullet : function(i)
22050     {
22051         if(Roo.isTouch){
22052             return;
22053         }
22054         
22055         Roo.each(this.el.select('.bullet', true).elements, function(el){
22056             el.removeClass('selected');
22057         });
22058
22059         var bullet = this.el.select('.bullet-' + i, true).first();
22060         
22061         if(!bullet){
22062             return;
22063         }
22064         
22065         bullet.addClass('selected');
22066     }
22067     
22068     
22069   
22070 });
22071
22072  
22073
22074  
22075  
22076 Roo.apply(Roo.bootstrap.TabGroup, {
22077     
22078     groups: {},
22079      /**
22080     * register a Navigation Group
22081     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22082     */
22083     register : function(navgrp)
22084     {
22085         this.groups[navgrp.navId] = navgrp;
22086         
22087     },
22088     /**
22089     * fetch a Navigation Group based on the navigation ID
22090     * if one does not exist , it will get created.
22091     * @param {string} the navgroup to add
22092     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22093     */
22094     get: function(navId) {
22095         if (typeof(this.groups[navId]) == 'undefined') {
22096             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22097         }
22098         return this.groups[navId] ;
22099     }
22100     
22101     
22102     
22103 });
22104
22105  /*
22106  * - LGPL
22107  *
22108  * TabPanel
22109  * 
22110  */
22111
22112 /**
22113  * @class Roo.bootstrap.TabPanel
22114  * @extends Roo.bootstrap.Component
22115  * Bootstrap TabPanel class
22116  * @cfg {Boolean} active panel active
22117  * @cfg {String} html panel content
22118  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22119  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22120  * @cfg {String} href click to link..
22121  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22122  * 
22123  * 
22124  * @constructor
22125  * Create a new TabPanel
22126  * @param {Object} config The config object
22127  */
22128
22129 Roo.bootstrap.TabPanel = function(config){
22130     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22131     this.addEvents({
22132         /**
22133              * @event changed
22134              * Fires when the active status changes
22135              * @param {Roo.bootstrap.TabPanel} this
22136              * @param {Boolean} state the new state
22137             
22138          */
22139         'changed': true,
22140         /**
22141              * @event beforedeactivate
22142              * Fires before a tab is de-activated - can be used to do validation on a form.
22143              * @param {Roo.bootstrap.TabPanel} this
22144              * @return {Boolean} false if there is an error
22145             
22146          */
22147         'beforedeactivate': true
22148      });
22149     
22150     this.tabId = this.tabId || Roo.id();
22151   
22152 };
22153
22154 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22155     
22156     active: false,
22157     html: false,
22158     tabId: false,
22159     navId : false,
22160     href : '',
22161     touchSlide : false,
22162     getAutoCreate : function(){
22163         
22164         
22165         var cfg = {
22166             tag: 'div',
22167             // item is needed for carousel - not sure if it has any effect otherwise
22168             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22169             html: this.html || ''
22170         };
22171         
22172         if(this.active){
22173             cfg.cls += ' active';
22174         }
22175         
22176         if(this.tabId){
22177             cfg.tabId = this.tabId;
22178         }
22179         
22180         
22181         
22182         return cfg;
22183     },
22184     
22185     initEvents:  function()
22186     {
22187         var p = this.parent();
22188         
22189         this.navId = this.navId || p.navId;
22190         
22191         if (typeof(this.navId) != 'undefined') {
22192             // not really needed.. but just in case.. parent should be a NavGroup.
22193             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22194             
22195             tg.register(this);
22196             
22197             var i = tg.tabs.length - 1;
22198             
22199             if(this.active && tg.bullets > 0 && i < tg.bullets){
22200                 tg.setActiveBullet(i);
22201             }
22202         }
22203         
22204         this.el.on('click', this.onClick, this);
22205         
22206         if(Roo.isTouch && this.touchSlide){
22207             this.el.on("touchstart", this.onTouchStart, this);
22208             this.el.on("touchmove", this.onTouchMove, this);
22209             this.el.on("touchend", this.onTouchEnd, this);
22210         }
22211         
22212     },
22213     
22214     onRender : function(ct, position)
22215     {
22216         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22217     },
22218     
22219     setActive : function(state)
22220     {
22221         Roo.log("panel - set active " + this.tabId + "=" + state);
22222         
22223         this.active = state;
22224         if (!state) {
22225             this.el.removeClass('active');
22226             
22227         } else  if (!this.el.hasClass('active')) {
22228             this.el.addClass('active');
22229         }
22230         
22231         this.fireEvent('changed', this, state);
22232     },
22233     
22234     onClick : function(e)
22235     {
22236         e.preventDefault();
22237         
22238         if(!this.href.length){
22239             return;
22240         }
22241         
22242         window.location.href = this.href;
22243     },
22244     
22245     startX : 0,
22246     startY : 0,
22247     endX : 0,
22248     endY : 0,
22249     swiping : false,
22250     
22251     onTouchStart : function(e)
22252     {
22253         this.swiping = false;
22254         
22255         this.startX = e.browserEvent.touches[0].clientX;
22256         this.startY = e.browserEvent.touches[0].clientY;
22257     },
22258     
22259     onTouchMove : function(e)
22260     {
22261         this.swiping = true;
22262         
22263         this.endX = e.browserEvent.touches[0].clientX;
22264         this.endY = e.browserEvent.touches[0].clientY;
22265     },
22266     
22267     onTouchEnd : function(e)
22268     {
22269         if(!this.swiping){
22270             this.onClick(e);
22271             return;
22272         }
22273         
22274         var tabGroup = this.parent();
22275         
22276         if(this.endX > this.startX){ // swiping right
22277             tabGroup.showPanelPrev();
22278             return;
22279         }
22280         
22281         if(this.startX > this.endX){ // swiping left
22282             tabGroup.showPanelNext();
22283             return;
22284         }
22285     }
22286     
22287     
22288 });
22289  
22290
22291  
22292
22293  /*
22294  * - LGPL
22295  *
22296  * DateField
22297  * 
22298  */
22299
22300 /**
22301  * @class Roo.bootstrap.DateField
22302  * @extends Roo.bootstrap.Input
22303  * Bootstrap DateField class
22304  * @cfg {Number} weekStart default 0
22305  * @cfg {String} viewMode default empty, (months|years)
22306  * @cfg {String} minViewMode default empty, (months|years)
22307  * @cfg {Number} startDate default -Infinity
22308  * @cfg {Number} endDate default Infinity
22309  * @cfg {Boolean} todayHighlight default false
22310  * @cfg {Boolean} todayBtn default false
22311  * @cfg {Boolean} calendarWeeks default false
22312  * @cfg {Object} daysOfWeekDisabled default empty
22313  * @cfg {Boolean} singleMode default false (true | false)
22314  * 
22315  * @cfg {Boolean} keyboardNavigation default true
22316  * @cfg {String} language default en
22317  * 
22318  * @constructor
22319  * Create a new DateField
22320  * @param {Object} config The config object
22321  */
22322
22323 Roo.bootstrap.DateField = function(config){
22324     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22325      this.addEvents({
22326             /**
22327              * @event show
22328              * Fires when this field show.
22329              * @param {Roo.bootstrap.DateField} this
22330              * @param {Mixed} date The date value
22331              */
22332             show : true,
22333             /**
22334              * @event show
22335              * Fires when this field hide.
22336              * @param {Roo.bootstrap.DateField} this
22337              * @param {Mixed} date The date value
22338              */
22339             hide : true,
22340             /**
22341              * @event select
22342              * Fires when select a date.
22343              * @param {Roo.bootstrap.DateField} this
22344              * @param {Mixed} date The date value
22345              */
22346             select : true,
22347             /**
22348              * @event beforeselect
22349              * Fires when before select a date.
22350              * @param {Roo.bootstrap.DateField} this
22351              * @param {Mixed} date The date value
22352              */
22353             beforeselect : true
22354         });
22355 };
22356
22357 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22358     
22359     /**
22360      * @cfg {String} format
22361      * The default date format string which can be overriden for localization support.  The format must be
22362      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22363      */
22364     format : "m/d/y",
22365     /**
22366      * @cfg {String} altFormats
22367      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22368      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22369      */
22370     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22371     
22372     weekStart : 0,
22373     
22374     viewMode : '',
22375     
22376     minViewMode : '',
22377     
22378     todayHighlight : false,
22379     
22380     todayBtn: false,
22381     
22382     language: 'en',
22383     
22384     keyboardNavigation: true,
22385     
22386     calendarWeeks: false,
22387     
22388     startDate: -Infinity,
22389     
22390     endDate: Infinity,
22391     
22392     daysOfWeekDisabled: [],
22393     
22394     _events: [],
22395     
22396     singleMode : false,
22397     
22398     UTCDate: function()
22399     {
22400         return new Date(Date.UTC.apply(Date, arguments));
22401     },
22402     
22403     UTCToday: function()
22404     {
22405         var today = new Date();
22406         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22407     },
22408     
22409     getDate: function() {
22410             var d = this.getUTCDate();
22411             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22412     },
22413     
22414     getUTCDate: function() {
22415             return this.date;
22416     },
22417     
22418     setDate: function(d) {
22419             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22420     },
22421     
22422     setUTCDate: function(d) {
22423             this.date = d;
22424             this.setValue(this.formatDate(this.date));
22425     },
22426         
22427     onRender: function(ct, position)
22428     {
22429         
22430         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22431         
22432         this.language = this.language || 'en';
22433         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22434         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22435         
22436         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22437         this.format = this.format || 'm/d/y';
22438         this.isInline = false;
22439         this.isInput = true;
22440         this.component = this.el.select('.add-on', true).first() || false;
22441         this.component = (this.component && this.component.length === 0) ? false : this.component;
22442         this.hasInput = this.component && this.inputEl().length;
22443         
22444         if (typeof(this.minViewMode === 'string')) {
22445             switch (this.minViewMode) {
22446                 case 'months':
22447                     this.minViewMode = 1;
22448                     break;
22449                 case 'years':
22450                     this.minViewMode = 2;
22451                     break;
22452                 default:
22453                     this.minViewMode = 0;
22454                     break;
22455             }
22456         }
22457         
22458         if (typeof(this.viewMode === 'string')) {
22459             switch (this.viewMode) {
22460                 case 'months':
22461                     this.viewMode = 1;
22462                     break;
22463                 case 'years':
22464                     this.viewMode = 2;
22465                     break;
22466                 default:
22467                     this.viewMode = 0;
22468                     break;
22469             }
22470         }
22471                 
22472         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22473         
22474 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22475         
22476         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22477         
22478         this.picker().on('mousedown', this.onMousedown, this);
22479         this.picker().on('click', this.onClick, this);
22480         
22481         this.picker().addClass('datepicker-dropdown');
22482         
22483         this.startViewMode = this.viewMode;
22484         
22485         if(this.singleMode){
22486             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22487                 v.setVisibilityMode(Roo.Element.DISPLAY);
22488                 v.hide();
22489             });
22490             
22491             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22492                 v.setStyle('width', '189px');
22493             });
22494         }
22495         
22496         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22497             if(!this.calendarWeeks){
22498                 v.remove();
22499                 return;
22500             }
22501             
22502             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22503             v.attr('colspan', function(i, val){
22504                 return parseInt(val) + 1;
22505             });
22506         });
22507                         
22508         
22509         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22510         
22511         this.setStartDate(this.startDate);
22512         this.setEndDate(this.endDate);
22513         
22514         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22515         
22516         this.fillDow();
22517         this.fillMonths();
22518         this.update();
22519         this.showMode();
22520         
22521         if(this.isInline) {
22522             this.showPopup();
22523         }
22524     },
22525     
22526     picker : function()
22527     {
22528         return this.pickerEl;
22529 //        return this.el.select('.datepicker', true).first();
22530     },
22531     
22532     fillDow: function()
22533     {
22534         var dowCnt = this.weekStart;
22535         
22536         var dow = {
22537             tag: 'tr',
22538             cn: [
22539                 
22540             ]
22541         };
22542         
22543         if(this.calendarWeeks){
22544             dow.cn.push({
22545                 tag: 'th',
22546                 cls: 'cw',
22547                 html: '&nbsp;'
22548             })
22549         }
22550         
22551         while (dowCnt < this.weekStart + 7) {
22552             dow.cn.push({
22553                 tag: 'th',
22554                 cls: 'dow',
22555                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22556             });
22557         }
22558         
22559         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22560     },
22561     
22562     fillMonths: function()
22563     {    
22564         var i = 0;
22565         var months = this.picker().select('>.datepicker-months td', true).first();
22566         
22567         months.dom.innerHTML = '';
22568         
22569         while (i < 12) {
22570             var month = {
22571                 tag: 'span',
22572                 cls: 'month',
22573                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22574             };
22575             
22576             months.createChild(month);
22577         }
22578         
22579     },
22580     
22581     update: function()
22582     {
22583         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;
22584         
22585         if (this.date < this.startDate) {
22586             this.viewDate = new Date(this.startDate);
22587         } else if (this.date > this.endDate) {
22588             this.viewDate = new Date(this.endDate);
22589         } else {
22590             this.viewDate = new Date(this.date);
22591         }
22592         
22593         this.fill();
22594     },
22595     
22596     fill: function() 
22597     {
22598         var d = new Date(this.viewDate),
22599                 year = d.getUTCFullYear(),
22600                 month = d.getUTCMonth(),
22601                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22602                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22603                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22604                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22605                 currentDate = this.date && this.date.valueOf(),
22606                 today = this.UTCToday();
22607         
22608         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22609         
22610 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22611         
22612 //        this.picker.select('>tfoot th.today').
22613 //                                              .text(dates[this.language].today)
22614 //                                              .toggle(this.todayBtn !== false);
22615     
22616         this.updateNavArrows();
22617         this.fillMonths();
22618                                                 
22619         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22620         
22621         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22622          
22623         prevMonth.setUTCDate(day);
22624         
22625         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22626         
22627         var nextMonth = new Date(prevMonth);
22628         
22629         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22630         
22631         nextMonth = nextMonth.valueOf();
22632         
22633         var fillMonths = false;
22634         
22635         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22636         
22637         while(prevMonth.valueOf() <= nextMonth) {
22638             var clsName = '';
22639             
22640             if (prevMonth.getUTCDay() === this.weekStart) {
22641                 if(fillMonths){
22642                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22643                 }
22644                     
22645                 fillMonths = {
22646                     tag: 'tr',
22647                     cn: []
22648                 };
22649                 
22650                 if(this.calendarWeeks){
22651                     // ISO 8601: First week contains first thursday.
22652                     // ISO also states week starts on Monday, but we can be more abstract here.
22653                     var
22654                     // Start of current week: based on weekstart/current date
22655                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22656                     // Thursday of this week
22657                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22658                     // First Thursday of year, year from thursday
22659                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22660                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22661                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22662                     
22663                     fillMonths.cn.push({
22664                         tag: 'td',
22665                         cls: 'cw',
22666                         html: calWeek
22667                     });
22668                 }
22669             }
22670             
22671             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22672                 clsName += ' old';
22673             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22674                 clsName += ' new';
22675             }
22676             if (this.todayHighlight &&
22677                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22678                 prevMonth.getUTCMonth() == today.getMonth() &&
22679                 prevMonth.getUTCDate() == today.getDate()) {
22680                 clsName += ' today';
22681             }
22682             
22683             if (currentDate && prevMonth.valueOf() === currentDate) {
22684                 clsName += ' active';
22685             }
22686             
22687             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22688                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22689                     clsName += ' disabled';
22690             }
22691             
22692             fillMonths.cn.push({
22693                 tag: 'td',
22694                 cls: 'day ' + clsName,
22695                 html: prevMonth.getDate()
22696             });
22697             
22698             prevMonth.setDate(prevMonth.getDate()+1);
22699         }
22700           
22701         var currentYear = this.date && this.date.getUTCFullYear();
22702         var currentMonth = this.date && this.date.getUTCMonth();
22703         
22704         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22705         
22706         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22707             v.removeClass('active');
22708             
22709             if(currentYear === year && k === currentMonth){
22710                 v.addClass('active');
22711             }
22712             
22713             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22714                 v.addClass('disabled');
22715             }
22716             
22717         });
22718         
22719         
22720         year = parseInt(year/10, 10) * 10;
22721         
22722         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22723         
22724         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22725         
22726         year -= 1;
22727         for (var i = -1; i < 11; i++) {
22728             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22729                 tag: 'span',
22730                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22731                 html: year
22732             });
22733             
22734             year += 1;
22735         }
22736     },
22737     
22738     showMode: function(dir) 
22739     {
22740         if (dir) {
22741             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22742         }
22743         
22744         Roo.each(this.picker().select('>div',true).elements, function(v){
22745             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22746             v.hide();
22747         });
22748         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22749     },
22750     
22751     place: function()
22752     {
22753         if(this.isInline) {
22754             return;
22755         }
22756         
22757         this.picker().removeClass(['bottom', 'top']);
22758         
22759         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22760             /*
22761              * place to the top of element!
22762              *
22763              */
22764             
22765             this.picker().addClass('top');
22766             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22767             
22768             return;
22769         }
22770         
22771         this.picker().addClass('bottom');
22772         
22773         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22774     },
22775     
22776     parseDate : function(value)
22777     {
22778         if(!value || value instanceof Date){
22779             return value;
22780         }
22781         var v = Date.parseDate(value, this.format);
22782         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22783             v = Date.parseDate(value, 'Y-m-d');
22784         }
22785         if(!v && this.altFormats){
22786             if(!this.altFormatsArray){
22787                 this.altFormatsArray = this.altFormats.split("|");
22788             }
22789             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22790                 v = Date.parseDate(value, this.altFormatsArray[i]);
22791             }
22792         }
22793         return v;
22794     },
22795     
22796     formatDate : function(date, fmt)
22797     {   
22798         return (!date || !(date instanceof Date)) ?
22799         date : date.dateFormat(fmt || this.format);
22800     },
22801     
22802     onFocus : function()
22803     {
22804         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22805         this.showPopup();
22806     },
22807     
22808     onBlur : function()
22809     {
22810         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22811         
22812         var d = this.inputEl().getValue();
22813         
22814         this.setValue(d);
22815                 
22816         this.hidePopup();
22817     },
22818     
22819     showPopup : function()
22820     {
22821         this.picker().show();
22822         this.update();
22823         this.place();
22824         
22825         this.fireEvent('showpopup', this, this.date);
22826     },
22827     
22828     hidePopup : function()
22829     {
22830         if(this.isInline) {
22831             return;
22832         }
22833         this.picker().hide();
22834         this.viewMode = this.startViewMode;
22835         this.showMode();
22836         
22837         this.fireEvent('hidepopup', this, this.date);
22838         
22839     },
22840     
22841     onMousedown: function(e)
22842     {
22843         e.stopPropagation();
22844         e.preventDefault();
22845     },
22846     
22847     keyup: function(e)
22848     {
22849         Roo.bootstrap.DateField.superclass.keyup.call(this);
22850         this.update();
22851     },
22852
22853     setValue: function(v)
22854     {
22855         if(this.fireEvent('beforeselect', this, v) !== false){
22856             var d = new Date(this.parseDate(v) ).clearTime();
22857         
22858             if(isNaN(d.getTime())){
22859                 this.date = this.viewDate = '';
22860                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22861                 return;
22862             }
22863
22864             v = this.formatDate(d);
22865
22866             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22867
22868             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22869
22870             this.update();
22871
22872             this.fireEvent('select', this, this.date);
22873         }
22874     },
22875     
22876     getValue: function()
22877     {
22878         return this.formatDate(this.date);
22879     },
22880     
22881     fireKey: function(e)
22882     {
22883         if (!this.picker().isVisible()){
22884             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22885                 this.showPopup();
22886             }
22887             return;
22888         }
22889         
22890         var dateChanged = false,
22891         dir, day, month,
22892         newDate, newViewDate;
22893         
22894         switch(e.keyCode){
22895             case 27: // escape
22896                 this.hidePopup();
22897                 e.preventDefault();
22898                 break;
22899             case 37: // left
22900             case 39: // right
22901                 if (!this.keyboardNavigation) {
22902                     break;
22903                 }
22904                 dir = e.keyCode == 37 ? -1 : 1;
22905                 
22906                 if (e.ctrlKey){
22907                     newDate = this.moveYear(this.date, dir);
22908                     newViewDate = this.moveYear(this.viewDate, dir);
22909                 } else if (e.shiftKey){
22910                     newDate = this.moveMonth(this.date, dir);
22911                     newViewDate = this.moveMonth(this.viewDate, dir);
22912                 } else {
22913                     newDate = new Date(this.date);
22914                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22915                     newViewDate = new Date(this.viewDate);
22916                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22917                 }
22918                 if (this.dateWithinRange(newDate)){
22919                     this.date = newDate;
22920                     this.viewDate = newViewDate;
22921                     this.setValue(this.formatDate(this.date));
22922 //                    this.update();
22923                     e.preventDefault();
22924                     dateChanged = true;
22925                 }
22926                 break;
22927             case 38: // up
22928             case 40: // down
22929                 if (!this.keyboardNavigation) {
22930                     break;
22931                 }
22932                 dir = e.keyCode == 38 ? -1 : 1;
22933                 if (e.ctrlKey){
22934                     newDate = this.moveYear(this.date, dir);
22935                     newViewDate = this.moveYear(this.viewDate, dir);
22936                 } else if (e.shiftKey){
22937                     newDate = this.moveMonth(this.date, dir);
22938                     newViewDate = this.moveMonth(this.viewDate, dir);
22939                 } else {
22940                     newDate = new Date(this.date);
22941                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22942                     newViewDate = new Date(this.viewDate);
22943                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22944                 }
22945                 if (this.dateWithinRange(newDate)){
22946                     this.date = newDate;
22947                     this.viewDate = newViewDate;
22948                     this.setValue(this.formatDate(this.date));
22949 //                    this.update();
22950                     e.preventDefault();
22951                     dateChanged = true;
22952                 }
22953                 break;
22954             case 13: // enter
22955                 this.setValue(this.formatDate(this.date));
22956                 this.hidePopup();
22957                 e.preventDefault();
22958                 break;
22959             case 9: // tab
22960                 this.setValue(this.formatDate(this.date));
22961                 this.hidePopup();
22962                 break;
22963             case 16: // shift
22964             case 17: // ctrl
22965             case 18: // alt
22966                 break;
22967             default :
22968                 this.hidePopup();
22969                 
22970         }
22971     },
22972     
22973     
22974     onClick: function(e) 
22975     {
22976         e.stopPropagation();
22977         e.preventDefault();
22978         
22979         var target = e.getTarget();
22980         
22981         if(target.nodeName.toLowerCase() === 'i'){
22982             target = Roo.get(target).dom.parentNode;
22983         }
22984         
22985         var nodeName = target.nodeName;
22986         var className = target.className;
22987         var html = target.innerHTML;
22988         //Roo.log(nodeName);
22989         
22990         switch(nodeName.toLowerCase()) {
22991             case 'th':
22992                 switch(className) {
22993                     case 'switch':
22994                         this.showMode(1);
22995                         break;
22996                     case 'prev':
22997                     case 'next':
22998                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22999                         switch(this.viewMode){
23000                                 case 0:
23001                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23002                                         break;
23003                                 case 1:
23004                                 case 2:
23005                                         this.viewDate = this.moveYear(this.viewDate, dir);
23006                                         break;
23007                         }
23008                         this.fill();
23009                         break;
23010                     case 'today':
23011                         var date = new Date();
23012                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23013 //                        this.fill()
23014                         this.setValue(this.formatDate(this.date));
23015                         
23016                         this.hidePopup();
23017                         break;
23018                 }
23019                 break;
23020             case 'span':
23021                 if (className.indexOf('disabled') < 0) {
23022                 if (!this.viewDate) {
23023                     this.viewDate = new Date();
23024                 }
23025                 this.viewDate.setUTCDate(1);
23026                     if (className.indexOf('month') > -1) {
23027                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23028                     } else {
23029                         var year = parseInt(html, 10) || 0;
23030                         this.viewDate.setUTCFullYear(year);
23031                         
23032                     }
23033                     
23034                     if(this.singleMode){
23035                         this.setValue(this.formatDate(this.viewDate));
23036                         this.hidePopup();
23037                         return;
23038                     }
23039                     
23040                     this.showMode(-1);
23041                     this.fill();
23042                 }
23043                 break;
23044                 
23045             case 'td':
23046                 //Roo.log(className);
23047                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23048                     var day = parseInt(html, 10) || 1;
23049                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23050                         month = (this.viewDate || new Date()).getUTCMonth();
23051
23052                     if (className.indexOf('old') > -1) {
23053                         if(month === 0 ){
23054                             month = 11;
23055                             year -= 1;
23056                         }else{
23057                             month -= 1;
23058                         }
23059                     } else if (className.indexOf('new') > -1) {
23060                         if (month == 11) {
23061                             month = 0;
23062                             year += 1;
23063                         } else {
23064                             month += 1;
23065                         }
23066                     }
23067                     //Roo.log([year,month,day]);
23068                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23069                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23070 //                    this.fill();
23071                     //Roo.log(this.formatDate(this.date));
23072                     this.setValue(this.formatDate(this.date));
23073                     this.hidePopup();
23074                 }
23075                 break;
23076         }
23077     },
23078     
23079     setStartDate: function(startDate)
23080     {
23081         this.startDate = startDate || -Infinity;
23082         if (this.startDate !== -Infinity) {
23083             this.startDate = this.parseDate(this.startDate);
23084         }
23085         this.update();
23086         this.updateNavArrows();
23087     },
23088
23089     setEndDate: function(endDate)
23090     {
23091         this.endDate = endDate || Infinity;
23092         if (this.endDate !== Infinity) {
23093             this.endDate = this.parseDate(this.endDate);
23094         }
23095         this.update();
23096         this.updateNavArrows();
23097     },
23098     
23099     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23100     {
23101         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23102         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23103             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23104         }
23105         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23106             return parseInt(d, 10);
23107         });
23108         this.update();
23109         this.updateNavArrows();
23110     },
23111     
23112     updateNavArrows: function() 
23113     {
23114         if(this.singleMode){
23115             return;
23116         }
23117         
23118         var d = new Date(this.viewDate),
23119         year = d.getUTCFullYear(),
23120         month = d.getUTCMonth();
23121         
23122         Roo.each(this.picker().select('.prev', true).elements, function(v){
23123             v.show();
23124             switch (this.viewMode) {
23125                 case 0:
23126
23127                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23128                         v.hide();
23129                     }
23130                     break;
23131                 case 1:
23132                 case 2:
23133                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23134                         v.hide();
23135                     }
23136                     break;
23137             }
23138         });
23139         
23140         Roo.each(this.picker().select('.next', true).elements, function(v){
23141             v.show();
23142             switch (this.viewMode) {
23143                 case 0:
23144
23145                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23146                         v.hide();
23147                     }
23148                     break;
23149                 case 1:
23150                 case 2:
23151                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23152                         v.hide();
23153                     }
23154                     break;
23155             }
23156         })
23157     },
23158     
23159     moveMonth: function(date, dir)
23160     {
23161         if (!dir) {
23162             return date;
23163         }
23164         var new_date = new Date(date.valueOf()),
23165         day = new_date.getUTCDate(),
23166         month = new_date.getUTCMonth(),
23167         mag = Math.abs(dir),
23168         new_month, test;
23169         dir = dir > 0 ? 1 : -1;
23170         if (mag == 1){
23171             test = dir == -1
23172             // If going back one month, make sure month is not current month
23173             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23174             ? function(){
23175                 return new_date.getUTCMonth() == month;
23176             }
23177             // If going forward one month, make sure month is as expected
23178             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23179             : function(){
23180                 return new_date.getUTCMonth() != new_month;
23181             };
23182             new_month = month + dir;
23183             new_date.setUTCMonth(new_month);
23184             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23185             if (new_month < 0 || new_month > 11) {
23186                 new_month = (new_month + 12) % 12;
23187             }
23188         } else {
23189             // For magnitudes >1, move one month at a time...
23190             for (var i=0; i<mag; i++) {
23191                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23192                 new_date = this.moveMonth(new_date, dir);
23193             }
23194             // ...then reset the day, keeping it in the new month
23195             new_month = new_date.getUTCMonth();
23196             new_date.setUTCDate(day);
23197             test = function(){
23198                 return new_month != new_date.getUTCMonth();
23199             };
23200         }
23201         // Common date-resetting loop -- if date is beyond end of month, make it
23202         // end of month
23203         while (test()){
23204             new_date.setUTCDate(--day);
23205             new_date.setUTCMonth(new_month);
23206         }
23207         return new_date;
23208     },
23209
23210     moveYear: function(date, dir)
23211     {
23212         return this.moveMonth(date, dir*12);
23213     },
23214
23215     dateWithinRange: function(date)
23216     {
23217         return date >= this.startDate && date <= this.endDate;
23218     },
23219
23220     
23221     remove: function() 
23222     {
23223         this.picker().remove();
23224     },
23225     
23226     validateValue : function(value)
23227     {
23228         if(this.getVisibilityEl().hasClass('hidden')){
23229             return true;
23230         }
23231         
23232         if(value.length < 1)  {
23233             if(this.allowBlank){
23234                 return true;
23235             }
23236             return false;
23237         }
23238         
23239         if(value.length < this.minLength){
23240             return false;
23241         }
23242         if(value.length > this.maxLength){
23243             return false;
23244         }
23245         if(this.vtype){
23246             var vt = Roo.form.VTypes;
23247             if(!vt[this.vtype](value, this)){
23248                 return false;
23249             }
23250         }
23251         if(typeof this.validator == "function"){
23252             var msg = this.validator(value);
23253             if(msg !== true){
23254                 return false;
23255             }
23256         }
23257         
23258         if(this.regex && !this.regex.test(value)){
23259             return false;
23260         }
23261         
23262         if(typeof(this.parseDate(value)) == 'undefined'){
23263             return false;
23264         }
23265         
23266         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23267             return false;
23268         }      
23269         
23270         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23271             return false;
23272         } 
23273         
23274         
23275         return true;
23276     },
23277     
23278     reset : function()
23279     {
23280         this.date = this.viewDate = '';
23281         
23282         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23283     }
23284    
23285 });
23286
23287 Roo.apply(Roo.bootstrap.DateField,  {
23288     
23289     head : {
23290         tag: 'thead',
23291         cn: [
23292         {
23293             tag: 'tr',
23294             cn: [
23295             {
23296                 tag: 'th',
23297                 cls: 'prev',
23298                 html: '<i class="fa fa-arrow-left"/>'
23299             },
23300             {
23301                 tag: 'th',
23302                 cls: 'switch',
23303                 colspan: '5'
23304             },
23305             {
23306                 tag: 'th',
23307                 cls: 'next',
23308                 html: '<i class="fa fa-arrow-right"/>'
23309             }
23310
23311             ]
23312         }
23313         ]
23314     },
23315     
23316     content : {
23317         tag: 'tbody',
23318         cn: [
23319         {
23320             tag: 'tr',
23321             cn: [
23322             {
23323                 tag: 'td',
23324                 colspan: '7'
23325             }
23326             ]
23327         }
23328         ]
23329     },
23330     
23331     footer : {
23332         tag: 'tfoot',
23333         cn: [
23334         {
23335             tag: 'tr',
23336             cn: [
23337             {
23338                 tag: 'th',
23339                 colspan: '7',
23340                 cls: 'today'
23341             }
23342                     
23343             ]
23344         }
23345         ]
23346     },
23347     
23348     dates:{
23349         en: {
23350             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23351             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23352             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23353             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23354             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23355             today: "Today"
23356         }
23357     },
23358     
23359     modes: [
23360     {
23361         clsName: 'days',
23362         navFnc: 'Month',
23363         navStep: 1
23364     },
23365     {
23366         clsName: 'months',
23367         navFnc: 'FullYear',
23368         navStep: 1
23369     },
23370     {
23371         clsName: 'years',
23372         navFnc: 'FullYear',
23373         navStep: 10
23374     }]
23375 });
23376
23377 Roo.apply(Roo.bootstrap.DateField,  {
23378   
23379     template : {
23380         tag: 'div',
23381         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23382         cn: [
23383         {
23384             tag: 'div',
23385             cls: 'datepicker-days',
23386             cn: [
23387             {
23388                 tag: 'table',
23389                 cls: 'table-condensed',
23390                 cn:[
23391                 Roo.bootstrap.DateField.head,
23392                 {
23393                     tag: 'tbody'
23394                 },
23395                 Roo.bootstrap.DateField.footer
23396                 ]
23397             }
23398             ]
23399         },
23400         {
23401             tag: 'div',
23402             cls: 'datepicker-months',
23403             cn: [
23404             {
23405                 tag: 'table',
23406                 cls: 'table-condensed',
23407                 cn:[
23408                 Roo.bootstrap.DateField.head,
23409                 Roo.bootstrap.DateField.content,
23410                 Roo.bootstrap.DateField.footer
23411                 ]
23412             }
23413             ]
23414         },
23415         {
23416             tag: 'div',
23417             cls: 'datepicker-years',
23418             cn: [
23419             {
23420                 tag: 'table',
23421                 cls: 'table-condensed',
23422                 cn:[
23423                 Roo.bootstrap.DateField.head,
23424                 Roo.bootstrap.DateField.content,
23425                 Roo.bootstrap.DateField.footer
23426                 ]
23427             }
23428             ]
23429         }
23430         ]
23431     }
23432 });
23433
23434  
23435
23436  /*
23437  * - LGPL
23438  *
23439  * TimeField
23440  * 
23441  */
23442
23443 /**
23444  * @class Roo.bootstrap.TimeField
23445  * @extends Roo.bootstrap.Input
23446  * Bootstrap DateField class
23447  * 
23448  * 
23449  * @constructor
23450  * Create a new TimeField
23451  * @param {Object} config The config object
23452  */
23453
23454 Roo.bootstrap.TimeField = function(config){
23455     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23456     this.addEvents({
23457             /**
23458              * @event show
23459              * Fires when this field show.
23460              * @param {Roo.bootstrap.DateField} thisthis
23461              * @param {Mixed} date The date value
23462              */
23463             show : true,
23464             /**
23465              * @event show
23466              * Fires when this field hide.
23467              * @param {Roo.bootstrap.DateField} this
23468              * @param {Mixed} date The date value
23469              */
23470             hide : true,
23471             /**
23472              * @event select
23473              * Fires when select a date.
23474              * @param {Roo.bootstrap.DateField} this
23475              * @param {Mixed} date The date value
23476              */
23477             select : true
23478         });
23479 };
23480
23481 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23482     
23483     /**
23484      * @cfg {String} format
23485      * The default time format string which can be overriden for localization support.  The format must be
23486      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23487      */
23488     format : "H:i",
23489
23490     getAutoCreate : function()
23491     {
23492         this.after = '<i class="fa far fa-clock"></i>';
23493         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23494         
23495          
23496     },
23497     onRender: function(ct, position)
23498     {
23499         
23500         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23501                 
23502         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23503         
23504         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23505         
23506         this.pop = this.picker().select('>.datepicker-time',true).first();
23507         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23508         
23509         this.picker().on('mousedown', this.onMousedown, this);
23510         this.picker().on('click', this.onClick, this);
23511         
23512         this.picker().addClass('datepicker-dropdown');
23513     
23514         this.fillTime();
23515         this.update();
23516             
23517         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23518         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23519         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23520         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23521         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23522         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23523
23524     },
23525     
23526     fireKey: function(e){
23527         if (!this.picker().isVisible()){
23528             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23529                 this.show();
23530             }
23531             return;
23532         }
23533
23534         e.preventDefault();
23535         
23536         switch(e.keyCode){
23537             case 27: // escape
23538                 this.hide();
23539                 break;
23540             case 37: // left
23541             case 39: // right
23542                 this.onTogglePeriod();
23543                 break;
23544             case 38: // up
23545                 this.onIncrementMinutes();
23546                 break;
23547             case 40: // down
23548                 this.onDecrementMinutes();
23549                 break;
23550             case 13: // enter
23551             case 9: // tab
23552                 this.setTime();
23553                 break;
23554         }
23555     },
23556     
23557     onClick: function(e) {
23558         e.stopPropagation();
23559         e.preventDefault();
23560     },
23561     
23562     picker : function()
23563     {
23564         return this.pickerEl;
23565     },
23566     
23567     fillTime: function()
23568     {    
23569         var time = this.pop.select('tbody', true).first();
23570         
23571         time.dom.innerHTML = '';
23572         
23573         time.createChild({
23574             tag: 'tr',
23575             cn: [
23576                 {
23577                     tag: 'td',
23578                     cn: [
23579                         {
23580                             tag: 'a',
23581                             href: '#',
23582                             cls: 'btn',
23583                             cn: [
23584                                 {
23585                                     tag: 'i',
23586                                     cls: 'hours-up fa fas fa-chevron-up'
23587                                 }
23588                             ]
23589                         } 
23590                     ]
23591                 },
23592                 {
23593                     tag: 'td',
23594                     cls: 'separator'
23595                 },
23596                 {
23597                     tag: 'td',
23598                     cn: [
23599                         {
23600                             tag: 'a',
23601                             href: '#',
23602                             cls: 'btn',
23603                             cn: [
23604                                 {
23605                                     tag: 'i',
23606                                     cls: 'minutes-up fa fas fa-chevron-up'
23607                                 }
23608                             ]
23609                         }
23610                     ]
23611                 },
23612                 {
23613                     tag: 'td',
23614                     cls: 'separator'
23615                 }
23616             ]
23617         });
23618         
23619         time.createChild({
23620             tag: 'tr',
23621             cn: [
23622                 {
23623                     tag: 'td',
23624                     cn: [
23625                         {
23626                             tag: 'span',
23627                             cls: 'timepicker-hour',
23628                             html: '00'
23629                         }  
23630                     ]
23631                 },
23632                 {
23633                     tag: 'td',
23634                     cls: 'separator',
23635                     html: ':'
23636                 },
23637                 {
23638                     tag: 'td',
23639                     cn: [
23640                         {
23641                             tag: 'span',
23642                             cls: 'timepicker-minute',
23643                             html: '00'
23644                         }  
23645                     ]
23646                 },
23647                 {
23648                     tag: 'td',
23649                     cls: 'separator'
23650                 },
23651                 {
23652                     tag: 'td',
23653                     cn: [
23654                         {
23655                             tag: 'button',
23656                             type: 'button',
23657                             cls: 'btn btn-primary period',
23658                             html: 'AM'
23659                             
23660                         }
23661                     ]
23662                 }
23663             ]
23664         });
23665         
23666         time.createChild({
23667             tag: 'tr',
23668             cn: [
23669                 {
23670                     tag: 'td',
23671                     cn: [
23672                         {
23673                             tag: 'a',
23674                             href: '#',
23675                             cls: 'btn',
23676                             cn: [
23677                                 {
23678                                     tag: 'span',
23679                                     cls: 'hours-down fa fas fa-chevron-down'
23680                                 }
23681                             ]
23682                         }
23683                     ]
23684                 },
23685                 {
23686                     tag: 'td',
23687                     cls: 'separator'
23688                 },
23689                 {
23690                     tag: 'td',
23691                     cn: [
23692                         {
23693                             tag: 'a',
23694                             href: '#',
23695                             cls: 'btn',
23696                             cn: [
23697                                 {
23698                                     tag: 'span',
23699                                     cls: 'minutes-down fa fas fa-chevron-down'
23700                                 }
23701                             ]
23702                         }
23703                     ]
23704                 },
23705                 {
23706                     tag: 'td',
23707                     cls: 'separator'
23708                 }
23709             ]
23710         });
23711         
23712     },
23713     
23714     update: function()
23715     {
23716         
23717         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23718         
23719         this.fill();
23720     },
23721     
23722     fill: function() 
23723     {
23724         var hours = this.time.getHours();
23725         var minutes = this.time.getMinutes();
23726         var period = 'AM';
23727         
23728         if(hours > 11){
23729             period = 'PM';
23730         }
23731         
23732         if(hours == 0){
23733             hours = 12;
23734         }
23735         
23736         
23737         if(hours > 12){
23738             hours = hours - 12;
23739         }
23740         
23741         if(hours < 10){
23742             hours = '0' + hours;
23743         }
23744         
23745         if(minutes < 10){
23746             minutes = '0' + minutes;
23747         }
23748         
23749         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23750         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23751         this.pop.select('button', true).first().dom.innerHTML = period;
23752         
23753     },
23754     
23755     place: function()
23756     {   
23757         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23758         
23759         var cls = ['bottom'];
23760         
23761         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23762             cls.pop();
23763             cls.push('top');
23764         }
23765         
23766         cls.push('right');
23767         
23768         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23769             cls.pop();
23770             cls.push('left');
23771         }
23772         //this.picker().setXY(20000,20000);
23773         this.picker().addClass(cls.join('-'));
23774         
23775         var _this = this;
23776         
23777         Roo.each(cls, function(c){
23778             if(c == 'bottom'){
23779                 (function() {
23780                  //  
23781                 }).defer(200);
23782                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23783                 //_this.picker().setTop(_this.inputEl().getHeight());
23784                 return;
23785             }
23786             if(c == 'top'){
23787                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23788                 
23789                 //_this.picker().setTop(0 - _this.picker().getHeight());
23790                 return;
23791             }
23792             /*
23793             if(c == 'left'){
23794                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23795                 return;
23796             }
23797             if(c == 'right'){
23798                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23799                 return;
23800             }
23801             */
23802         });
23803         
23804     },
23805   
23806     onFocus : function()
23807     {
23808         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23809         this.show();
23810     },
23811     
23812     onBlur : function()
23813     {
23814         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23815         this.hide();
23816     },
23817     
23818     show : function()
23819     {
23820         this.picker().show();
23821         this.pop.show();
23822         this.update();
23823         this.place();
23824         
23825         this.fireEvent('show', this, this.date);
23826     },
23827     
23828     hide : function()
23829     {
23830         this.picker().hide();
23831         this.pop.hide();
23832         
23833         this.fireEvent('hide', this, this.date);
23834     },
23835     
23836     setTime : function()
23837     {
23838         this.hide();
23839         this.setValue(this.time.format(this.format));
23840         
23841         this.fireEvent('select', this, this.date);
23842         
23843         
23844     },
23845     
23846     onMousedown: function(e){
23847         e.stopPropagation();
23848         e.preventDefault();
23849     },
23850     
23851     onIncrementHours: function()
23852     {
23853         Roo.log('onIncrementHours');
23854         this.time = this.time.add(Date.HOUR, 1);
23855         this.update();
23856         
23857     },
23858     
23859     onDecrementHours: function()
23860     {
23861         Roo.log('onDecrementHours');
23862         this.time = this.time.add(Date.HOUR, -1);
23863         this.update();
23864     },
23865     
23866     onIncrementMinutes: function()
23867     {
23868         Roo.log('onIncrementMinutes');
23869         this.time = this.time.add(Date.MINUTE, 1);
23870         this.update();
23871     },
23872     
23873     onDecrementMinutes: function()
23874     {
23875         Roo.log('onDecrementMinutes');
23876         this.time = this.time.add(Date.MINUTE, -1);
23877         this.update();
23878     },
23879     
23880     onTogglePeriod: function()
23881     {
23882         Roo.log('onTogglePeriod');
23883         this.time = this.time.add(Date.HOUR, 12);
23884         this.update();
23885     }
23886     
23887    
23888 });
23889  
23890
23891 Roo.apply(Roo.bootstrap.TimeField,  {
23892   
23893     template : {
23894         tag: 'div',
23895         cls: 'datepicker dropdown-menu',
23896         cn: [
23897             {
23898                 tag: 'div',
23899                 cls: 'datepicker-time',
23900                 cn: [
23901                 {
23902                     tag: 'table',
23903                     cls: 'table-condensed',
23904                     cn:[
23905                         {
23906                             tag: 'tbody',
23907                             cn: [
23908                                 {
23909                                     tag: 'tr',
23910                                     cn: [
23911                                     {
23912                                         tag: 'td',
23913                                         colspan: '7'
23914                                     }
23915                                     ]
23916                                 }
23917                             ]
23918                         },
23919                         {
23920                             tag: 'tfoot',
23921                             cn: [
23922                                 {
23923                                     tag: 'tr',
23924                                     cn: [
23925                                     {
23926                                         tag: 'th',
23927                                         colspan: '7',
23928                                         cls: '',
23929                                         cn: [
23930                                             {
23931                                                 tag: 'button',
23932                                                 cls: 'btn btn-info ok',
23933                                                 html: 'OK'
23934                                             }
23935                                         ]
23936                                     }
23937                     
23938                                     ]
23939                                 }
23940                             ]
23941                         }
23942                     ]
23943                 }
23944                 ]
23945             }
23946         ]
23947     }
23948 });
23949
23950  
23951
23952  /*
23953  * - LGPL
23954  *
23955  * MonthField
23956  * 
23957  */
23958
23959 /**
23960  * @class Roo.bootstrap.MonthField
23961  * @extends Roo.bootstrap.Input
23962  * Bootstrap MonthField class
23963  * 
23964  * @cfg {String} language default en
23965  * 
23966  * @constructor
23967  * Create a new MonthField
23968  * @param {Object} config The config object
23969  */
23970
23971 Roo.bootstrap.MonthField = function(config){
23972     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23973     
23974     this.addEvents({
23975         /**
23976          * @event show
23977          * Fires when this field show.
23978          * @param {Roo.bootstrap.MonthField} this
23979          * @param {Mixed} date The date value
23980          */
23981         show : true,
23982         /**
23983          * @event show
23984          * Fires when this field hide.
23985          * @param {Roo.bootstrap.MonthField} this
23986          * @param {Mixed} date The date value
23987          */
23988         hide : true,
23989         /**
23990          * @event select
23991          * Fires when select a date.
23992          * @param {Roo.bootstrap.MonthField} this
23993          * @param {String} oldvalue The old value
23994          * @param {String} newvalue The new value
23995          */
23996         select : true
23997     });
23998 };
23999
24000 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24001     
24002     onRender: function(ct, position)
24003     {
24004         
24005         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24006         
24007         this.language = this.language || 'en';
24008         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24009         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24010         
24011         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24012         this.isInline = false;
24013         this.isInput = true;
24014         this.component = this.el.select('.add-on', true).first() || false;
24015         this.component = (this.component && this.component.length === 0) ? false : this.component;
24016         this.hasInput = this.component && this.inputEL().length;
24017         
24018         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24019         
24020         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24021         
24022         this.picker().on('mousedown', this.onMousedown, this);
24023         this.picker().on('click', this.onClick, this);
24024         
24025         this.picker().addClass('datepicker-dropdown');
24026         
24027         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24028             v.setStyle('width', '189px');
24029         });
24030         
24031         this.fillMonths();
24032         
24033         this.update();
24034         
24035         if(this.isInline) {
24036             this.show();
24037         }
24038         
24039     },
24040     
24041     setValue: function(v, suppressEvent)
24042     {   
24043         var o = this.getValue();
24044         
24045         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24046         
24047         this.update();
24048
24049         if(suppressEvent !== true){
24050             this.fireEvent('select', this, o, v);
24051         }
24052         
24053     },
24054     
24055     getValue: function()
24056     {
24057         return this.value;
24058     },
24059     
24060     onClick: function(e) 
24061     {
24062         e.stopPropagation();
24063         e.preventDefault();
24064         
24065         var target = e.getTarget();
24066         
24067         if(target.nodeName.toLowerCase() === 'i'){
24068             target = Roo.get(target).dom.parentNode;
24069         }
24070         
24071         var nodeName = target.nodeName;
24072         var className = target.className;
24073         var html = target.innerHTML;
24074         
24075         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24076             return;
24077         }
24078         
24079         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24080         
24081         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24082         
24083         this.hide();
24084                         
24085     },
24086     
24087     picker : function()
24088     {
24089         return this.pickerEl;
24090     },
24091     
24092     fillMonths: function()
24093     {    
24094         var i = 0;
24095         var months = this.picker().select('>.datepicker-months td', true).first();
24096         
24097         months.dom.innerHTML = '';
24098         
24099         while (i < 12) {
24100             var month = {
24101                 tag: 'span',
24102                 cls: 'month',
24103                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24104             };
24105             
24106             months.createChild(month);
24107         }
24108         
24109     },
24110     
24111     update: function()
24112     {
24113         var _this = this;
24114         
24115         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24116             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24117         }
24118         
24119         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24120             e.removeClass('active');
24121             
24122             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24123                 e.addClass('active');
24124             }
24125         })
24126     },
24127     
24128     place: function()
24129     {
24130         if(this.isInline) {
24131             return;
24132         }
24133         
24134         this.picker().removeClass(['bottom', 'top']);
24135         
24136         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24137             /*
24138              * place to the top of element!
24139              *
24140              */
24141             
24142             this.picker().addClass('top');
24143             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24144             
24145             return;
24146         }
24147         
24148         this.picker().addClass('bottom');
24149         
24150         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24151     },
24152     
24153     onFocus : function()
24154     {
24155         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24156         this.show();
24157     },
24158     
24159     onBlur : function()
24160     {
24161         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24162         
24163         var d = this.inputEl().getValue();
24164         
24165         this.setValue(d);
24166                 
24167         this.hide();
24168     },
24169     
24170     show : function()
24171     {
24172         this.picker().show();
24173         this.picker().select('>.datepicker-months', true).first().show();
24174         this.update();
24175         this.place();
24176         
24177         this.fireEvent('show', this, this.date);
24178     },
24179     
24180     hide : function()
24181     {
24182         if(this.isInline) {
24183             return;
24184         }
24185         this.picker().hide();
24186         this.fireEvent('hide', this, this.date);
24187         
24188     },
24189     
24190     onMousedown: function(e)
24191     {
24192         e.stopPropagation();
24193         e.preventDefault();
24194     },
24195     
24196     keyup: function(e)
24197     {
24198         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24199         this.update();
24200     },
24201
24202     fireKey: function(e)
24203     {
24204         if (!this.picker().isVisible()){
24205             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24206                 this.show();
24207             }
24208             return;
24209         }
24210         
24211         var dir;
24212         
24213         switch(e.keyCode){
24214             case 27: // escape
24215                 this.hide();
24216                 e.preventDefault();
24217                 break;
24218             case 37: // left
24219             case 39: // right
24220                 dir = e.keyCode == 37 ? -1 : 1;
24221                 
24222                 this.vIndex = this.vIndex + dir;
24223                 
24224                 if(this.vIndex < 0){
24225                     this.vIndex = 0;
24226                 }
24227                 
24228                 if(this.vIndex > 11){
24229                     this.vIndex = 11;
24230                 }
24231                 
24232                 if(isNaN(this.vIndex)){
24233                     this.vIndex = 0;
24234                 }
24235                 
24236                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24237                 
24238                 break;
24239             case 38: // up
24240             case 40: // down
24241                 
24242                 dir = e.keyCode == 38 ? -1 : 1;
24243                 
24244                 this.vIndex = this.vIndex + dir * 4;
24245                 
24246                 if(this.vIndex < 0){
24247                     this.vIndex = 0;
24248                 }
24249                 
24250                 if(this.vIndex > 11){
24251                     this.vIndex = 11;
24252                 }
24253                 
24254                 if(isNaN(this.vIndex)){
24255                     this.vIndex = 0;
24256                 }
24257                 
24258                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24259                 break;
24260                 
24261             case 13: // enter
24262                 
24263                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24264                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24265                 }
24266                 
24267                 this.hide();
24268                 e.preventDefault();
24269                 break;
24270             case 9: // tab
24271                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24272                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24273                 }
24274                 this.hide();
24275                 break;
24276             case 16: // shift
24277             case 17: // ctrl
24278             case 18: // alt
24279                 break;
24280             default :
24281                 this.hide();
24282                 
24283         }
24284     },
24285     
24286     remove: function() 
24287     {
24288         this.picker().remove();
24289     }
24290    
24291 });
24292
24293 Roo.apply(Roo.bootstrap.MonthField,  {
24294     
24295     content : {
24296         tag: 'tbody',
24297         cn: [
24298         {
24299             tag: 'tr',
24300             cn: [
24301             {
24302                 tag: 'td',
24303                 colspan: '7'
24304             }
24305             ]
24306         }
24307         ]
24308     },
24309     
24310     dates:{
24311         en: {
24312             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24313             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24314         }
24315     }
24316 });
24317
24318 Roo.apply(Roo.bootstrap.MonthField,  {
24319   
24320     template : {
24321         tag: 'div',
24322         cls: 'datepicker dropdown-menu roo-dynamic',
24323         cn: [
24324             {
24325                 tag: 'div',
24326                 cls: 'datepicker-months',
24327                 cn: [
24328                 {
24329                     tag: 'table',
24330                     cls: 'table-condensed',
24331                     cn:[
24332                         Roo.bootstrap.DateField.content
24333                     ]
24334                 }
24335                 ]
24336             }
24337         ]
24338     }
24339 });
24340
24341  
24342
24343  
24344  /*
24345  * - LGPL
24346  *
24347  * CheckBox
24348  * 
24349  */
24350
24351 /**
24352  * @class Roo.bootstrap.CheckBox
24353  * @extends Roo.bootstrap.Input
24354  * Bootstrap CheckBox class
24355  * 
24356  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24357  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24358  * @cfg {String} boxLabel The text that appears beside the checkbox
24359  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24360  * @cfg {Boolean} checked initnal the element
24361  * @cfg {Boolean} inline inline the element (default false)
24362  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24363  * @cfg {String} tooltip label tooltip
24364  * 
24365  * @constructor
24366  * Create a new CheckBox
24367  * @param {Object} config The config object
24368  */
24369
24370 Roo.bootstrap.CheckBox = function(config){
24371     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24372    
24373     this.addEvents({
24374         /**
24375         * @event check
24376         * Fires when the element is checked or unchecked.
24377         * @param {Roo.bootstrap.CheckBox} this This input
24378         * @param {Boolean} checked The new checked value
24379         */
24380        check : true,
24381        /**
24382         * @event click
24383         * Fires when the element is click.
24384         * @param {Roo.bootstrap.CheckBox} this This input
24385         */
24386        click : true
24387     });
24388     
24389 };
24390
24391 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24392   
24393     inputType: 'checkbox',
24394     inputValue: 1,
24395     valueOff: 0,
24396     boxLabel: false,
24397     checked: false,
24398     weight : false,
24399     inline: false,
24400     tooltip : '',
24401     
24402     // checkbox success does not make any sense really.. 
24403     invalidClass : "",
24404     validClass : "",
24405     
24406     
24407     getAutoCreate : function()
24408     {
24409         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24410         
24411         var id = Roo.id();
24412         
24413         var cfg = {};
24414         
24415         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24416         
24417         if(this.inline){
24418             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24419         }
24420         
24421         var input =  {
24422             tag: 'input',
24423             id : id,
24424             type : this.inputType,
24425             value : this.inputValue,
24426             cls : 'roo-' + this.inputType, //'form-box',
24427             placeholder : this.placeholder || ''
24428             
24429         };
24430         
24431         if(this.inputType != 'radio'){
24432             var hidden =  {
24433                 tag: 'input',
24434                 type : 'hidden',
24435                 cls : 'roo-hidden-value',
24436                 value : this.checked ? this.inputValue : this.valueOff
24437             };
24438         }
24439         
24440             
24441         if (this.weight) { // Validity check?
24442             cfg.cls += " " + this.inputType + "-" + this.weight;
24443         }
24444         
24445         if (this.disabled) {
24446             input.disabled=true;
24447         }
24448         
24449         if(this.checked){
24450             input.checked = this.checked;
24451         }
24452         
24453         if (this.name) {
24454             
24455             input.name = this.name;
24456             
24457             if(this.inputType != 'radio'){
24458                 hidden.name = this.name;
24459                 input.name = '_hidden_' + this.name;
24460             }
24461         }
24462         
24463         if (this.size) {
24464             input.cls += ' input-' + this.size;
24465         }
24466         
24467         var settings=this;
24468         
24469         ['xs','sm','md','lg'].map(function(size){
24470             if (settings[size]) {
24471                 cfg.cls += ' col-' + size + '-' + settings[size];
24472             }
24473         });
24474         
24475         var inputblock = input;
24476          
24477         if (this.before || this.after) {
24478             
24479             inputblock = {
24480                 cls : 'input-group',
24481                 cn :  [] 
24482             };
24483             
24484             if (this.before) {
24485                 inputblock.cn.push({
24486                     tag :'span',
24487                     cls : 'input-group-addon',
24488                     html : this.before
24489                 });
24490             }
24491             
24492             inputblock.cn.push(input);
24493             
24494             if(this.inputType != 'radio'){
24495                 inputblock.cn.push(hidden);
24496             }
24497             
24498             if (this.after) {
24499                 inputblock.cn.push({
24500                     tag :'span',
24501                     cls : 'input-group-addon',
24502                     html : this.after
24503                 });
24504             }
24505             
24506         }
24507         var boxLabelCfg = false;
24508         
24509         if(this.boxLabel){
24510            
24511             boxLabelCfg = {
24512                 tag: 'label',
24513                 //'for': id, // box label is handled by onclick - so no for...
24514                 cls: 'box-label',
24515                 html: this.boxLabel
24516             };
24517             if(this.tooltip){
24518                 boxLabelCfg.tooltip = this.tooltip;
24519             }
24520              
24521         }
24522         
24523         
24524         if (align ==='left' && this.fieldLabel.length) {
24525 //                Roo.log("left and has label");
24526             cfg.cn = [
24527                 {
24528                     tag: 'label',
24529                     'for' :  id,
24530                     cls : 'control-label',
24531                     html : this.fieldLabel
24532                 },
24533                 {
24534                     cls : "", 
24535                     cn: [
24536                         inputblock
24537                     ]
24538                 }
24539             ];
24540             
24541             if (boxLabelCfg) {
24542                 cfg.cn[1].cn.push(boxLabelCfg);
24543             }
24544             
24545             if(this.labelWidth > 12){
24546                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24547             }
24548             
24549             if(this.labelWidth < 13 && this.labelmd == 0){
24550                 this.labelmd = this.labelWidth;
24551             }
24552             
24553             if(this.labellg > 0){
24554                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24555                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24556             }
24557             
24558             if(this.labelmd > 0){
24559                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24560                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24561             }
24562             
24563             if(this.labelsm > 0){
24564                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24565                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24566             }
24567             
24568             if(this.labelxs > 0){
24569                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24570                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24571             }
24572             
24573         } else if ( this.fieldLabel.length) {
24574 //                Roo.log(" label");
24575                 cfg.cn = [
24576                    
24577                     {
24578                         tag: this.boxLabel ? 'span' : 'label',
24579                         'for': id,
24580                         cls: 'control-label box-input-label',
24581                         //cls : 'input-group-addon',
24582                         html : this.fieldLabel
24583                     },
24584                     
24585                     inputblock
24586                     
24587                 ];
24588                 if (boxLabelCfg) {
24589                     cfg.cn.push(boxLabelCfg);
24590                 }
24591
24592         } else {
24593             
24594 //                Roo.log(" no label && no align");
24595                 cfg.cn = [  inputblock ] ;
24596                 if (boxLabelCfg) {
24597                     cfg.cn.push(boxLabelCfg);
24598                 }
24599
24600                 
24601         }
24602         
24603        
24604         
24605         if(this.inputType != 'radio'){
24606             cfg.cn.push(hidden);
24607         }
24608         
24609         return cfg;
24610         
24611     },
24612     
24613     /**
24614      * return the real input element.
24615      */
24616     inputEl: function ()
24617     {
24618         return this.el.select('input.roo-' + this.inputType,true).first();
24619     },
24620     hiddenEl: function ()
24621     {
24622         return this.el.select('input.roo-hidden-value',true).first();
24623     },
24624     
24625     labelEl: function()
24626     {
24627         return this.el.select('label.control-label',true).first();
24628     },
24629     /* depricated... */
24630     
24631     label: function()
24632     {
24633         return this.labelEl();
24634     },
24635     
24636     boxLabelEl: function()
24637     {
24638         return this.el.select('label.box-label',true).first();
24639     },
24640     
24641     initEvents : function()
24642     {
24643 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24644         
24645         this.inputEl().on('click', this.onClick,  this);
24646         
24647         if (this.boxLabel) { 
24648             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24649         }
24650         
24651         this.startValue = this.getValue();
24652         
24653         if(this.groupId){
24654             Roo.bootstrap.CheckBox.register(this);
24655         }
24656     },
24657     
24658     onClick : function(e)
24659     {   
24660         if(this.fireEvent('click', this, e) !== false){
24661             this.setChecked(!this.checked);
24662         }
24663         
24664     },
24665     
24666     setChecked : function(state,suppressEvent)
24667     {
24668         this.startValue = this.getValue();
24669
24670         if(this.inputType == 'radio'){
24671             
24672             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24673                 e.dom.checked = false;
24674             });
24675             
24676             this.inputEl().dom.checked = true;
24677             
24678             this.inputEl().dom.value = this.inputValue;
24679             
24680             if(suppressEvent !== true){
24681                 this.fireEvent('check', this, true);
24682             }
24683             
24684             this.validate();
24685             
24686             return;
24687         }
24688         
24689         this.checked = state;
24690         
24691         this.inputEl().dom.checked = state;
24692         
24693         
24694         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24695         
24696         if(suppressEvent !== true){
24697             this.fireEvent('check', this, state);
24698         }
24699         
24700         this.validate();
24701     },
24702     
24703     getValue : function()
24704     {
24705         if(this.inputType == 'radio'){
24706             return this.getGroupValue();
24707         }
24708         
24709         return this.hiddenEl().dom.value;
24710         
24711     },
24712     
24713     getGroupValue : function()
24714     {
24715         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24716             return '';
24717         }
24718         
24719         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24720     },
24721     
24722     setValue : function(v,suppressEvent)
24723     {
24724         if(this.inputType == 'radio'){
24725             this.setGroupValue(v, suppressEvent);
24726             return;
24727         }
24728         
24729         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24730         
24731         this.validate();
24732     },
24733     
24734     setGroupValue : function(v, suppressEvent)
24735     {
24736         this.startValue = this.getValue();
24737         
24738         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24739             e.dom.checked = false;
24740             
24741             if(e.dom.value == v){
24742                 e.dom.checked = true;
24743             }
24744         });
24745         
24746         if(suppressEvent !== true){
24747             this.fireEvent('check', this, true);
24748         }
24749
24750         this.validate();
24751         
24752         return;
24753     },
24754     
24755     validate : function()
24756     {
24757         if(this.getVisibilityEl().hasClass('hidden')){
24758             return true;
24759         }
24760         
24761         if(
24762                 this.disabled || 
24763                 (this.inputType == 'radio' && this.validateRadio()) ||
24764                 (this.inputType == 'checkbox' && this.validateCheckbox())
24765         ){
24766             this.markValid();
24767             return true;
24768         }
24769         
24770         this.markInvalid();
24771         return false;
24772     },
24773     
24774     validateRadio : function()
24775     {
24776         if(this.getVisibilityEl().hasClass('hidden')){
24777             return true;
24778         }
24779         
24780         if(this.allowBlank){
24781             return true;
24782         }
24783         
24784         var valid = false;
24785         
24786         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24787             if(!e.dom.checked){
24788                 return;
24789             }
24790             
24791             valid = true;
24792             
24793             return false;
24794         });
24795         
24796         return valid;
24797     },
24798     
24799     validateCheckbox : function()
24800     {
24801         if(!this.groupId){
24802             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24803             //return (this.getValue() == this.inputValue) ? true : false;
24804         }
24805         
24806         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24807         
24808         if(!group){
24809             return false;
24810         }
24811         
24812         var r = false;
24813         
24814         for(var i in group){
24815             if(group[i].el.isVisible(true)){
24816                 r = false;
24817                 break;
24818             }
24819             
24820             r = true;
24821         }
24822         
24823         for(var i in group){
24824             if(r){
24825                 break;
24826             }
24827             
24828             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24829         }
24830         
24831         return r;
24832     },
24833     
24834     /**
24835      * Mark this field as valid
24836      */
24837     markValid : function()
24838     {
24839         var _this = this;
24840         
24841         this.fireEvent('valid', this);
24842         
24843         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24844         
24845         if(this.groupId){
24846             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24847         }
24848         
24849         if(label){
24850             label.markValid();
24851         }
24852
24853         if(this.inputType == 'radio'){
24854             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24855                 var fg = e.findParent('.form-group', false, true);
24856                 if (Roo.bootstrap.version == 3) {
24857                     fg.removeClass([_this.invalidClass, _this.validClass]);
24858                     fg.addClass(_this.validClass);
24859                 } else {
24860                     fg.removeClass(['is-valid', 'is-invalid']);
24861                     fg.addClass('is-valid');
24862                 }
24863             });
24864             
24865             return;
24866         }
24867
24868         if(!this.groupId){
24869             var fg = this.el.findParent('.form-group', false, true);
24870             if (Roo.bootstrap.version == 3) {
24871                 fg.removeClass([this.invalidClass, this.validClass]);
24872                 fg.addClass(this.validClass);
24873             } else {
24874                 fg.removeClass(['is-valid', 'is-invalid']);
24875                 fg.addClass('is-valid');
24876             }
24877             return;
24878         }
24879         
24880         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24881         
24882         if(!group){
24883             return;
24884         }
24885         
24886         for(var i in group){
24887             var fg = group[i].el.findParent('.form-group', false, true);
24888             if (Roo.bootstrap.version == 3) {
24889                 fg.removeClass([this.invalidClass, this.validClass]);
24890                 fg.addClass(this.validClass);
24891             } else {
24892                 fg.removeClass(['is-valid', 'is-invalid']);
24893                 fg.addClass('is-valid');
24894             }
24895         }
24896     },
24897     
24898      /**
24899      * Mark this field as invalid
24900      * @param {String} msg The validation message
24901      */
24902     markInvalid : function(msg)
24903     {
24904         if(this.allowBlank){
24905             return;
24906         }
24907         
24908         var _this = this;
24909         
24910         this.fireEvent('invalid', this, msg);
24911         
24912         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24913         
24914         if(this.groupId){
24915             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24916         }
24917         
24918         if(label){
24919             label.markInvalid();
24920         }
24921             
24922         if(this.inputType == 'radio'){
24923             
24924             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24925                 var fg = e.findParent('.form-group', false, true);
24926                 if (Roo.bootstrap.version == 3) {
24927                     fg.removeClass([_this.invalidClass, _this.validClass]);
24928                     fg.addClass(_this.invalidClass);
24929                 } else {
24930                     fg.removeClass(['is-invalid', 'is-valid']);
24931                     fg.addClass('is-invalid');
24932                 }
24933             });
24934             
24935             return;
24936         }
24937         
24938         if(!this.groupId){
24939             var fg = this.el.findParent('.form-group', false, true);
24940             if (Roo.bootstrap.version == 3) {
24941                 fg.removeClass([_this.invalidClass, _this.validClass]);
24942                 fg.addClass(_this.invalidClass);
24943             } else {
24944                 fg.removeClass(['is-invalid', 'is-valid']);
24945                 fg.addClass('is-invalid');
24946             }
24947             return;
24948         }
24949         
24950         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24951         
24952         if(!group){
24953             return;
24954         }
24955         
24956         for(var i in group){
24957             var fg = group[i].el.findParent('.form-group', false, true);
24958             if (Roo.bootstrap.version == 3) {
24959                 fg.removeClass([_this.invalidClass, _this.validClass]);
24960                 fg.addClass(_this.invalidClass);
24961             } else {
24962                 fg.removeClass(['is-invalid', 'is-valid']);
24963                 fg.addClass('is-invalid');
24964             }
24965         }
24966         
24967     },
24968     
24969     clearInvalid : function()
24970     {
24971         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24972         
24973         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24974         
24975         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24976         
24977         if (label && label.iconEl) {
24978             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24979             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24980         }
24981     },
24982     
24983     disable : function()
24984     {
24985         if(this.inputType != 'radio'){
24986             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24987             return;
24988         }
24989         
24990         var _this = this;
24991         
24992         if(this.rendered){
24993             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24994                 _this.getActionEl().addClass(this.disabledClass);
24995                 e.dom.disabled = true;
24996             });
24997         }
24998         
24999         this.disabled = true;
25000         this.fireEvent("disable", this);
25001         return this;
25002     },
25003
25004     enable : function()
25005     {
25006         if(this.inputType != 'radio'){
25007             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25008             return;
25009         }
25010         
25011         var _this = this;
25012         
25013         if(this.rendered){
25014             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25015                 _this.getActionEl().removeClass(this.disabledClass);
25016                 e.dom.disabled = false;
25017             });
25018         }
25019         
25020         this.disabled = false;
25021         this.fireEvent("enable", this);
25022         return this;
25023     },
25024     
25025     setBoxLabel : function(v)
25026     {
25027         this.boxLabel = v;
25028         
25029         if(this.rendered){
25030             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25031         }
25032     }
25033
25034 });
25035
25036 Roo.apply(Roo.bootstrap.CheckBox, {
25037     
25038     groups: {},
25039     
25040      /**
25041     * register a CheckBox Group
25042     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25043     */
25044     register : function(checkbox)
25045     {
25046         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25047             this.groups[checkbox.groupId] = {};
25048         }
25049         
25050         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25051             return;
25052         }
25053         
25054         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25055         
25056     },
25057     /**
25058     * fetch a CheckBox Group based on the group ID
25059     * @param {string} the group ID
25060     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25061     */
25062     get: function(groupId) {
25063         if (typeof(this.groups[groupId]) == 'undefined') {
25064             return false;
25065         }
25066         
25067         return this.groups[groupId] ;
25068     }
25069     
25070     
25071 });
25072 /*
25073  * - LGPL
25074  *
25075  * RadioItem
25076  * 
25077  */
25078
25079 /**
25080  * @class Roo.bootstrap.Radio
25081  * @extends Roo.bootstrap.Component
25082  * Bootstrap Radio class
25083  * @cfg {String} boxLabel - the label associated
25084  * @cfg {String} value - the value of radio
25085  * 
25086  * @constructor
25087  * Create a new Radio
25088  * @param {Object} config The config object
25089  */
25090 Roo.bootstrap.Radio = function(config){
25091     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25092     
25093 };
25094
25095 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25096     
25097     boxLabel : '',
25098     
25099     value : '',
25100     
25101     getAutoCreate : function()
25102     {
25103         var cfg = {
25104             tag : 'div',
25105             cls : 'form-group radio',
25106             cn : [
25107                 {
25108                     tag : 'label',
25109                     cls : 'box-label',
25110                     html : this.boxLabel
25111                 }
25112             ]
25113         };
25114         
25115         return cfg;
25116     },
25117     
25118     initEvents : function() 
25119     {
25120         this.parent().register(this);
25121         
25122         this.el.on('click', this.onClick, this);
25123         
25124     },
25125     
25126     onClick : function(e)
25127     {
25128         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25129             this.setChecked(true);
25130         }
25131     },
25132     
25133     setChecked : function(state, suppressEvent)
25134     {
25135         this.parent().setValue(this.value, suppressEvent);
25136         
25137     },
25138     
25139     setBoxLabel : function(v)
25140     {
25141         this.boxLabel = v;
25142         
25143         if(this.rendered){
25144             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25145         }
25146     }
25147     
25148 });
25149  
25150
25151  /*
25152  * - LGPL
25153  *
25154  * Input
25155  * 
25156  */
25157
25158 /**
25159  * @class Roo.bootstrap.SecurePass
25160  * @extends Roo.bootstrap.Input
25161  * Bootstrap SecurePass class
25162  *
25163  * 
25164  * @constructor
25165  * Create a new SecurePass
25166  * @param {Object} config The config object
25167  */
25168  
25169 Roo.bootstrap.SecurePass = function (config) {
25170     // these go here, so the translation tool can replace them..
25171     this.errors = {
25172         PwdEmpty: "Please type a password, and then retype it to confirm.",
25173         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25174         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25175         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25176         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25177         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25178         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25179         TooWeak: "Your password is Too Weak."
25180     },
25181     this.meterLabel = "Password strength:";
25182     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25183     this.meterClass = [
25184         "roo-password-meter-tooweak", 
25185         "roo-password-meter-weak", 
25186         "roo-password-meter-medium", 
25187         "roo-password-meter-strong", 
25188         "roo-password-meter-grey"
25189     ];
25190     
25191     this.errors = {};
25192     
25193     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25194 }
25195
25196 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25197     /**
25198      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25199      * {
25200      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25201      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25202      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25203      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25204      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25205      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25206      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25207      * })
25208      */
25209     // private
25210     
25211     meterWidth: 300,
25212     errorMsg :'',    
25213     errors: false,
25214     imageRoot: '/',
25215     /**
25216      * @cfg {String/Object} Label for the strength meter (defaults to
25217      * 'Password strength:')
25218      */
25219     // private
25220     meterLabel: '',
25221     /**
25222      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25223      * ['Weak', 'Medium', 'Strong'])
25224      */
25225     // private    
25226     pwdStrengths: false,    
25227     // private
25228     strength: 0,
25229     // private
25230     _lastPwd: null,
25231     // private
25232     kCapitalLetter: 0,
25233     kSmallLetter: 1,
25234     kDigit: 2,
25235     kPunctuation: 3,
25236     
25237     insecure: false,
25238     // private
25239     initEvents: function ()
25240     {
25241         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25242
25243         if (this.el.is('input[type=password]') && Roo.isSafari) {
25244             this.el.on('keydown', this.SafariOnKeyDown, this);
25245         }
25246
25247         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25248     },
25249     // private
25250     onRender: function (ct, position)
25251     {
25252         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25253         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25254         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25255
25256         this.trigger.createChild({
25257                    cn: [
25258                     {
25259                     //id: 'PwdMeter',
25260                     tag: 'div',
25261                     cls: 'roo-password-meter-grey col-xs-12',
25262                     style: {
25263                         //width: 0,
25264                         //width: this.meterWidth + 'px'                                                
25265                         }
25266                     },
25267                     {                            
25268                          cls: 'roo-password-meter-text'                          
25269                     }
25270                 ]            
25271         });
25272
25273          
25274         if (this.hideTrigger) {
25275             this.trigger.setDisplayed(false);
25276         }
25277         this.setSize(this.width || '', this.height || '');
25278     },
25279     // private
25280     onDestroy: function ()
25281     {
25282         if (this.trigger) {
25283             this.trigger.removeAllListeners();
25284             this.trigger.remove();
25285         }
25286         if (this.wrap) {
25287             this.wrap.remove();
25288         }
25289         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25290     },
25291     // private
25292     checkStrength: function ()
25293     {
25294         var pwd = this.inputEl().getValue();
25295         if (pwd == this._lastPwd) {
25296             return;
25297         }
25298
25299         var strength;
25300         if (this.ClientSideStrongPassword(pwd)) {
25301             strength = 3;
25302         } else if (this.ClientSideMediumPassword(pwd)) {
25303             strength = 2;
25304         } else if (this.ClientSideWeakPassword(pwd)) {
25305             strength = 1;
25306         } else {
25307             strength = 0;
25308         }
25309         
25310         Roo.log('strength1: ' + strength);
25311         
25312         //var pm = this.trigger.child('div/div/div').dom;
25313         var pm = this.trigger.child('div/div');
25314         pm.removeClass(this.meterClass);
25315         pm.addClass(this.meterClass[strength]);
25316                 
25317         
25318         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25319                 
25320         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25321         
25322         this._lastPwd = pwd;
25323     },
25324     reset: function ()
25325     {
25326         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25327         
25328         this._lastPwd = '';
25329         
25330         var pm = this.trigger.child('div/div');
25331         pm.removeClass(this.meterClass);
25332         pm.addClass('roo-password-meter-grey');        
25333         
25334         
25335         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25336         
25337         pt.innerHTML = '';
25338         this.inputEl().dom.type='password';
25339     },
25340     // private
25341     validateValue: function (value)
25342     {
25343         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25344             return false;
25345         }
25346         if (value.length == 0) {
25347             if (this.allowBlank) {
25348                 this.clearInvalid();
25349                 return true;
25350             }
25351
25352             this.markInvalid(this.errors.PwdEmpty);
25353             this.errorMsg = this.errors.PwdEmpty;
25354             return false;
25355         }
25356         
25357         if(this.insecure){
25358             return true;
25359         }
25360         
25361         if (!value.match(/[\x21-\x7e]+/)) {
25362             this.markInvalid(this.errors.PwdBadChar);
25363             this.errorMsg = this.errors.PwdBadChar;
25364             return false;
25365         }
25366         if (value.length < 6) {
25367             this.markInvalid(this.errors.PwdShort);
25368             this.errorMsg = this.errors.PwdShort;
25369             return false;
25370         }
25371         if (value.length > 16) {
25372             this.markInvalid(this.errors.PwdLong);
25373             this.errorMsg = this.errors.PwdLong;
25374             return false;
25375         }
25376         var strength;
25377         if (this.ClientSideStrongPassword(value)) {
25378             strength = 3;
25379         } else if (this.ClientSideMediumPassword(value)) {
25380             strength = 2;
25381         } else if (this.ClientSideWeakPassword(value)) {
25382             strength = 1;
25383         } else {
25384             strength = 0;
25385         }
25386
25387         
25388         if (strength < 2) {
25389             //this.markInvalid(this.errors.TooWeak);
25390             this.errorMsg = this.errors.TooWeak;
25391             //return false;
25392         }
25393         
25394         
25395         console.log('strength2: ' + strength);
25396         
25397         //var pm = this.trigger.child('div/div/div').dom;
25398         
25399         var pm = this.trigger.child('div/div');
25400         pm.removeClass(this.meterClass);
25401         pm.addClass(this.meterClass[strength]);
25402                 
25403         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25404                 
25405         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25406         
25407         this.errorMsg = ''; 
25408         return true;
25409     },
25410     // private
25411     CharacterSetChecks: function (type)
25412     {
25413         this.type = type;
25414         this.fResult = false;
25415     },
25416     // private
25417     isctype: function (character, type)
25418     {
25419         switch (type) {  
25420             case this.kCapitalLetter:
25421                 if (character >= 'A' && character <= 'Z') {
25422                     return true;
25423                 }
25424                 break;
25425             
25426             case this.kSmallLetter:
25427                 if (character >= 'a' && character <= 'z') {
25428                     return true;
25429                 }
25430                 break;
25431             
25432             case this.kDigit:
25433                 if (character >= '0' && character <= '9') {
25434                     return true;
25435                 }
25436                 break;
25437             
25438             case this.kPunctuation:
25439                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25440                     return true;
25441                 }
25442                 break;
25443             
25444             default:
25445                 return false;
25446         }
25447
25448     },
25449     // private
25450     IsLongEnough: function (pwd, size)
25451     {
25452         return !(pwd == null || isNaN(size) || pwd.length < size);
25453     },
25454     // private
25455     SpansEnoughCharacterSets: function (word, nb)
25456     {
25457         if (!this.IsLongEnough(word, nb))
25458         {
25459             return false;
25460         }
25461
25462         var characterSetChecks = new Array(
25463             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25464             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25465         );
25466         
25467         for (var index = 0; index < word.length; ++index) {
25468             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25469                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25470                     characterSetChecks[nCharSet].fResult = true;
25471                     break;
25472                 }
25473             }
25474         }
25475
25476         var nCharSets = 0;
25477         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25478             if (characterSetChecks[nCharSet].fResult) {
25479                 ++nCharSets;
25480             }
25481         }
25482
25483         if (nCharSets < nb) {
25484             return false;
25485         }
25486         return true;
25487     },
25488     // private
25489     ClientSideStrongPassword: function (pwd)
25490     {
25491         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25492     },
25493     // private
25494     ClientSideMediumPassword: function (pwd)
25495     {
25496         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25497     },
25498     // private
25499     ClientSideWeakPassword: function (pwd)
25500     {
25501         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25502     }
25503           
25504 })//<script type="text/javascript">
25505
25506 /*
25507  * Based  Ext JS Library 1.1.1
25508  * Copyright(c) 2006-2007, Ext JS, LLC.
25509  * LGPL
25510  *
25511  */
25512  
25513 /**
25514  * @class Roo.HtmlEditorCore
25515  * @extends Roo.Component
25516  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25517  *
25518  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25519  */
25520
25521 Roo.HtmlEditorCore = function(config){
25522     
25523     
25524     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25525     
25526     
25527     this.addEvents({
25528         /**
25529          * @event initialize
25530          * Fires when the editor is fully initialized (including the iframe)
25531          * @param {Roo.HtmlEditorCore} this
25532          */
25533         initialize: true,
25534         /**
25535          * @event activate
25536          * Fires when the editor is first receives the focus. Any insertion must wait
25537          * until after this event.
25538          * @param {Roo.HtmlEditorCore} this
25539          */
25540         activate: true,
25541          /**
25542          * @event beforesync
25543          * Fires before the textarea is updated with content from the editor iframe. Return false
25544          * to cancel the sync.
25545          * @param {Roo.HtmlEditorCore} this
25546          * @param {String} html
25547          */
25548         beforesync: true,
25549          /**
25550          * @event beforepush
25551          * Fires before the iframe editor is updated with content from the textarea. Return false
25552          * to cancel the push.
25553          * @param {Roo.HtmlEditorCore} this
25554          * @param {String} html
25555          */
25556         beforepush: true,
25557          /**
25558          * @event sync
25559          * Fires when the textarea is updated with content from the editor iframe.
25560          * @param {Roo.HtmlEditorCore} this
25561          * @param {String} html
25562          */
25563         sync: true,
25564          /**
25565          * @event push
25566          * Fires when the iframe editor is updated with content from the textarea.
25567          * @param {Roo.HtmlEditorCore} this
25568          * @param {String} html
25569          */
25570         push: true,
25571         
25572         /**
25573          * @event editorevent
25574          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25575          * @param {Roo.HtmlEditorCore} this
25576          */
25577         editorevent: true
25578         
25579     });
25580     
25581     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25582     
25583     // defaults : white / black...
25584     this.applyBlacklists();
25585     
25586     
25587     
25588 };
25589
25590
25591 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25592
25593
25594      /**
25595      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25596      */
25597     
25598     owner : false,
25599     
25600      /**
25601      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25602      *                        Roo.resizable.
25603      */
25604     resizable : false,
25605      /**
25606      * @cfg {Number} height (in pixels)
25607      */   
25608     height: 300,
25609    /**
25610      * @cfg {Number} width (in pixels)
25611      */   
25612     width: 500,
25613     
25614     /**
25615      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25616      * 
25617      */
25618     stylesheets: false,
25619     
25620     /**
25621      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25622      */
25623     allowComments: false,
25624     // id of frame..
25625     frameId: false,
25626     
25627     // private properties
25628     validationEvent : false,
25629     deferHeight: true,
25630     initialized : false,
25631     activated : false,
25632     sourceEditMode : false,
25633     onFocus : Roo.emptyFn,
25634     iframePad:3,
25635     hideMode:'offsets',
25636     
25637     clearUp: true,
25638     
25639     // blacklist + whitelisted elements..
25640     black: false,
25641     white: false,
25642      
25643     bodyCls : '',
25644
25645     /**
25646      * Protected method that will not generally be called directly. It
25647      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25648      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25649      */
25650     getDocMarkup : function(){
25651         // body styles..
25652         var st = '';
25653         
25654         // inherit styels from page...?? 
25655         if (this.stylesheets === false) {
25656             
25657             Roo.get(document.head).select('style').each(function(node) {
25658                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25659             });
25660             
25661             Roo.get(document.head).select('link').each(function(node) { 
25662                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25663             });
25664             
25665         } else if (!this.stylesheets.length) {
25666                 // simple..
25667                 st = '<style type="text/css">' +
25668                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25669                    '</style>';
25670         } else {
25671             for (var i in this.stylesheets) { 
25672                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25673             }
25674             
25675         }
25676         
25677         st +=  '<style type="text/css">' +
25678             'IMG { cursor: pointer } ' +
25679         '</style>';
25680
25681         var cls = 'roo-htmleditor-body';
25682         
25683         if(this.bodyCls.length){
25684             cls += ' ' + this.bodyCls;
25685         }
25686         
25687         return '<html><head>' + st  +
25688             //<style type="text/css">' +
25689             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25690             //'</style>' +
25691             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25692     },
25693
25694     // private
25695     onRender : function(ct, position)
25696     {
25697         var _t = this;
25698         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25699         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25700         
25701         
25702         this.el.dom.style.border = '0 none';
25703         this.el.dom.setAttribute('tabIndex', -1);
25704         this.el.addClass('x-hidden hide');
25705         
25706         
25707         
25708         if(Roo.isIE){ // fix IE 1px bogus margin
25709             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25710         }
25711        
25712         
25713         this.frameId = Roo.id();
25714         
25715          
25716         
25717         var iframe = this.owner.wrap.createChild({
25718             tag: 'iframe',
25719             cls: 'form-control', // bootstrap..
25720             id: this.frameId,
25721             name: this.frameId,
25722             frameBorder : 'no',
25723             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25724         }, this.el
25725         );
25726         
25727         
25728         this.iframe = iframe.dom;
25729
25730          this.assignDocWin();
25731         
25732         this.doc.designMode = 'on';
25733        
25734         this.doc.open();
25735         this.doc.write(this.getDocMarkup());
25736         this.doc.close();
25737
25738         
25739         var task = { // must defer to wait for browser to be ready
25740             run : function(){
25741                 //console.log("run task?" + this.doc.readyState);
25742                 this.assignDocWin();
25743                 if(this.doc.body || this.doc.readyState == 'complete'){
25744                     try {
25745                         this.doc.designMode="on";
25746                     } catch (e) {
25747                         return;
25748                     }
25749                     Roo.TaskMgr.stop(task);
25750                     this.initEditor.defer(10, this);
25751                 }
25752             },
25753             interval : 10,
25754             duration: 10000,
25755             scope: this
25756         };
25757         Roo.TaskMgr.start(task);
25758
25759     },
25760
25761     // private
25762     onResize : function(w, h)
25763     {
25764          Roo.log('resize: ' +w + ',' + h );
25765         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25766         if(!this.iframe){
25767             return;
25768         }
25769         if(typeof w == 'number'){
25770             
25771             this.iframe.style.width = w + 'px';
25772         }
25773         if(typeof h == 'number'){
25774             
25775             this.iframe.style.height = h + 'px';
25776             if(this.doc){
25777                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25778             }
25779         }
25780         
25781     },
25782
25783     /**
25784      * Toggles the editor between standard and source edit mode.
25785      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25786      */
25787     toggleSourceEdit : function(sourceEditMode){
25788         
25789         this.sourceEditMode = sourceEditMode === true;
25790         
25791         if(this.sourceEditMode){
25792  
25793             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25794             
25795         }else{
25796             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25797             //this.iframe.className = '';
25798             this.deferFocus();
25799         }
25800         //this.setSize(this.owner.wrap.getSize());
25801         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25802     },
25803
25804     
25805   
25806
25807     /**
25808      * Protected method that will not generally be called directly. If you need/want
25809      * custom HTML cleanup, this is the method you should override.
25810      * @param {String} html The HTML to be cleaned
25811      * return {String} The cleaned HTML
25812      */
25813     cleanHtml : function(html){
25814         html = String(html);
25815         if(html.length > 5){
25816             if(Roo.isSafari){ // strip safari nonsense
25817                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25818             }
25819         }
25820         if(html == '&nbsp;'){
25821             html = '';
25822         }
25823         return html;
25824     },
25825
25826     /**
25827      * HTML Editor -> Textarea
25828      * Protected method that will not generally be called directly. Syncs the contents
25829      * of the editor iframe with the textarea.
25830      */
25831     syncValue : function(){
25832         if(this.initialized){
25833             var bd = (this.doc.body || this.doc.documentElement);
25834             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25835             var html = bd.innerHTML;
25836             if(Roo.isSafari){
25837                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25838                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25839                 if(m && m[1]){
25840                     html = '<div style="'+m[0]+'">' + html + '</div>';
25841                 }
25842             }
25843             html = this.cleanHtml(html);
25844             // fix up the special chars.. normaly like back quotes in word...
25845             // however we do not want to do this with chinese..
25846             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25847                 
25848                 var cc = match.charCodeAt();
25849
25850                 // Get the character value, handling surrogate pairs
25851                 if (match.length == 2) {
25852                     // It's a surrogate pair, calculate the Unicode code point
25853                     var high = match.charCodeAt(0) - 0xD800;
25854                     var low  = match.charCodeAt(1) - 0xDC00;
25855                     cc = (high * 0x400) + low + 0x10000;
25856                 }  else if (
25857                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25858                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25859                     (cc >= 0xf900 && cc < 0xfb00 )
25860                 ) {
25861                         return match;
25862                 }  
25863          
25864                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25865                 return "&#" + cc + ";";
25866                 
25867                 
25868             });
25869             
25870             
25871              
25872             if(this.owner.fireEvent('beforesync', this, html) !== false){
25873                 this.el.dom.value = html;
25874                 this.owner.fireEvent('sync', this, html);
25875             }
25876         }
25877     },
25878
25879     /**
25880      * Protected method that will not generally be called directly. Pushes the value of the textarea
25881      * into the iframe editor.
25882      */
25883     pushValue : function(){
25884         if(this.initialized){
25885             var v = this.el.dom.value.trim();
25886             
25887 //            if(v.length < 1){
25888 //                v = '&#160;';
25889 //            }
25890             
25891             if(this.owner.fireEvent('beforepush', this, v) !== false){
25892                 var d = (this.doc.body || this.doc.documentElement);
25893                 d.innerHTML = v;
25894                 this.cleanUpPaste();
25895                 this.el.dom.value = d.innerHTML;
25896                 this.owner.fireEvent('push', this, v);
25897             }
25898         }
25899     },
25900
25901     // private
25902     deferFocus : function(){
25903         this.focus.defer(10, this);
25904     },
25905
25906     // doc'ed in Field
25907     focus : function(){
25908         if(this.win && !this.sourceEditMode){
25909             this.win.focus();
25910         }else{
25911             this.el.focus();
25912         }
25913     },
25914     
25915     assignDocWin: function()
25916     {
25917         var iframe = this.iframe;
25918         
25919          if(Roo.isIE){
25920             this.doc = iframe.contentWindow.document;
25921             this.win = iframe.contentWindow;
25922         } else {
25923 //            if (!Roo.get(this.frameId)) {
25924 //                return;
25925 //            }
25926 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25927 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25928             
25929             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25930                 return;
25931             }
25932             
25933             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25934             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25935         }
25936     },
25937     
25938     // private
25939     initEditor : function(){
25940         //console.log("INIT EDITOR");
25941         this.assignDocWin();
25942         
25943         
25944         
25945         this.doc.designMode="on";
25946         this.doc.open();
25947         this.doc.write(this.getDocMarkup());
25948         this.doc.close();
25949         
25950         var dbody = (this.doc.body || this.doc.documentElement);
25951         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25952         // this copies styles from the containing element into thsi one..
25953         // not sure why we need all of this..
25954         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25955         
25956         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25957         //ss['background-attachment'] = 'fixed'; // w3c
25958         dbody.bgProperties = 'fixed'; // ie
25959         //Roo.DomHelper.applyStyles(dbody, ss);
25960         Roo.EventManager.on(this.doc, {
25961             //'mousedown': this.onEditorEvent,
25962             'mouseup': this.onEditorEvent,
25963             'dblclick': this.onEditorEvent,
25964             'click': this.onEditorEvent,
25965             'keyup': this.onEditorEvent,
25966             buffer:100,
25967             scope: this
25968         });
25969         if(Roo.isGecko){
25970             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25971         }
25972         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25973             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25974         }
25975         this.initialized = true;
25976
25977         this.owner.fireEvent('initialize', this);
25978         this.pushValue();
25979     },
25980
25981     // private
25982     onDestroy : function(){
25983         
25984         
25985         
25986         if(this.rendered){
25987             
25988             //for (var i =0; i < this.toolbars.length;i++) {
25989             //    // fixme - ask toolbars for heights?
25990             //    this.toolbars[i].onDestroy();
25991            // }
25992             
25993             //this.wrap.dom.innerHTML = '';
25994             //this.wrap.remove();
25995         }
25996     },
25997
25998     // private
25999     onFirstFocus : function(){
26000         
26001         this.assignDocWin();
26002         
26003         
26004         this.activated = true;
26005          
26006     
26007         if(Roo.isGecko){ // prevent silly gecko errors
26008             this.win.focus();
26009             var s = this.win.getSelection();
26010             if(!s.focusNode || s.focusNode.nodeType != 3){
26011                 var r = s.getRangeAt(0);
26012                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26013                 r.collapse(true);
26014                 this.deferFocus();
26015             }
26016             try{
26017                 this.execCmd('useCSS', true);
26018                 this.execCmd('styleWithCSS', false);
26019             }catch(e){}
26020         }
26021         this.owner.fireEvent('activate', this);
26022     },
26023
26024     // private
26025     adjustFont: function(btn){
26026         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26027         //if(Roo.isSafari){ // safari
26028         //    adjust *= 2;
26029        // }
26030         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26031         if(Roo.isSafari){ // safari
26032             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26033             v =  (v < 10) ? 10 : v;
26034             v =  (v > 48) ? 48 : v;
26035             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26036             
26037         }
26038         
26039         
26040         v = Math.max(1, v+adjust);
26041         
26042         this.execCmd('FontSize', v  );
26043     },
26044
26045     onEditorEvent : function(e)
26046     {
26047         this.owner.fireEvent('editorevent', this, e);
26048       //  this.updateToolbar();
26049         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26050     },
26051
26052     insertTag : function(tg)
26053     {
26054         // could be a bit smarter... -> wrap the current selected tRoo..
26055         if (tg.toLowerCase() == 'span' ||
26056             tg.toLowerCase() == 'code' ||
26057             tg.toLowerCase() == 'sup' ||
26058             tg.toLowerCase() == 'sub' 
26059             ) {
26060             
26061             range = this.createRange(this.getSelection());
26062             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26063             wrappingNode.appendChild(range.extractContents());
26064             range.insertNode(wrappingNode);
26065
26066             return;
26067             
26068             
26069             
26070         }
26071         this.execCmd("formatblock",   tg);
26072         
26073     },
26074     
26075     insertText : function(txt)
26076     {
26077         
26078         
26079         var range = this.createRange();
26080         range.deleteContents();
26081                //alert(Sender.getAttribute('label'));
26082                
26083         range.insertNode(this.doc.createTextNode(txt));
26084     } ,
26085     
26086      
26087
26088     /**
26089      * Executes a Midas editor command on the editor document and performs necessary focus and
26090      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26091      * @param {String} cmd The Midas command
26092      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26093      */
26094     relayCmd : function(cmd, value){
26095         this.win.focus();
26096         this.execCmd(cmd, value);
26097         this.owner.fireEvent('editorevent', this);
26098         //this.updateToolbar();
26099         this.owner.deferFocus();
26100     },
26101
26102     /**
26103      * Executes a Midas editor command directly on the editor document.
26104      * For visual commands, you should use {@link #relayCmd} instead.
26105      * <b>This should only be called after the editor is initialized.</b>
26106      * @param {String} cmd The Midas command
26107      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26108      */
26109     execCmd : function(cmd, value){
26110         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26111         this.syncValue();
26112     },
26113  
26114  
26115    
26116     /**
26117      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26118      * to insert tRoo.
26119      * @param {String} text | dom node.. 
26120      */
26121     insertAtCursor : function(text)
26122     {
26123         
26124         if(!this.activated){
26125             return;
26126         }
26127         /*
26128         if(Roo.isIE){
26129             this.win.focus();
26130             var r = this.doc.selection.createRange();
26131             if(r){
26132                 r.collapse(true);
26133                 r.pasteHTML(text);
26134                 this.syncValue();
26135                 this.deferFocus();
26136             
26137             }
26138             return;
26139         }
26140         */
26141         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26142             this.win.focus();
26143             
26144             
26145             // from jquery ui (MIT licenced)
26146             var range, node;
26147             var win = this.win;
26148             
26149             if (win.getSelection && win.getSelection().getRangeAt) {
26150                 range = win.getSelection().getRangeAt(0);
26151                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26152                 range.insertNode(node);
26153             } else if (win.document.selection && win.document.selection.createRange) {
26154                 // no firefox support
26155                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26156                 win.document.selection.createRange().pasteHTML(txt);
26157             } else {
26158                 // no firefox support
26159                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26160                 this.execCmd('InsertHTML', txt);
26161             } 
26162             
26163             this.syncValue();
26164             
26165             this.deferFocus();
26166         }
26167     },
26168  // private
26169     mozKeyPress : function(e){
26170         if(e.ctrlKey){
26171             var c = e.getCharCode(), cmd;
26172           
26173             if(c > 0){
26174                 c = String.fromCharCode(c).toLowerCase();
26175                 switch(c){
26176                     case 'b':
26177                         cmd = 'bold';
26178                         break;
26179                     case 'i':
26180                         cmd = 'italic';
26181                         break;
26182                     
26183                     case 'u':
26184                         cmd = 'underline';
26185                         break;
26186                     
26187                     case 'v':
26188                         this.cleanUpPaste.defer(100, this);
26189                         return;
26190                         
26191                 }
26192                 if(cmd){
26193                     this.win.focus();
26194                     this.execCmd(cmd);
26195                     this.deferFocus();
26196                     e.preventDefault();
26197                 }
26198                 
26199             }
26200         }
26201     },
26202
26203     // private
26204     fixKeys : function(){ // load time branching for fastest keydown performance
26205         if(Roo.isIE){
26206             return function(e){
26207                 var k = e.getKey(), r;
26208                 if(k == e.TAB){
26209                     e.stopEvent();
26210                     r = this.doc.selection.createRange();
26211                     if(r){
26212                         r.collapse(true);
26213                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26214                         this.deferFocus();
26215                     }
26216                     return;
26217                 }
26218                 
26219                 if(k == e.ENTER){
26220                     r = this.doc.selection.createRange();
26221                     if(r){
26222                         var target = r.parentElement();
26223                         if(!target || target.tagName.toLowerCase() != 'li'){
26224                             e.stopEvent();
26225                             r.pasteHTML('<br />');
26226                             r.collapse(false);
26227                             r.select();
26228                         }
26229                     }
26230                 }
26231                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26232                     this.cleanUpPaste.defer(100, this);
26233                     return;
26234                 }
26235                 
26236                 
26237             };
26238         }else if(Roo.isOpera){
26239             return function(e){
26240                 var k = e.getKey();
26241                 if(k == e.TAB){
26242                     e.stopEvent();
26243                     this.win.focus();
26244                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26245                     this.deferFocus();
26246                 }
26247                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26248                     this.cleanUpPaste.defer(100, this);
26249                     return;
26250                 }
26251                 
26252             };
26253         }else if(Roo.isSafari){
26254             return function(e){
26255                 var k = e.getKey();
26256                 
26257                 if(k == e.TAB){
26258                     e.stopEvent();
26259                     this.execCmd('InsertText','\t');
26260                     this.deferFocus();
26261                     return;
26262                 }
26263                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26264                     this.cleanUpPaste.defer(100, this);
26265                     return;
26266                 }
26267                 
26268              };
26269         }
26270     }(),
26271     
26272     getAllAncestors: function()
26273     {
26274         var p = this.getSelectedNode();
26275         var a = [];
26276         if (!p) {
26277             a.push(p); // push blank onto stack..
26278             p = this.getParentElement();
26279         }
26280         
26281         
26282         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26283             a.push(p);
26284             p = p.parentNode;
26285         }
26286         a.push(this.doc.body);
26287         return a;
26288     },
26289     lastSel : false,
26290     lastSelNode : false,
26291     
26292     
26293     getSelection : function() 
26294     {
26295         this.assignDocWin();
26296         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26297     },
26298     
26299     getSelectedNode: function() 
26300     {
26301         // this may only work on Gecko!!!
26302         
26303         // should we cache this!!!!
26304         
26305         
26306         
26307          
26308         var range = this.createRange(this.getSelection()).cloneRange();
26309         
26310         if (Roo.isIE) {
26311             var parent = range.parentElement();
26312             while (true) {
26313                 var testRange = range.duplicate();
26314                 testRange.moveToElementText(parent);
26315                 if (testRange.inRange(range)) {
26316                     break;
26317                 }
26318                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26319                     break;
26320                 }
26321                 parent = parent.parentElement;
26322             }
26323             return parent;
26324         }
26325         
26326         // is ancestor a text element.
26327         var ac =  range.commonAncestorContainer;
26328         if (ac.nodeType == 3) {
26329             ac = ac.parentNode;
26330         }
26331         
26332         var ar = ac.childNodes;
26333          
26334         var nodes = [];
26335         var other_nodes = [];
26336         var has_other_nodes = false;
26337         for (var i=0;i<ar.length;i++) {
26338             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26339                 continue;
26340             }
26341             // fullly contained node.
26342             
26343             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26344                 nodes.push(ar[i]);
26345                 continue;
26346             }
26347             
26348             // probably selected..
26349             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26350                 other_nodes.push(ar[i]);
26351                 continue;
26352             }
26353             // outer..
26354             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26355                 continue;
26356             }
26357             
26358             
26359             has_other_nodes = true;
26360         }
26361         if (!nodes.length && other_nodes.length) {
26362             nodes= other_nodes;
26363         }
26364         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26365             return false;
26366         }
26367         
26368         return nodes[0];
26369     },
26370     createRange: function(sel)
26371     {
26372         // this has strange effects when using with 
26373         // top toolbar - not sure if it's a great idea.
26374         //this.editor.contentWindow.focus();
26375         if (typeof sel != "undefined") {
26376             try {
26377                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26378             } catch(e) {
26379                 return this.doc.createRange();
26380             }
26381         } else {
26382             return this.doc.createRange();
26383         }
26384     },
26385     getParentElement: function()
26386     {
26387         
26388         this.assignDocWin();
26389         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26390         
26391         var range = this.createRange(sel);
26392          
26393         try {
26394             var p = range.commonAncestorContainer;
26395             while (p.nodeType == 3) { // text node
26396                 p = p.parentNode;
26397             }
26398             return p;
26399         } catch (e) {
26400             return null;
26401         }
26402     
26403     },
26404     /***
26405      *
26406      * Range intersection.. the hard stuff...
26407      *  '-1' = before
26408      *  '0' = hits..
26409      *  '1' = after.
26410      *         [ -- selected range --- ]
26411      *   [fail]                        [fail]
26412      *
26413      *    basically..
26414      *      if end is before start or  hits it. fail.
26415      *      if start is after end or hits it fail.
26416      *
26417      *   if either hits (but other is outside. - then it's not 
26418      *   
26419      *    
26420      **/
26421     
26422     
26423     // @see http://www.thismuchiknow.co.uk/?p=64.
26424     rangeIntersectsNode : function(range, node)
26425     {
26426         var nodeRange = node.ownerDocument.createRange();
26427         try {
26428             nodeRange.selectNode(node);
26429         } catch (e) {
26430             nodeRange.selectNodeContents(node);
26431         }
26432     
26433         var rangeStartRange = range.cloneRange();
26434         rangeStartRange.collapse(true);
26435     
26436         var rangeEndRange = range.cloneRange();
26437         rangeEndRange.collapse(false);
26438     
26439         var nodeStartRange = nodeRange.cloneRange();
26440         nodeStartRange.collapse(true);
26441     
26442         var nodeEndRange = nodeRange.cloneRange();
26443         nodeEndRange.collapse(false);
26444     
26445         return rangeStartRange.compareBoundaryPoints(
26446                  Range.START_TO_START, nodeEndRange) == -1 &&
26447                rangeEndRange.compareBoundaryPoints(
26448                  Range.START_TO_START, nodeStartRange) == 1;
26449         
26450          
26451     },
26452     rangeCompareNode : function(range, node)
26453     {
26454         var nodeRange = node.ownerDocument.createRange();
26455         try {
26456             nodeRange.selectNode(node);
26457         } catch (e) {
26458             nodeRange.selectNodeContents(node);
26459         }
26460         
26461         
26462         range.collapse(true);
26463     
26464         nodeRange.collapse(true);
26465      
26466         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26467         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26468          
26469         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26470         
26471         var nodeIsBefore   =  ss == 1;
26472         var nodeIsAfter    = ee == -1;
26473         
26474         if (nodeIsBefore && nodeIsAfter) {
26475             return 0; // outer
26476         }
26477         if (!nodeIsBefore && nodeIsAfter) {
26478             return 1; //right trailed.
26479         }
26480         
26481         if (nodeIsBefore && !nodeIsAfter) {
26482             return 2;  // left trailed.
26483         }
26484         // fully contined.
26485         return 3;
26486     },
26487
26488     // private? - in a new class?
26489     cleanUpPaste :  function()
26490     {
26491         // cleans up the whole document..
26492         Roo.log('cleanuppaste');
26493         
26494         this.cleanUpChildren(this.doc.body);
26495         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26496         if (clean != this.doc.body.innerHTML) {
26497             this.doc.body.innerHTML = clean;
26498         }
26499         
26500     },
26501     
26502     cleanWordChars : function(input) {// change the chars to hex code
26503         var he = Roo.HtmlEditorCore;
26504         
26505         var output = input;
26506         Roo.each(he.swapCodes, function(sw) { 
26507             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26508             
26509             output = output.replace(swapper, sw[1]);
26510         });
26511         
26512         return output;
26513     },
26514     
26515     
26516     cleanUpChildren : function (n)
26517     {
26518         if (!n.childNodes.length) {
26519             return;
26520         }
26521         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26522            this.cleanUpChild(n.childNodes[i]);
26523         }
26524     },
26525     
26526     
26527         
26528     
26529     cleanUpChild : function (node)
26530     {
26531         var ed = this;
26532         //console.log(node);
26533         if (node.nodeName == "#text") {
26534             // clean up silly Windows -- stuff?
26535             return; 
26536         }
26537         if (node.nodeName == "#comment") {
26538             if (!this.allowComments) {
26539                 node.parentNode.removeChild(node);
26540             }
26541             // clean up silly Windows -- stuff?
26542             return; 
26543         }
26544         var lcname = node.tagName.toLowerCase();
26545         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26546         // whitelist of tags..
26547         
26548         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26549             // remove node.
26550             node.parentNode.removeChild(node);
26551             return;
26552             
26553         }
26554         
26555         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26556         
26557         // spans with no attributes - just remove them..
26558         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26559             remove_keep_children = true;
26560         }
26561         
26562         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26563         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26564         
26565         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26566         //    remove_keep_children = true;
26567         //}
26568         
26569         if (remove_keep_children) {
26570             this.cleanUpChildren(node);
26571             // inserts everything just before this node...
26572             while (node.childNodes.length) {
26573                 var cn = node.childNodes[0];
26574                 node.removeChild(cn);
26575                 node.parentNode.insertBefore(cn, node);
26576             }
26577             node.parentNode.removeChild(node);
26578             return;
26579         }
26580         
26581         if (!node.attributes || !node.attributes.length) {
26582             
26583           
26584             
26585             
26586             this.cleanUpChildren(node);
26587             return;
26588         }
26589         
26590         function cleanAttr(n,v)
26591         {
26592             
26593             if (v.match(/^\./) || v.match(/^\//)) {
26594                 return;
26595             }
26596             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26597                 return;
26598             }
26599             if (v.match(/^#/)) {
26600                 return;
26601             }
26602             if (v.match(/^\{/)) { // allow template editing.
26603                 return;
26604             }
26605 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26606             node.removeAttribute(n);
26607             
26608         }
26609         
26610         var cwhite = this.cwhite;
26611         var cblack = this.cblack;
26612             
26613         function cleanStyle(n,v)
26614         {
26615             if (v.match(/expression/)) { //XSS?? should we even bother..
26616                 node.removeAttribute(n);
26617                 return;
26618             }
26619             
26620             var parts = v.split(/;/);
26621             var clean = [];
26622             
26623             Roo.each(parts, function(p) {
26624                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26625                 if (!p.length) {
26626                     return true;
26627                 }
26628                 var l = p.split(':').shift().replace(/\s+/g,'');
26629                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26630                 
26631                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26632 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26633                     //node.removeAttribute(n);
26634                     return true;
26635                 }
26636                 //Roo.log()
26637                 // only allow 'c whitelisted system attributes'
26638                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26639 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26640                     //node.removeAttribute(n);
26641                     return true;
26642                 }
26643                 
26644                 
26645                  
26646                 
26647                 clean.push(p);
26648                 return true;
26649             });
26650             if (clean.length) { 
26651                 node.setAttribute(n, clean.join(';'));
26652             } else {
26653                 node.removeAttribute(n);
26654             }
26655             
26656         }
26657         
26658         
26659         for (var i = node.attributes.length-1; i > -1 ; i--) {
26660             var a = node.attributes[i];
26661             //console.log(a);
26662             
26663             if (a.name.toLowerCase().substr(0,2)=='on')  {
26664                 node.removeAttribute(a.name);
26665                 continue;
26666             }
26667             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26668                 node.removeAttribute(a.name);
26669                 continue;
26670             }
26671             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26672                 cleanAttr(a.name,a.value); // fixme..
26673                 continue;
26674             }
26675             if (a.name == 'style') {
26676                 cleanStyle(a.name,a.value);
26677                 continue;
26678             }
26679             /// clean up MS crap..
26680             // tecnically this should be a list of valid class'es..
26681             
26682             
26683             if (a.name == 'class') {
26684                 if (a.value.match(/^Mso/)) {
26685                     node.removeAttribute('class');
26686                 }
26687                 
26688                 if (a.value.match(/^body$/)) {
26689                     node.removeAttribute('class');
26690                 }
26691                 continue;
26692             }
26693             
26694             // style cleanup!?
26695             // class cleanup?
26696             
26697         }
26698         
26699         
26700         this.cleanUpChildren(node);
26701         
26702         
26703     },
26704     
26705     /**
26706      * Clean up MS wordisms...
26707      */
26708     cleanWord : function(node)
26709     {
26710         if (!node) {
26711             this.cleanWord(this.doc.body);
26712             return;
26713         }
26714         
26715         if(
26716                 node.nodeName == 'SPAN' &&
26717                 !node.hasAttributes() &&
26718                 node.childNodes.length == 1 &&
26719                 node.firstChild.nodeName == "#text"  
26720         ) {
26721             var textNode = node.firstChild;
26722             node.removeChild(textNode);
26723             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26724                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26725             }
26726             node.parentNode.insertBefore(textNode, node);
26727             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26728                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26729             }
26730             node.parentNode.removeChild(node);
26731         }
26732         
26733         if (node.nodeName == "#text") {
26734             // clean up silly Windows -- stuff?
26735             return; 
26736         }
26737         if (node.nodeName == "#comment") {
26738             node.parentNode.removeChild(node);
26739             // clean up silly Windows -- stuff?
26740             return; 
26741         }
26742         
26743         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26744             node.parentNode.removeChild(node);
26745             return;
26746         }
26747         //Roo.log(node.tagName);
26748         // remove - but keep children..
26749         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26750             //Roo.log('-- removed');
26751             while (node.childNodes.length) {
26752                 var cn = node.childNodes[0];
26753                 node.removeChild(cn);
26754                 node.parentNode.insertBefore(cn, node);
26755                 // move node to parent - and clean it..
26756                 this.cleanWord(cn);
26757             }
26758             node.parentNode.removeChild(node);
26759             /// no need to iterate chidlren = it's got none..
26760             //this.iterateChildren(node, this.cleanWord);
26761             return;
26762         }
26763         // clean styles
26764         if (node.className.length) {
26765             
26766             var cn = node.className.split(/\W+/);
26767             var cna = [];
26768             Roo.each(cn, function(cls) {
26769                 if (cls.match(/Mso[a-zA-Z]+/)) {
26770                     return;
26771                 }
26772                 cna.push(cls);
26773             });
26774             node.className = cna.length ? cna.join(' ') : '';
26775             if (!cna.length) {
26776                 node.removeAttribute("class");
26777             }
26778         }
26779         
26780         if (node.hasAttribute("lang")) {
26781             node.removeAttribute("lang");
26782         }
26783         
26784         if (node.hasAttribute("style")) {
26785             
26786             var styles = node.getAttribute("style").split(";");
26787             var nstyle = [];
26788             Roo.each(styles, function(s) {
26789                 if (!s.match(/:/)) {
26790                     return;
26791                 }
26792                 var kv = s.split(":");
26793                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26794                     return;
26795                 }
26796                 // what ever is left... we allow.
26797                 nstyle.push(s);
26798             });
26799             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26800             if (!nstyle.length) {
26801                 node.removeAttribute('style');
26802             }
26803         }
26804         this.iterateChildren(node, this.cleanWord);
26805         
26806         
26807         
26808     },
26809     /**
26810      * iterateChildren of a Node, calling fn each time, using this as the scole..
26811      * @param {DomNode} node node to iterate children of.
26812      * @param {Function} fn method of this class to call on each item.
26813      */
26814     iterateChildren : function(node, fn)
26815     {
26816         if (!node.childNodes.length) {
26817                 return;
26818         }
26819         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26820            fn.call(this, node.childNodes[i])
26821         }
26822     },
26823     
26824     
26825     /**
26826      * cleanTableWidths.
26827      *
26828      * Quite often pasting from word etc.. results in tables with column and widths.
26829      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26830      *
26831      */
26832     cleanTableWidths : function(node)
26833     {
26834          
26835          
26836         if (!node) {
26837             this.cleanTableWidths(this.doc.body);
26838             return;
26839         }
26840         
26841         // ignore list...
26842         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26843             return; 
26844         }
26845         Roo.log(node.tagName);
26846         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26847             this.iterateChildren(node, this.cleanTableWidths);
26848             return;
26849         }
26850         if (node.hasAttribute('width')) {
26851             node.removeAttribute('width');
26852         }
26853         
26854          
26855         if (node.hasAttribute("style")) {
26856             // pretty basic...
26857             
26858             var styles = node.getAttribute("style").split(";");
26859             var nstyle = [];
26860             Roo.each(styles, function(s) {
26861                 if (!s.match(/:/)) {
26862                     return;
26863                 }
26864                 var kv = s.split(":");
26865                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26866                     return;
26867                 }
26868                 // what ever is left... we allow.
26869                 nstyle.push(s);
26870             });
26871             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26872             if (!nstyle.length) {
26873                 node.removeAttribute('style');
26874             }
26875         }
26876         
26877         this.iterateChildren(node, this.cleanTableWidths);
26878         
26879         
26880     },
26881     
26882     
26883     
26884     
26885     domToHTML : function(currentElement, depth, nopadtext) {
26886         
26887         depth = depth || 0;
26888         nopadtext = nopadtext || false;
26889     
26890         if (!currentElement) {
26891             return this.domToHTML(this.doc.body);
26892         }
26893         
26894         //Roo.log(currentElement);
26895         var j;
26896         var allText = false;
26897         var nodeName = currentElement.nodeName;
26898         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26899         
26900         if  (nodeName == '#text') {
26901             
26902             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26903         }
26904         
26905         
26906         var ret = '';
26907         if (nodeName != 'BODY') {
26908              
26909             var i = 0;
26910             // Prints the node tagName, such as <A>, <IMG>, etc
26911             if (tagName) {
26912                 var attr = [];
26913                 for(i = 0; i < currentElement.attributes.length;i++) {
26914                     // quoting?
26915                     var aname = currentElement.attributes.item(i).name;
26916                     if (!currentElement.attributes.item(i).value.length) {
26917                         continue;
26918                     }
26919                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26920                 }
26921                 
26922                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26923             } 
26924             else {
26925                 
26926                 // eack
26927             }
26928         } else {
26929             tagName = false;
26930         }
26931         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26932             return ret;
26933         }
26934         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26935             nopadtext = true;
26936         }
26937         
26938         
26939         // Traverse the tree
26940         i = 0;
26941         var currentElementChild = currentElement.childNodes.item(i);
26942         var allText = true;
26943         var innerHTML  = '';
26944         lastnode = '';
26945         while (currentElementChild) {
26946             // Formatting code (indent the tree so it looks nice on the screen)
26947             var nopad = nopadtext;
26948             if (lastnode == 'SPAN') {
26949                 nopad  = true;
26950             }
26951             // text
26952             if  (currentElementChild.nodeName == '#text') {
26953                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26954                 toadd = nopadtext ? toadd : toadd.trim();
26955                 if (!nopad && toadd.length > 80) {
26956                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26957                 }
26958                 innerHTML  += toadd;
26959                 
26960                 i++;
26961                 currentElementChild = currentElement.childNodes.item(i);
26962                 lastNode = '';
26963                 continue;
26964             }
26965             allText = false;
26966             
26967             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26968                 
26969             // Recursively traverse the tree structure of the child node
26970             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26971             lastnode = currentElementChild.nodeName;
26972             i++;
26973             currentElementChild=currentElement.childNodes.item(i);
26974         }
26975         
26976         ret += innerHTML;
26977         
26978         if (!allText) {
26979                 // The remaining code is mostly for formatting the tree
26980             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26981         }
26982         
26983         
26984         if (tagName) {
26985             ret+= "</"+tagName+">";
26986         }
26987         return ret;
26988         
26989     },
26990         
26991     applyBlacklists : function()
26992     {
26993         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26994         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26995         
26996         this.white = [];
26997         this.black = [];
26998         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26999             if (b.indexOf(tag) > -1) {
27000                 return;
27001             }
27002             this.white.push(tag);
27003             
27004         }, this);
27005         
27006         Roo.each(w, function(tag) {
27007             if (b.indexOf(tag) > -1) {
27008                 return;
27009             }
27010             if (this.white.indexOf(tag) > -1) {
27011                 return;
27012             }
27013             this.white.push(tag);
27014             
27015         }, this);
27016         
27017         
27018         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27019             if (w.indexOf(tag) > -1) {
27020                 return;
27021             }
27022             this.black.push(tag);
27023             
27024         }, this);
27025         
27026         Roo.each(b, function(tag) {
27027             if (w.indexOf(tag) > -1) {
27028                 return;
27029             }
27030             if (this.black.indexOf(tag) > -1) {
27031                 return;
27032             }
27033             this.black.push(tag);
27034             
27035         }, this);
27036         
27037         
27038         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27039         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27040         
27041         this.cwhite = [];
27042         this.cblack = [];
27043         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27044             if (b.indexOf(tag) > -1) {
27045                 return;
27046             }
27047             this.cwhite.push(tag);
27048             
27049         }, this);
27050         
27051         Roo.each(w, function(tag) {
27052             if (b.indexOf(tag) > -1) {
27053                 return;
27054             }
27055             if (this.cwhite.indexOf(tag) > -1) {
27056                 return;
27057             }
27058             this.cwhite.push(tag);
27059             
27060         }, this);
27061         
27062         
27063         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27064             if (w.indexOf(tag) > -1) {
27065                 return;
27066             }
27067             this.cblack.push(tag);
27068             
27069         }, this);
27070         
27071         Roo.each(b, function(tag) {
27072             if (w.indexOf(tag) > -1) {
27073                 return;
27074             }
27075             if (this.cblack.indexOf(tag) > -1) {
27076                 return;
27077             }
27078             this.cblack.push(tag);
27079             
27080         }, this);
27081     },
27082     
27083     setStylesheets : function(stylesheets)
27084     {
27085         if(typeof(stylesheets) == 'string'){
27086             Roo.get(this.iframe.contentDocument.head).createChild({
27087                 tag : 'link',
27088                 rel : 'stylesheet',
27089                 type : 'text/css',
27090                 href : stylesheets
27091             });
27092             
27093             return;
27094         }
27095         var _this = this;
27096      
27097         Roo.each(stylesheets, function(s) {
27098             if(!s.length){
27099                 return;
27100             }
27101             
27102             Roo.get(_this.iframe.contentDocument.head).createChild({
27103                 tag : 'link',
27104                 rel : 'stylesheet',
27105                 type : 'text/css',
27106                 href : s
27107             });
27108         });
27109
27110         
27111     },
27112     
27113     removeStylesheets : function()
27114     {
27115         var _this = this;
27116         
27117         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27118             s.remove();
27119         });
27120     },
27121     
27122     setStyle : function(style)
27123     {
27124         Roo.get(this.iframe.contentDocument.head).createChild({
27125             tag : 'style',
27126             type : 'text/css',
27127             html : style
27128         });
27129
27130         return;
27131     }
27132     
27133     // hide stuff that is not compatible
27134     /**
27135      * @event blur
27136      * @hide
27137      */
27138     /**
27139      * @event change
27140      * @hide
27141      */
27142     /**
27143      * @event focus
27144      * @hide
27145      */
27146     /**
27147      * @event specialkey
27148      * @hide
27149      */
27150     /**
27151      * @cfg {String} fieldClass @hide
27152      */
27153     /**
27154      * @cfg {String} focusClass @hide
27155      */
27156     /**
27157      * @cfg {String} autoCreate @hide
27158      */
27159     /**
27160      * @cfg {String} inputType @hide
27161      */
27162     /**
27163      * @cfg {String} invalidClass @hide
27164      */
27165     /**
27166      * @cfg {String} invalidText @hide
27167      */
27168     /**
27169      * @cfg {String} msgFx @hide
27170      */
27171     /**
27172      * @cfg {String} validateOnBlur @hide
27173      */
27174 });
27175
27176 Roo.HtmlEditorCore.white = [
27177         'area', 'br', 'img', 'input', 'hr', 'wbr',
27178         
27179        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27180        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27181        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27182        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27183        'table',   'ul',         'xmp', 
27184        
27185        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27186       'thead',   'tr', 
27187      
27188       'dir', 'menu', 'ol', 'ul', 'dl',
27189        
27190       'embed',  'object'
27191 ];
27192
27193
27194 Roo.HtmlEditorCore.black = [
27195     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27196         'applet', // 
27197         'base',   'basefont', 'bgsound', 'blink',  'body', 
27198         'frame',  'frameset', 'head',    'html',   'ilayer', 
27199         'iframe', 'layer',  'link',     'meta',    'object',   
27200         'script', 'style' ,'title',  'xml' // clean later..
27201 ];
27202 Roo.HtmlEditorCore.clean = [
27203     'script', 'style', 'title', 'xml'
27204 ];
27205 Roo.HtmlEditorCore.remove = [
27206     'font'
27207 ];
27208 // attributes..
27209
27210 Roo.HtmlEditorCore.ablack = [
27211     'on'
27212 ];
27213     
27214 Roo.HtmlEditorCore.aclean = [ 
27215     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27216 ];
27217
27218 // protocols..
27219 Roo.HtmlEditorCore.pwhite= [
27220         'http',  'https',  'mailto'
27221 ];
27222
27223 // white listed style attributes.
27224 Roo.HtmlEditorCore.cwhite= [
27225       //  'text-align', /// default is to allow most things..
27226       
27227          
27228 //        'font-size'//??
27229 ];
27230
27231 // black listed style attributes.
27232 Roo.HtmlEditorCore.cblack= [
27233       //  'font-size' -- this can be set by the project 
27234 ];
27235
27236
27237 Roo.HtmlEditorCore.swapCodes   =[ 
27238     [    8211, "&#8211;" ], 
27239     [    8212, "&#8212;" ], 
27240     [    8216,  "'" ],  
27241     [    8217, "'" ],  
27242     [    8220, '"' ],  
27243     [    8221, '"' ],  
27244     [    8226, "*" ],  
27245     [    8230, "..." ]
27246 ]; 
27247
27248     /*
27249  * - LGPL
27250  *
27251  * HtmlEditor
27252  * 
27253  */
27254
27255 /**
27256  * @class Roo.bootstrap.HtmlEditor
27257  * @extends Roo.bootstrap.TextArea
27258  * Bootstrap HtmlEditor class
27259
27260  * @constructor
27261  * Create a new HtmlEditor
27262  * @param {Object} config The config object
27263  */
27264
27265 Roo.bootstrap.HtmlEditor = function(config){
27266     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27267     if (!this.toolbars) {
27268         this.toolbars = [];
27269     }
27270     
27271     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27272     this.addEvents({
27273             /**
27274              * @event initialize
27275              * Fires when the editor is fully initialized (including the iframe)
27276              * @param {HtmlEditor} this
27277              */
27278             initialize: true,
27279             /**
27280              * @event activate
27281              * Fires when the editor is first receives the focus. Any insertion must wait
27282              * until after this event.
27283              * @param {HtmlEditor} this
27284              */
27285             activate: true,
27286              /**
27287              * @event beforesync
27288              * Fires before the textarea is updated with content from the editor iframe. Return false
27289              * to cancel the sync.
27290              * @param {HtmlEditor} this
27291              * @param {String} html
27292              */
27293             beforesync: true,
27294              /**
27295              * @event beforepush
27296              * Fires before the iframe editor is updated with content from the textarea. Return false
27297              * to cancel the push.
27298              * @param {HtmlEditor} this
27299              * @param {String} html
27300              */
27301             beforepush: true,
27302              /**
27303              * @event sync
27304              * Fires when the textarea is updated with content from the editor iframe.
27305              * @param {HtmlEditor} this
27306              * @param {String} html
27307              */
27308             sync: true,
27309              /**
27310              * @event push
27311              * Fires when the iframe editor is updated with content from the textarea.
27312              * @param {HtmlEditor} this
27313              * @param {String} html
27314              */
27315             push: true,
27316              /**
27317              * @event editmodechange
27318              * Fires when the editor switches edit modes
27319              * @param {HtmlEditor} this
27320              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27321              */
27322             editmodechange: true,
27323             /**
27324              * @event editorevent
27325              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27326              * @param {HtmlEditor} this
27327              */
27328             editorevent: true,
27329             /**
27330              * @event firstfocus
27331              * Fires when on first focus - needed by toolbars..
27332              * @param {HtmlEditor} this
27333              */
27334             firstfocus: true,
27335             /**
27336              * @event autosave
27337              * Auto save the htmlEditor value as a file into Events
27338              * @param {HtmlEditor} this
27339              */
27340             autosave: true,
27341             /**
27342              * @event savedpreview
27343              * preview the saved version of htmlEditor
27344              * @param {HtmlEditor} this
27345              */
27346             savedpreview: true
27347         });
27348 };
27349
27350
27351 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27352     
27353     
27354       /**
27355      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27356      */
27357     toolbars : false,
27358     
27359      /**
27360     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27361     */
27362     btns : [],
27363    
27364      /**
27365      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27366      *                        Roo.resizable.
27367      */
27368     resizable : false,
27369      /**
27370      * @cfg {Number} height (in pixels)
27371      */   
27372     height: 300,
27373    /**
27374      * @cfg {Number} width (in pixels)
27375      */   
27376     width: false,
27377     
27378     /**
27379      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27380      * 
27381      */
27382     stylesheets: false,
27383     
27384     // id of frame..
27385     frameId: false,
27386     
27387     // private properties
27388     validationEvent : false,
27389     deferHeight: true,
27390     initialized : false,
27391     activated : false,
27392     
27393     onFocus : Roo.emptyFn,
27394     iframePad:3,
27395     hideMode:'offsets',
27396     
27397     tbContainer : false,
27398     
27399     bodyCls : '',
27400     
27401     toolbarContainer :function() {
27402         return this.wrap.select('.x-html-editor-tb',true).first();
27403     },
27404
27405     /**
27406      * Protected method that will not generally be called directly. It
27407      * is called when the editor creates its toolbar. Override this method if you need to
27408      * add custom toolbar buttons.
27409      * @param {HtmlEditor} editor
27410      */
27411     createToolbar : function(){
27412         Roo.log('renewing');
27413         Roo.log("create toolbars");
27414         
27415         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27416         this.toolbars[0].render(this.toolbarContainer());
27417         
27418         return;
27419         
27420 //        if (!editor.toolbars || !editor.toolbars.length) {
27421 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27422 //        }
27423 //        
27424 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27425 //            editor.toolbars[i] = Roo.factory(
27426 //                    typeof(editor.toolbars[i]) == 'string' ?
27427 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27428 //                Roo.bootstrap.HtmlEditor);
27429 //            editor.toolbars[i].init(editor);
27430 //        }
27431     },
27432
27433      
27434     // private
27435     onRender : function(ct, position)
27436     {
27437        // Roo.log("Call onRender: " + this.xtype);
27438         var _t = this;
27439         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27440       
27441         this.wrap = this.inputEl().wrap({
27442             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27443         });
27444         
27445         this.editorcore.onRender(ct, position);
27446          
27447         if (this.resizable) {
27448             this.resizeEl = new Roo.Resizable(this.wrap, {
27449                 pinned : true,
27450                 wrap: true,
27451                 dynamic : true,
27452                 minHeight : this.height,
27453                 height: this.height,
27454                 handles : this.resizable,
27455                 width: this.width,
27456                 listeners : {
27457                     resize : function(r, w, h) {
27458                         _t.onResize(w,h); // -something
27459                     }
27460                 }
27461             });
27462             
27463         }
27464         this.createToolbar(this);
27465        
27466         
27467         if(!this.width && this.resizable){
27468             this.setSize(this.wrap.getSize());
27469         }
27470         if (this.resizeEl) {
27471             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27472             // should trigger onReize..
27473         }
27474         
27475     },
27476
27477     // private
27478     onResize : function(w, h)
27479     {
27480         Roo.log('resize: ' +w + ',' + h );
27481         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27482         var ew = false;
27483         var eh = false;
27484         
27485         if(this.inputEl() ){
27486             if(typeof w == 'number'){
27487                 var aw = w - this.wrap.getFrameWidth('lr');
27488                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27489                 ew = aw;
27490             }
27491             if(typeof h == 'number'){
27492                  var tbh = -11;  // fixme it needs to tool bar size!
27493                 for (var i =0; i < this.toolbars.length;i++) {
27494                     // fixme - ask toolbars for heights?
27495                     tbh += this.toolbars[i].el.getHeight();
27496                     //if (this.toolbars[i].footer) {
27497                     //    tbh += this.toolbars[i].footer.el.getHeight();
27498                     //}
27499                 }
27500               
27501                 
27502                 
27503                 
27504                 
27505                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27506                 ah -= 5; // knock a few pixes off for look..
27507                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27508                 var eh = ah;
27509             }
27510         }
27511         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27512         this.editorcore.onResize(ew,eh);
27513         
27514     },
27515
27516     /**
27517      * Toggles the editor between standard and source edit mode.
27518      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27519      */
27520     toggleSourceEdit : function(sourceEditMode)
27521     {
27522         this.editorcore.toggleSourceEdit(sourceEditMode);
27523         
27524         if(this.editorcore.sourceEditMode){
27525             Roo.log('editor - showing textarea');
27526             
27527 //            Roo.log('in');
27528 //            Roo.log(this.syncValue());
27529             this.syncValue();
27530             this.inputEl().removeClass(['hide', 'x-hidden']);
27531             this.inputEl().dom.removeAttribute('tabIndex');
27532             this.inputEl().focus();
27533         }else{
27534             Roo.log('editor - hiding textarea');
27535 //            Roo.log('out')
27536 //            Roo.log(this.pushValue()); 
27537             this.pushValue();
27538             
27539             this.inputEl().addClass(['hide', 'x-hidden']);
27540             this.inputEl().dom.setAttribute('tabIndex', -1);
27541             //this.deferFocus();
27542         }
27543          
27544         if(this.resizable){
27545             this.setSize(this.wrap.getSize());
27546         }
27547         
27548         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27549     },
27550  
27551     // private (for BoxComponent)
27552     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27553
27554     // private (for BoxComponent)
27555     getResizeEl : function(){
27556         return this.wrap;
27557     },
27558
27559     // private (for BoxComponent)
27560     getPositionEl : function(){
27561         return this.wrap;
27562     },
27563
27564     // private
27565     initEvents : function(){
27566         this.originalValue = this.getValue();
27567     },
27568
27569 //    /**
27570 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27571 //     * @method
27572 //     */
27573 //    markInvalid : Roo.emptyFn,
27574 //    /**
27575 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27576 //     * @method
27577 //     */
27578 //    clearInvalid : Roo.emptyFn,
27579
27580     setValue : function(v){
27581         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27582         this.editorcore.pushValue();
27583     },
27584
27585      
27586     // private
27587     deferFocus : function(){
27588         this.focus.defer(10, this);
27589     },
27590
27591     // doc'ed in Field
27592     focus : function(){
27593         this.editorcore.focus();
27594         
27595     },
27596       
27597
27598     // private
27599     onDestroy : function(){
27600         
27601         
27602         
27603         if(this.rendered){
27604             
27605             for (var i =0; i < this.toolbars.length;i++) {
27606                 // fixme - ask toolbars for heights?
27607                 this.toolbars[i].onDestroy();
27608             }
27609             
27610             this.wrap.dom.innerHTML = '';
27611             this.wrap.remove();
27612         }
27613     },
27614
27615     // private
27616     onFirstFocus : function(){
27617         //Roo.log("onFirstFocus");
27618         this.editorcore.onFirstFocus();
27619          for (var i =0; i < this.toolbars.length;i++) {
27620             this.toolbars[i].onFirstFocus();
27621         }
27622         
27623     },
27624     
27625     // private
27626     syncValue : function()
27627     {   
27628         this.editorcore.syncValue();
27629     },
27630     
27631     pushValue : function()
27632     {   
27633         this.editorcore.pushValue();
27634     }
27635      
27636     
27637     // hide stuff that is not compatible
27638     /**
27639      * @event blur
27640      * @hide
27641      */
27642     /**
27643      * @event change
27644      * @hide
27645      */
27646     /**
27647      * @event focus
27648      * @hide
27649      */
27650     /**
27651      * @event specialkey
27652      * @hide
27653      */
27654     /**
27655      * @cfg {String} fieldClass @hide
27656      */
27657     /**
27658      * @cfg {String} focusClass @hide
27659      */
27660     /**
27661      * @cfg {String} autoCreate @hide
27662      */
27663     /**
27664      * @cfg {String} inputType @hide
27665      */
27666      
27667     /**
27668      * @cfg {String} invalidText @hide
27669      */
27670     /**
27671      * @cfg {String} msgFx @hide
27672      */
27673     /**
27674      * @cfg {String} validateOnBlur @hide
27675      */
27676 });
27677  
27678     
27679    
27680    
27681    
27682       
27683 Roo.namespace('Roo.bootstrap.htmleditor');
27684 /**
27685  * @class Roo.bootstrap.HtmlEditorToolbar1
27686  * Basic Toolbar
27687  * 
27688  * @example
27689  * Usage:
27690  *
27691  new Roo.bootstrap.HtmlEditor({
27692     ....
27693     toolbars : [
27694         new Roo.bootstrap.HtmlEditorToolbar1({
27695             disable : { fonts: 1 , format: 1, ..., ... , ...],
27696             btns : [ .... ]
27697         })
27698     }
27699      
27700  * 
27701  * @cfg {Object} disable List of elements to disable..
27702  * @cfg {Array} btns List of additional buttons.
27703  * 
27704  * 
27705  * NEEDS Extra CSS? 
27706  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27707  */
27708  
27709 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27710 {
27711     
27712     Roo.apply(this, config);
27713     
27714     // default disabled, based on 'good practice'..
27715     this.disable = this.disable || {};
27716     Roo.applyIf(this.disable, {
27717         fontSize : true,
27718         colors : true,
27719         specialElements : true
27720     });
27721     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27722     
27723     this.editor = config.editor;
27724     this.editorcore = config.editor.editorcore;
27725     
27726     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27727     
27728     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27729     // dont call parent... till later.
27730 }
27731 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27732      
27733     bar : true,
27734     
27735     editor : false,
27736     editorcore : false,
27737     
27738     
27739     formats : [
27740         "p" ,  
27741         "h1","h2","h3","h4","h5","h6", 
27742         "pre", "code", 
27743         "abbr", "acronym", "address", "cite", "samp", "var",
27744         'div','span'
27745     ],
27746     
27747     onRender : function(ct, position)
27748     {
27749        // Roo.log("Call onRender: " + this.xtype);
27750         
27751        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27752        Roo.log(this.el);
27753        this.el.dom.style.marginBottom = '0';
27754        var _this = this;
27755        var editorcore = this.editorcore;
27756        var editor= this.editor;
27757        
27758        var children = [];
27759        var btn = function(id,cmd , toggle, handler, html){
27760        
27761             var  event = toggle ? 'toggle' : 'click';
27762        
27763             var a = {
27764                 size : 'sm',
27765                 xtype: 'Button',
27766                 xns: Roo.bootstrap,
27767                 //glyphicon : id,
27768                 fa: id,
27769                 cmd : id || cmd,
27770                 enableToggle:toggle !== false,
27771                 html : html || '',
27772                 pressed : toggle ? false : null,
27773                 listeners : {}
27774             };
27775             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27776                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27777             };
27778             children.push(a);
27779             return a;
27780        }
27781        
27782     //    var cb_box = function...
27783         
27784         var style = {
27785                 xtype: 'Button',
27786                 size : 'sm',
27787                 xns: Roo.bootstrap,
27788                 fa : 'font',
27789                 //html : 'submit'
27790                 menu : {
27791                     xtype: 'Menu',
27792                     xns: Roo.bootstrap,
27793                     items:  []
27794                 }
27795         };
27796         Roo.each(this.formats, function(f) {
27797             style.menu.items.push({
27798                 xtype :'MenuItem',
27799                 xns: Roo.bootstrap,
27800                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27801                 tagname : f,
27802                 listeners : {
27803                     click : function()
27804                     {
27805                         editorcore.insertTag(this.tagname);
27806                         editor.focus();
27807                     }
27808                 }
27809                 
27810             });
27811         });
27812         children.push(style);   
27813         
27814         btn('bold',false,true);
27815         btn('italic',false,true);
27816         btn('align-left', 'justifyleft',true);
27817         btn('align-center', 'justifycenter',true);
27818         btn('align-right' , 'justifyright',true);
27819         btn('link', false, false, function(btn) {
27820             //Roo.log("create link?");
27821             var url = prompt(this.createLinkText, this.defaultLinkValue);
27822             if(url && url != 'http:/'+'/'){
27823                 this.editorcore.relayCmd('createlink', url);
27824             }
27825         }),
27826         btn('list','insertunorderedlist',true);
27827         btn('pencil', false,true, function(btn){
27828                 Roo.log(this);
27829                 this.toggleSourceEdit(btn.pressed);
27830         });
27831         
27832         if (this.editor.btns.length > 0) {
27833             for (var i = 0; i<this.editor.btns.length; i++) {
27834                 children.push(this.editor.btns[i]);
27835             }
27836         }
27837         
27838         /*
27839         var cog = {
27840                 xtype: 'Button',
27841                 size : 'sm',
27842                 xns: Roo.bootstrap,
27843                 glyphicon : 'cog',
27844                 //html : 'submit'
27845                 menu : {
27846                     xtype: 'Menu',
27847                     xns: Roo.bootstrap,
27848                     items:  []
27849                 }
27850         };
27851         
27852         cog.menu.items.push({
27853             xtype :'MenuItem',
27854             xns: Roo.bootstrap,
27855             html : Clean styles,
27856             tagname : f,
27857             listeners : {
27858                 click : function()
27859                 {
27860                     editorcore.insertTag(this.tagname);
27861                     editor.focus();
27862                 }
27863             }
27864             
27865         });
27866        */
27867         
27868          
27869        this.xtype = 'NavSimplebar';
27870         
27871         for(var i=0;i< children.length;i++) {
27872             
27873             this.buttons.add(this.addxtypeChild(children[i]));
27874             
27875         }
27876         
27877         editor.on('editorevent', this.updateToolbar, this);
27878     },
27879     onBtnClick : function(id)
27880     {
27881        this.editorcore.relayCmd(id);
27882        this.editorcore.focus();
27883     },
27884     
27885     /**
27886      * Protected method that will not generally be called directly. It triggers
27887      * a toolbar update by reading the markup state of the current selection in the editor.
27888      */
27889     updateToolbar: function(){
27890
27891         if(!this.editorcore.activated){
27892             this.editor.onFirstFocus(); // is this neeed?
27893             return;
27894         }
27895
27896         var btns = this.buttons; 
27897         var doc = this.editorcore.doc;
27898         btns.get('bold').setActive(doc.queryCommandState('bold'));
27899         btns.get('italic').setActive(doc.queryCommandState('italic'));
27900         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27901         
27902         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27903         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27904         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27905         
27906         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27907         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27908          /*
27909         
27910         var ans = this.editorcore.getAllAncestors();
27911         if (this.formatCombo) {
27912             
27913             
27914             var store = this.formatCombo.store;
27915             this.formatCombo.setValue("");
27916             for (var i =0; i < ans.length;i++) {
27917                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27918                     // select it..
27919                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27920                     break;
27921                 }
27922             }
27923         }
27924         
27925         
27926         
27927         // hides menus... - so this cant be on a menu...
27928         Roo.bootstrap.MenuMgr.hideAll();
27929         */
27930         Roo.bootstrap.MenuMgr.hideAll();
27931         //this.editorsyncValue();
27932     },
27933     onFirstFocus: function() {
27934         this.buttons.each(function(item){
27935            item.enable();
27936         });
27937     },
27938     toggleSourceEdit : function(sourceEditMode){
27939         
27940           
27941         if(sourceEditMode){
27942             Roo.log("disabling buttons");
27943            this.buttons.each( function(item){
27944                 if(item.cmd != 'pencil'){
27945                     item.disable();
27946                 }
27947             });
27948           
27949         }else{
27950             Roo.log("enabling buttons");
27951             if(this.editorcore.initialized){
27952                 this.buttons.each( function(item){
27953                     item.enable();
27954                 });
27955             }
27956             
27957         }
27958         Roo.log("calling toggole on editor");
27959         // tell the editor that it's been pressed..
27960         this.editor.toggleSourceEdit(sourceEditMode);
27961        
27962     }
27963 });
27964
27965
27966
27967
27968  
27969 /*
27970  * - LGPL
27971  */
27972
27973 /**
27974  * @class Roo.bootstrap.Markdown
27975  * @extends Roo.bootstrap.TextArea
27976  * Bootstrap Showdown editable area
27977  * @cfg {string} content
27978  * 
27979  * @constructor
27980  * Create a new Showdown
27981  */
27982
27983 Roo.bootstrap.Markdown = function(config){
27984     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27985    
27986 };
27987
27988 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27989     
27990     editing :false,
27991     
27992     initEvents : function()
27993     {
27994         
27995         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27996         this.markdownEl = this.el.createChild({
27997             cls : 'roo-markdown-area'
27998         });
27999         this.inputEl().addClass('d-none');
28000         if (this.getValue() == '') {
28001             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28002             
28003         } else {
28004             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28005         }
28006         this.markdownEl.on('click', this.toggleTextEdit, this);
28007         this.on('blur', this.toggleTextEdit, this);
28008         this.on('specialkey', this.resizeTextArea, this);
28009     },
28010     
28011     toggleTextEdit : function()
28012     {
28013         var sh = this.markdownEl.getHeight();
28014         this.inputEl().addClass('d-none');
28015         this.markdownEl.addClass('d-none');
28016         if (!this.editing) {
28017             // show editor?
28018             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28019             this.inputEl().removeClass('d-none');
28020             this.inputEl().focus();
28021             this.editing = true;
28022             return;
28023         }
28024         // show showdown...
28025         this.updateMarkdown();
28026         this.markdownEl.removeClass('d-none');
28027         this.editing = false;
28028         return;
28029     },
28030     updateMarkdown : function()
28031     {
28032         if (this.getValue() == '') {
28033             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28034             return;
28035         }
28036  
28037         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28038     },
28039     
28040     resizeTextArea: function () {
28041         
28042         var sh = 100;
28043         Roo.log([sh, this.getValue().split("\n").length * 30]);
28044         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28045     },
28046     setValue : function(val)
28047     {
28048         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28049         if (!this.editing) {
28050             this.updateMarkdown();
28051         }
28052         
28053     },
28054     focus : function()
28055     {
28056         if (!this.editing) {
28057             this.toggleTextEdit();
28058         }
28059         
28060     }
28061
28062
28063 });/*
28064  * Based on:
28065  * Ext JS Library 1.1.1
28066  * Copyright(c) 2006-2007, Ext JS, LLC.
28067  *
28068  * Originally Released Under LGPL - original licence link has changed is not relivant.
28069  *
28070  * Fork - LGPL
28071  * <script type="text/javascript">
28072  */
28073  
28074 /**
28075  * @class Roo.bootstrap.PagingToolbar
28076  * @extends Roo.bootstrap.NavSimplebar
28077  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28078  * @constructor
28079  * Create a new PagingToolbar
28080  * @param {Object} config The config object
28081  * @param {Roo.data.Store} store
28082  */
28083 Roo.bootstrap.PagingToolbar = function(config)
28084 {
28085     // old args format still supported... - xtype is prefered..
28086         // created from xtype...
28087     
28088     this.ds = config.dataSource;
28089     
28090     if (config.store && !this.ds) {
28091         this.store= Roo.factory(config.store, Roo.data);
28092         this.ds = this.store;
28093         this.ds.xmodule = this.xmodule || false;
28094     }
28095     
28096     this.toolbarItems = [];
28097     if (config.items) {
28098         this.toolbarItems = config.items;
28099     }
28100     
28101     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28102     
28103     this.cursor = 0;
28104     
28105     if (this.ds) { 
28106         this.bind(this.ds);
28107     }
28108     
28109     if (Roo.bootstrap.version == 4) {
28110         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28111     } else {
28112         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28113     }
28114     
28115 };
28116
28117 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28118     /**
28119      * @cfg {Roo.data.Store} dataSource
28120      * The underlying data store providing the paged data
28121      */
28122     /**
28123      * @cfg {String/HTMLElement/Element} container
28124      * container The id or element that will contain the toolbar
28125      */
28126     /**
28127      * @cfg {Boolean} displayInfo
28128      * True to display the displayMsg (defaults to false)
28129      */
28130     /**
28131      * @cfg {Number} pageSize
28132      * The number of records to display per page (defaults to 20)
28133      */
28134     pageSize: 20,
28135     /**
28136      * @cfg {String} displayMsg
28137      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28138      */
28139     displayMsg : 'Displaying {0} - {1} of {2}',
28140     /**
28141      * @cfg {String} emptyMsg
28142      * The message to display when no records are found (defaults to "No data to display")
28143      */
28144     emptyMsg : 'No data to display',
28145     /**
28146      * Customizable piece of the default paging text (defaults to "Page")
28147      * @type String
28148      */
28149     beforePageText : "Page",
28150     /**
28151      * Customizable piece of the default paging text (defaults to "of %0")
28152      * @type String
28153      */
28154     afterPageText : "of {0}",
28155     /**
28156      * Customizable piece of the default paging text (defaults to "First Page")
28157      * @type String
28158      */
28159     firstText : "First Page",
28160     /**
28161      * Customizable piece of the default paging text (defaults to "Previous Page")
28162      * @type String
28163      */
28164     prevText : "Previous Page",
28165     /**
28166      * Customizable piece of the default paging text (defaults to "Next Page")
28167      * @type String
28168      */
28169     nextText : "Next Page",
28170     /**
28171      * Customizable piece of the default paging text (defaults to "Last Page")
28172      * @type String
28173      */
28174     lastText : "Last Page",
28175     /**
28176      * Customizable piece of the default paging text (defaults to "Refresh")
28177      * @type String
28178      */
28179     refreshText : "Refresh",
28180
28181     buttons : false,
28182     // private
28183     onRender : function(ct, position) 
28184     {
28185         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28186         this.navgroup.parentId = this.id;
28187         this.navgroup.onRender(this.el, null);
28188         // add the buttons to the navgroup
28189         
28190         if(this.displayInfo){
28191             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28192             this.displayEl = this.el.select('.x-paging-info', true).first();
28193 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28194 //            this.displayEl = navel.el.select('span',true).first();
28195         }
28196         
28197         var _this = this;
28198         
28199         if(this.buttons){
28200             Roo.each(_this.buttons, function(e){ // this might need to use render????
28201                Roo.factory(e).render(_this.el);
28202             });
28203         }
28204             
28205         Roo.each(_this.toolbarItems, function(e) {
28206             _this.navgroup.addItem(e);
28207         });
28208         
28209         
28210         this.first = this.navgroup.addItem({
28211             tooltip: this.firstText,
28212             cls: "prev btn-outline-secondary",
28213             html : ' <i class="fa fa-step-backward"></i>',
28214             disabled: true,
28215             preventDefault: true,
28216             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28217         });
28218         
28219         this.prev =  this.navgroup.addItem({
28220             tooltip: this.prevText,
28221             cls: "prev btn-outline-secondary",
28222             html : ' <i class="fa fa-backward"></i>',
28223             disabled: true,
28224             preventDefault: true,
28225             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28226         });
28227     //this.addSeparator();
28228         
28229         
28230         var field = this.navgroup.addItem( {
28231             tagtype : 'span',
28232             cls : 'x-paging-position  btn-outline-secondary',
28233              disabled: true,
28234             html : this.beforePageText  +
28235                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28236                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28237          } ); //?? escaped?
28238         
28239         this.field = field.el.select('input', true).first();
28240         this.field.on("keydown", this.onPagingKeydown, this);
28241         this.field.on("focus", function(){this.dom.select();});
28242     
28243     
28244         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28245         //this.field.setHeight(18);
28246         //this.addSeparator();
28247         this.next = this.navgroup.addItem({
28248             tooltip: this.nextText,
28249             cls: "next btn-outline-secondary",
28250             html : ' <i class="fa fa-forward"></i>',
28251             disabled: true,
28252             preventDefault: true,
28253             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28254         });
28255         this.last = this.navgroup.addItem({
28256             tooltip: this.lastText,
28257             html : ' <i class="fa fa-step-forward"></i>',
28258             cls: "next btn-outline-secondary",
28259             disabled: true,
28260             preventDefault: true,
28261             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28262         });
28263     //this.addSeparator();
28264         this.loading = this.navgroup.addItem({
28265             tooltip: this.refreshText,
28266             cls: "btn-outline-secondary",
28267             html : ' <i class="fa fa-refresh"></i>',
28268             preventDefault: true,
28269             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28270         });
28271         
28272     },
28273
28274     // private
28275     updateInfo : function(){
28276         if(this.displayEl){
28277             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28278             var msg = count == 0 ?
28279                 this.emptyMsg :
28280                 String.format(
28281                     this.displayMsg,
28282                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28283                 );
28284             this.displayEl.update(msg);
28285         }
28286     },
28287
28288     // private
28289     onLoad : function(ds, r, o)
28290     {
28291         this.cursor = o.params && o.params.start ? o.params.start : 0;
28292         
28293         var d = this.getPageData(),
28294             ap = d.activePage,
28295             ps = d.pages;
28296         
28297         
28298         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28299         this.field.dom.value = ap;
28300         this.first.setDisabled(ap == 1);
28301         this.prev.setDisabled(ap == 1);
28302         this.next.setDisabled(ap == ps);
28303         this.last.setDisabled(ap == ps);
28304         this.loading.enable();
28305         this.updateInfo();
28306     },
28307
28308     // private
28309     getPageData : function(){
28310         var total = this.ds.getTotalCount();
28311         return {
28312             total : total,
28313             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28314             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28315         };
28316     },
28317
28318     // private
28319     onLoadError : function(){
28320         this.loading.enable();
28321     },
28322
28323     // private
28324     onPagingKeydown : function(e){
28325         var k = e.getKey();
28326         var d = this.getPageData();
28327         if(k == e.RETURN){
28328             var v = this.field.dom.value, pageNum;
28329             if(!v || isNaN(pageNum = parseInt(v, 10))){
28330                 this.field.dom.value = d.activePage;
28331                 return;
28332             }
28333             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28334             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28335             e.stopEvent();
28336         }
28337         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))
28338         {
28339           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28340           this.field.dom.value = pageNum;
28341           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28342           e.stopEvent();
28343         }
28344         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28345         {
28346           var v = this.field.dom.value, pageNum; 
28347           var increment = (e.shiftKey) ? 10 : 1;
28348           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28349                 increment *= -1;
28350           }
28351           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28352             this.field.dom.value = d.activePage;
28353             return;
28354           }
28355           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28356           {
28357             this.field.dom.value = parseInt(v, 10) + increment;
28358             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28359             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28360           }
28361           e.stopEvent();
28362         }
28363     },
28364
28365     // private
28366     beforeLoad : function(){
28367         if(this.loading){
28368             this.loading.disable();
28369         }
28370     },
28371
28372     // private
28373     onClick : function(which){
28374         
28375         var ds = this.ds;
28376         if (!ds) {
28377             return;
28378         }
28379         
28380         switch(which){
28381             case "first":
28382                 ds.load({params:{start: 0, limit: this.pageSize}});
28383             break;
28384             case "prev":
28385                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28386             break;
28387             case "next":
28388                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28389             break;
28390             case "last":
28391                 var total = ds.getTotalCount();
28392                 var extra = total % this.pageSize;
28393                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28394                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28395             break;
28396             case "refresh":
28397                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28398             break;
28399         }
28400     },
28401
28402     /**
28403      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28404      * @param {Roo.data.Store} store The data store to unbind
28405      */
28406     unbind : function(ds){
28407         ds.un("beforeload", this.beforeLoad, this);
28408         ds.un("load", this.onLoad, this);
28409         ds.un("loadexception", this.onLoadError, this);
28410         ds.un("remove", this.updateInfo, this);
28411         ds.un("add", this.updateInfo, this);
28412         this.ds = undefined;
28413     },
28414
28415     /**
28416      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28417      * @param {Roo.data.Store} store The data store to bind
28418      */
28419     bind : function(ds){
28420         ds.on("beforeload", this.beforeLoad, this);
28421         ds.on("load", this.onLoad, this);
28422         ds.on("loadexception", this.onLoadError, this);
28423         ds.on("remove", this.updateInfo, this);
28424         ds.on("add", this.updateInfo, this);
28425         this.ds = ds;
28426     }
28427 });/*
28428  * - LGPL
28429  *
28430  * element
28431  * 
28432  */
28433
28434 /**
28435  * @class Roo.bootstrap.MessageBar
28436  * @extends Roo.bootstrap.Component
28437  * Bootstrap MessageBar class
28438  * @cfg {String} html contents of the MessageBar
28439  * @cfg {String} weight (info | success | warning | danger) default info
28440  * @cfg {String} beforeClass insert the bar before the given class
28441  * @cfg {Boolean} closable (true | false) default false
28442  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28443  * 
28444  * @constructor
28445  * Create a new Element
28446  * @param {Object} config The config object
28447  */
28448
28449 Roo.bootstrap.MessageBar = function(config){
28450     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28451 };
28452
28453 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28454     
28455     html: '',
28456     weight: 'info',
28457     closable: false,
28458     fixed: false,
28459     beforeClass: 'bootstrap-sticky-wrap',
28460     
28461     getAutoCreate : function(){
28462         
28463         var cfg = {
28464             tag: 'div',
28465             cls: 'alert alert-dismissable alert-' + this.weight,
28466             cn: [
28467                 {
28468                     tag: 'span',
28469                     cls: 'message',
28470                     html: this.html || ''
28471                 }
28472             ]
28473         };
28474         
28475         if(this.fixed){
28476             cfg.cls += ' alert-messages-fixed';
28477         }
28478         
28479         if(this.closable){
28480             cfg.cn.push({
28481                 tag: 'button',
28482                 cls: 'close',
28483                 html: 'x'
28484             });
28485         }
28486         
28487         return cfg;
28488     },
28489     
28490     onRender : function(ct, position)
28491     {
28492         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28493         
28494         if(!this.el){
28495             var cfg = Roo.apply({},  this.getAutoCreate());
28496             cfg.id = Roo.id();
28497             
28498             if (this.cls) {
28499                 cfg.cls += ' ' + this.cls;
28500             }
28501             if (this.style) {
28502                 cfg.style = this.style;
28503             }
28504             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28505             
28506             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28507         }
28508         
28509         this.el.select('>button.close').on('click', this.hide, this);
28510         
28511     },
28512     
28513     show : function()
28514     {
28515         if (!this.rendered) {
28516             this.render();
28517         }
28518         
28519         this.el.show();
28520         
28521         this.fireEvent('show', this);
28522         
28523     },
28524     
28525     hide : function()
28526     {
28527         if (!this.rendered) {
28528             this.render();
28529         }
28530         
28531         this.el.hide();
28532         
28533         this.fireEvent('hide', this);
28534     },
28535     
28536     update : function()
28537     {
28538 //        var e = this.el.dom.firstChild;
28539 //        
28540 //        if(this.closable){
28541 //            e = e.nextSibling;
28542 //        }
28543 //        
28544 //        e.data = this.html || '';
28545
28546         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28547     }
28548    
28549 });
28550
28551  
28552
28553      /*
28554  * - LGPL
28555  *
28556  * Graph
28557  * 
28558  */
28559
28560
28561 /**
28562  * @class Roo.bootstrap.Graph
28563  * @extends Roo.bootstrap.Component
28564  * Bootstrap Graph class
28565 > Prameters
28566  -sm {number} sm 4
28567  -md {number} md 5
28568  @cfg {String} graphtype  bar | vbar | pie
28569  @cfg {number} g_x coodinator | centre x (pie)
28570  @cfg {number} g_y coodinator | centre y (pie)
28571  @cfg {number} g_r radius (pie)
28572  @cfg {number} g_height height of the chart (respected by all elements in the set)
28573  @cfg {number} g_width width of the chart (respected by all elements in the set)
28574  @cfg {Object} title The title of the chart
28575     
28576  -{Array}  values
28577  -opts (object) options for the chart 
28578      o {
28579      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28580      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28581      o vgutter (number)
28582      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.
28583      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28584      o to
28585      o stretch (boolean)
28586      o }
28587  -opts (object) options for the pie
28588      o{
28589      o cut
28590      o startAngle (number)
28591      o endAngle (number)
28592      } 
28593  *
28594  * @constructor
28595  * Create a new Input
28596  * @param {Object} config The config object
28597  */
28598
28599 Roo.bootstrap.Graph = function(config){
28600     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28601     
28602     this.addEvents({
28603         // img events
28604         /**
28605          * @event click
28606          * The img click event for the img.
28607          * @param {Roo.EventObject} e
28608          */
28609         "click" : true
28610     });
28611 };
28612
28613 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28614     
28615     sm: 4,
28616     md: 5,
28617     graphtype: 'bar',
28618     g_height: 250,
28619     g_width: 400,
28620     g_x: 50,
28621     g_y: 50,
28622     g_r: 30,
28623     opts:{
28624         //g_colors: this.colors,
28625         g_type: 'soft',
28626         g_gutter: '20%'
28627
28628     },
28629     title : false,
28630
28631     getAutoCreate : function(){
28632         
28633         var cfg = {
28634             tag: 'div',
28635             html : null
28636         };
28637         
28638         
28639         return  cfg;
28640     },
28641
28642     onRender : function(ct,position){
28643         
28644         
28645         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28646         
28647         if (typeof(Raphael) == 'undefined') {
28648             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28649             return;
28650         }
28651         
28652         this.raphael = Raphael(this.el.dom);
28653         
28654                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28655                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28656                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28657                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28658                 /*
28659                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28660                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28661                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28662                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28663                 
28664                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28665                 r.barchart(330, 10, 300, 220, data1);
28666                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28667                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28668                 */
28669                 
28670                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28671                 // r.barchart(30, 30, 560, 250,  xdata, {
28672                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28673                 //     axis : "0 0 1 1",
28674                 //     axisxlabels :  xdata
28675                 //     //yvalues : cols,
28676                    
28677                 // });
28678 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28679 //        
28680 //        this.load(null,xdata,{
28681 //                axis : "0 0 1 1",
28682 //                axisxlabels :  xdata
28683 //                });
28684
28685     },
28686
28687     load : function(graphtype,xdata,opts)
28688     {
28689         this.raphael.clear();
28690         if(!graphtype) {
28691             graphtype = this.graphtype;
28692         }
28693         if(!opts){
28694             opts = this.opts;
28695         }
28696         var r = this.raphael,
28697             fin = function () {
28698                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28699             },
28700             fout = function () {
28701                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28702             },
28703             pfin = function() {
28704                 this.sector.stop();
28705                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28706
28707                 if (this.label) {
28708                     this.label[0].stop();
28709                     this.label[0].attr({ r: 7.5 });
28710                     this.label[1].attr({ "font-weight": 800 });
28711                 }
28712             },
28713             pfout = function() {
28714                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28715
28716                 if (this.label) {
28717                     this.label[0].animate({ r: 5 }, 500, "bounce");
28718                     this.label[1].attr({ "font-weight": 400 });
28719                 }
28720             };
28721
28722         switch(graphtype){
28723             case 'bar':
28724                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28725                 break;
28726             case 'hbar':
28727                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28728                 break;
28729             case 'pie':
28730 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28731 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28732 //            
28733                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28734                 
28735                 break;
28736
28737         }
28738         
28739         if(this.title){
28740             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28741         }
28742         
28743     },
28744     
28745     setTitle: function(o)
28746     {
28747         this.title = o;
28748     },
28749     
28750     initEvents: function() {
28751         
28752         if(!this.href){
28753             this.el.on('click', this.onClick, this);
28754         }
28755     },
28756     
28757     onClick : function(e)
28758     {
28759         Roo.log('img onclick');
28760         this.fireEvent('click', this, e);
28761     }
28762    
28763 });
28764
28765  
28766 /*
28767  * - LGPL
28768  *
28769  * numberBox
28770  * 
28771  */
28772 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28773
28774 /**
28775  * @class Roo.bootstrap.dash.NumberBox
28776  * @extends Roo.bootstrap.Component
28777  * Bootstrap NumberBox class
28778  * @cfg {String} headline Box headline
28779  * @cfg {String} content Box content
28780  * @cfg {String} icon Box icon
28781  * @cfg {String} footer Footer text
28782  * @cfg {String} fhref Footer href
28783  * 
28784  * @constructor
28785  * Create a new NumberBox
28786  * @param {Object} config The config object
28787  */
28788
28789
28790 Roo.bootstrap.dash.NumberBox = function(config){
28791     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28792     
28793 };
28794
28795 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28796     
28797     headline : '',
28798     content : '',
28799     icon : '',
28800     footer : '',
28801     fhref : '',
28802     ficon : '',
28803     
28804     getAutoCreate : function(){
28805         
28806         var cfg = {
28807             tag : 'div',
28808             cls : 'small-box ',
28809             cn : [
28810                 {
28811                     tag : 'div',
28812                     cls : 'inner',
28813                     cn :[
28814                         {
28815                             tag : 'h3',
28816                             cls : 'roo-headline',
28817                             html : this.headline
28818                         },
28819                         {
28820                             tag : 'p',
28821                             cls : 'roo-content',
28822                             html : this.content
28823                         }
28824                     ]
28825                 }
28826             ]
28827         };
28828         
28829         if(this.icon){
28830             cfg.cn.push({
28831                 tag : 'div',
28832                 cls : 'icon',
28833                 cn :[
28834                     {
28835                         tag : 'i',
28836                         cls : 'ion ' + this.icon
28837                     }
28838                 ]
28839             });
28840         }
28841         
28842         if(this.footer){
28843             var footer = {
28844                 tag : 'a',
28845                 cls : 'small-box-footer',
28846                 href : this.fhref || '#',
28847                 html : this.footer
28848             };
28849             
28850             cfg.cn.push(footer);
28851             
28852         }
28853         
28854         return  cfg;
28855     },
28856
28857     onRender : function(ct,position){
28858         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28859
28860
28861        
28862                 
28863     },
28864
28865     setHeadline: function (value)
28866     {
28867         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28868     },
28869     
28870     setFooter: function (value, href)
28871     {
28872         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28873         
28874         if(href){
28875             this.el.select('a.small-box-footer',true).first().attr('href', href);
28876         }
28877         
28878     },
28879
28880     setContent: function (value)
28881     {
28882         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28883     },
28884
28885     initEvents: function() 
28886     {   
28887         
28888     }
28889     
28890 });
28891
28892  
28893 /*
28894  * - LGPL
28895  *
28896  * TabBox
28897  * 
28898  */
28899 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28900
28901 /**
28902  * @class Roo.bootstrap.dash.TabBox
28903  * @extends Roo.bootstrap.Component
28904  * Bootstrap TabBox class
28905  * @cfg {String} title Title of the TabBox
28906  * @cfg {String} icon Icon of the TabBox
28907  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28908  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28909  * 
28910  * @constructor
28911  * Create a new TabBox
28912  * @param {Object} config The config object
28913  */
28914
28915
28916 Roo.bootstrap.dash.TabBox = function(config){
28917     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28918     this.addEvents({
28919         // raw events
28920         /**
28921          * @event addpane
28922          * When a pane is added
28923          * @param {Roo.bootstrap.dash.TabPane} pane
28924          */
28925         "addpane" : true,
28926         /**
28927          * @event activatepane
28928          * When a pane is activated
28929          * @param {Roo.bootstrap.dash.TabPane} pane
28930          */
28931         "activatepane" : true
28932         
28933          
28934     });
28935     
28936     this.panes = [];
28937 };
28938
28939 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28940
28941     title : '',
28942     icon : false,
28943     showtabs : true,
28944     tabScrollable : false,
28945     
28946     getChildContainer : function()
28947     {
28948         return this.el.select('.tab-content', true).first();
28949     },
28950     
28951     getAutoCreate : function(){
28952         
28953         var header = {
28954             tag: 'li',
28955             cls: 'pull-left header',
28956             html: this.title,
28957             cn : []
28958         };
28959         
28960         if(this.icon){
28961             header.cn.push({
28962                 tag: 'i',
28963                 cls: 'fa ' + this.icon
28964             });
28965         }
28966         
28967         var h = {
28968             tag: 'ul',
28969             cls: 'nav nav-tabs pull-right',
28970             cn: [
28971                 header
28972             ]
28973         };
28974         
28975         if(this.tabScrollable){
28976             h = {
28977                 tag: 'div',
28978                 cls: 'tab-header',
28979                 cn: [
28980                     {
28981                         tag: 'ul',
28982                         cls: 'nav nav-tabs pull-right',
28983                         cn: [
28984                             header
28985                         ]
28986                     }
28987                 ]
28988             };
28989         }
28990         
28991         var cfg = {
28992             tag: 'div',
28993             cls: 'nav-tabs-custom',
28994             cn: [
28995                 h,
28996                 {
28997                     tag: 'div',
28998                     cls: 'tab-content no-padding',
28999                     cn: []
29000                 }
29001             ]
29002         };
29003
29004         return  cfg;
29005     },
29006     initEvents : function()
29007     {
29008         //Roo.log('add add pane handler');
29009         this.on('addpane', this.onAddPane, this);
29010     },
29011      /**
29012      * Updates the box title
29013      * @param {String} html to set the title to.
29014      */
29015     setTitle : function(value)
29016     {
29017         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29018     },
29019     onAddPane : function(pane)
29020     {
29021         this.panes.push(pane);
29022         //Roo.log('addpane');
29023         //Roo.log(pane);
29024         // tabs are rendere left to right..
29025         if(!this.showtabs){
29026             return;
29027         }
29028         
29029         var ctr = this.el.select('.nav-tabs', true).first();
29030          
29031          
29032         var existing = ctr.select('.nav-tab',true);
29033         var qty = existing.getCount();;
29034         
29035         
29036         var tab = ctr.createChild({
29037             tag : 'li',
29038             cls : 'nav-tab' + (qty ? '' : ' active'),
29039             cn : [
29040                 {
29041                     tag : 'a',
29042                     href:'#',
29043                     html : pane.title
29044                 }
29045             ]
29046         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29047         pane.tab = tab;
29048         
29049         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29050         if (!qty) {
29051             pane.el.addClass('active');
29052         }
29053         
29054                 
29055     },
29056     onTabClick : function(ev,un,ob,pane)
29057     {
29058         //Roo.log('tab - prev default');
29059         ev.preventDefault();
29060         
29061         
29062         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29063         pane.tab.addClass('active');
29064         //Roo.log(pane.title);
29065         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29066         // technically we should have a deactivate event.. but maybe add later.
29067         // and it should not de-activate the selected tab...
29068         this.fireEvent('activatepane', pane);
29069         pane.el.addClass('active');
29070         pane.fireEvent('activate');
29071         
29072         
29073     },
29074     
29075     getActivePane : function()
29076     {
29077         var r = false;
29078         Roo.each(this.panes, function(p) {
29079             if(p.el.hasClass('active')){
29080                 r = p;
29081                 return false;
29082             }
29083             
29084             return;
29085         });
29086         
29087         return r;
29088     }
29089     
29090     
29091 });
29092
29093  
29094 /*
29095  * - LGPL
29096  *
29097  * Tab pane
29098  * 
29099  */
29100 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29101 /**
29102  * @class Roo.bootstrap.TabPane
29103  * @extends Roo.bootstrap.Component
29104  * Bootstrap TabPane class
29105  * @cfg {Boolean} active (false | true) Default false
29106  * @cfg {String} title title of panel
29107
29108  * 
29109  * @constructor
29110  * Create a new TabPane
29111  * @param {Object} config The config object
29112  */
29113
29114 Roo.bootstrap.dash.TabPane = function(config){
29115     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29116     
29117     this.addEvents({
29118         // raw events
29119         /**
29120          * @event activate
29121          * When a pane is activated
29122          * @param {Roo.bootstrap.dash.TabPane} pane
29123          */
29124         "activate" : true
29125          
29126     });
29127 };
29128
29129 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29130     
29131     active : false,
29132     title : '',
29133     
29134     // the tabBox that this is attached to.
29135     tab : false,
29136      
29137     getAutoCreate : function() 
29138     {
29139         var cfg = {
29140             tag: 'div',
29141             cls: 'tab-pane'
29142         };
29143         
29144         if(this.active){
29145             cfg.cls += ' active';
29146         }
29147         
29148         return cfg;
29149     },
29150     initEvents  : function()
29151     {
29152         //Roo.log('trigger add pane handler');
29153         this.parent().fireEvent('addpane', this)
29154     },
29155     
29156      /**
29157      * Updates the tab title 
29158      * @param {String} html to set the title to.
29159      */
29160     setTitle: function(str)
29161     {
29162         if (!this.tab) {
29163             return;
29164         }
29165         this.title = str;
29166         this.tab.select('a', true).first().dom.innerHTML = str;
29167         
29168     }
29169     
29170     
29171     
29172 });
29173
29174  
29175
29176
29177  /*
29178  * - LGPL
29179  *
29180  * menu
29181  * 
29182  */
29183 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29184
29185 /**
29186  * @class Roo.bootstrap.menu.Menu
29187  * @extends Roo.bootstrap.Component
29188  * Bootstrap Menu class - container for Menu
29189  * @cfg {String} html Text of the menu
29190  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29191  * @cfg {String} icon Font awesome icon
29192  * @cfg {String} pos Menu align to (top | bottom) default bottom
29193  * 
29194  * 
29195  * @constructor
29196  * Create a new Menu
29197  * @param {Object} config The config object
29198  */
29199
29200
29201 Roo.bootstrap.menu.Menu = function(config){
29202     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29203     
29204     this.addEvents({
29205         /**
29206          * @event beforeshow
29207          * Fires before this menu is displayed
29208          * @param {Roo.bootstrap.menu.Menu} this
29209          */
29210         beforeshow : true,
29211         /**
29212          * @event beforehide
29213          * Fires before this menu is hidden
29214          * @param {Roo.bootstrap.menu.Menu} this
29215          */
29216         beforehide : true,
29217         /**
29218          * @event show
29219          * Fires after this menu is displayed
29220          * @param {Roo.bootstrap.menu.Menu} this
29221          */
29222         show : true,
29223         /**
29224          * @event hide
29225          * Fires after this menu is hidden
29226          * @param {Roo.bootstrap.menu.Menu} this
29227          */
29228         hide : true,
29229         /**
29230          * @event click
29231          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29232          * @param {Roo.bootstrap.menu.Menu} this
29233          * @param {Roo.EventObject} e
29234          */
29235         click : true
29236     });
29237     
29238 };
29239
29240 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29241     
29242     submenu : false,
29243     html : '',
29244     weight : 'default',
29245     icon : false,
29246     pos : 'bottom',
29247     
29248     
29249     getChildContainer : function() {
29250         if(this.isSubMenu){
29251             return this.el;
29252         }
29253         
29254         return this.el.select('ul.dropdown-menu', true).first();  
29255     },
29256     
29257     getAutoCreate : function()
29258     {
29259         var text = [
29260             {
29261                 tag : 'span',
29262                 cls : 'roo-menu-text',
29263                 html : this.html
29264             }
29265         ];
29266         
29267         if(this.icon){
29268             text.unshift({
29269                 tag : 'i',
29270                 cls : 'fa ' + this.icon
29271             })
29272         }
29273         
29274         
29275         var cfg = {
29276             tag : 'div',
29277             cls : 'btn-group',
29278             cn : [
29279                 {
29280                     tag : 'button',
29281                     cls : 'dropdown-button btn btn-' + this.weight,
29282                     cn : text
29283                 },
29284                 {
29285                     tag : 'button',
29286                     cls : 'dropdown-toggle btn btn-' + this.weight,
29287                     cn : [
29288                         {
29289                             tag : 'span',
29290                             cls : 'caret'
29291                         }
29292                     ]
29293                 },
29294                 {
29295                     tag : 'ul',
29296                     cls : 'dropdown-menu'
29297                 }
29298             ]
29299             
29300         };
29301         
29302         if(this.pos == 'top'){
29303             cfg.cls += ' dropup';
29304         }
29305         
29306         if(this.isSubMenu){
29307             cfg = {
29308                 tag : 'ul',
29309                 cls : 'dropdown-menu'
29310             }
29311         }
29312         
29313         return cfg;
29314     },
29315     
29316     onRender : function(ct, position)
29317     {
29318         this.isSubMenu = ct.hasClass('dropdown-submenu');
29319         
29320         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29321     },
29322     
29323     initEvents : function() 
29324     {
29325         if(this.isSubMenu){
29326             return;
29327         }
29328         
29329         this.hidden = true;
29330         
29331         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29332         this.triggerEl.on('click', this.onTriggerPress, this);
29333         
29334         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29335         this.buttonEl.on('click', this.onClick, this);
29336         
29337     },
29338     
29339     list : function()
29340     {
29341         if(this.isSubMenu){
29342             return this.el;
29343         }
29344         
29345         return this.el.select('ul.dropdown-menu', true).first();
29346     },
29347     
29348     onClick : function(e)
29349     {
29350         this.fireEvent("click", this, e);
29351     },
29352     
29353     onTriggerPress  : function(e)
29354     {   
29355         if (this.isVisible()) {
29356             this.hide();
29357         } else {
29358             this.show();
29359         }
29360     },
29361     
29362     isVisible : function(){
29363         return !this.hidden;
29364     },
29365     
29366     show : function()
29367     {
29368         this.fireEvent("beforeshow", this);
29369         
29370         this.hidden = false;
29371         this.el.addClass('open');
29372         
29373         Roo.get(document).on("mouseup", this.onMouseUp, this);
29374         
29375         this.fireEvent("show", this);
29376         
29377         
29378     },
29379     
29380     hide : function()
29381     {
29382         this.fireEvent("beforehide", this);
29383         
29384         this.hidden = true;
29385         this.el.removeClass('open');
29386         
29387         Roo.get(document).un("mouseup", this.onMouseUp);
29388         
29389         this.fireEvent("hide", this);
29390     },
29391     
29392     onMouseUp : function()
29393     {
29394         this.hide();
29395     }
29396     
29397 });
29398
29399  
29400  /*
29401  * - LGPL
29402  *
29403  * menu item
29404  * 
29405  */
29406 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29407
29408 /**
29409  * @class Roo.bootstrap.menu.Item
29410  * @extends Roo.bootstrap.Component
29411  * Bootstrap MenuItem class
29412  * @cfg {Boolean} submenu (true | false) default false
29413  * @cfg {String} html text of the item
29414  * @cfg {String} href the link
29415  * @cfg {Boolean} disable (true | false) default false
29416  * @cfg {Boolean} preventDefault (true | false) default true
29417  * @cfg {String} icon Font awesome icon
29418  * @cfg {String} pos Submenu align to (left | right) default right 
29419  * 
29420  * 
29421  * @constructor
29422  * Create a new Item
29423  * @param {Object} config The config object
29424  */
29425
29426
29427 Roo.bootstrap.menu.Item = function(config){
29428     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29429     this.addEvents({
29430         /**
29431          * @event mouseover
29432          * Fires when the mouse is hovering over this menu
29433          * @param {Roo.bootstrap.menu.Item} this
29434          * @param {Roo.EventObject} e
29435          */
29436         mouseover : true,
29437         /**
29438          * @event mouseout
29439          * Fires when the mouse exits this menu
29440          * @param {Roo.bootstrap.menu.Item} this
29441          * @param {Roo.EventObject} e
29442          */
29443         mouseout : true,
29444         // raw events
29445         /**
29446          * @event click
29447          * The raw click event for the entire grid.
29448          * @param {Roo.EventObject} e
29449          */
29450         click : true
29451     });
29452 };
29453
29454 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29455     
29456     submenu : false,
29457     href : '',
29458     html : '',
29459     preventDefault: true,
29460     disable : false,
29461     icon : false,
29462     pos : 'right',
29463     
29464     getAutoCreate : function()
29465     {
29466         var text = [
29467             {
29468                 tag : 'span',
29469                 cls : 'roo-menu-item-text',
29470                 html : this.html
29471             }
29472         ];
29473         
29474         if(this.icon){
29475             text.unshift({
29476                 tag : 'i',
29477                 cls : 'fa ' + this.icon
29478             })
29479         }
29480         
29481         var cfg = {
29482             tag : 'li',
29483             cn : [
29484                 {
29485                     tag : 'a',
29486                     href : this.href || '#',
29487                     cn : text
29488                 }
29489             ]
29490         };
29491         
29492         if(this.disable){
29493             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29494         }
29495         
29496         if(this.submenu){
29497             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29498             
29499             if(this.pos == 'left'){
29500                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29501             }
29502         }
29503         
29504         return cfg;
29505     },
29506     
29507     initEvents : function() 
29508     {
29509         this.el.on('mouseover', this.onMouseOver, this);
29510         this.el.on('mouseout', this.onMouseOut, this);
29511         
29512         this.el.select('a', true).first().on('click', this.onClick, this);
29513         
29514     },
29515     
29516     onClick : function(e)
29517     {
29518         if(this.preventDefault){
29519             e.preventDefault();
29520         }
29521         
29522         this.fireEvent("click", this, e);
29523     },
29524     
29525     onMouseOver : function(e)
29526     {
29527         if(this.submenu && this.pos == 'left'){
29528             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29529         }
29530         
29531         this.fireEvent("mouseover", this, e);
29532     },
29533     
29534     onMouseOut : function(e)
29535     {
29536         this.fireEvent("mouseout", this, e);
29537     }
29538 });
29539
29540  
29541
29542  /*
29543  * - LGPL
29544  *
29545  * menu separator
29546  * 
29547  */
29548 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29549
29550 /**
29551  * @class Roo.bootstrap.menu.Separator
29552  * @extends Roo.bootstrap.Component
29553  * Bootstrap Separator class
29554  * 
29555  * @constructor
29556  * Create a new Separator
29557  * @param {Object} config The config object
29558  */
29559
29560
29561 Roo.bootstrap.menu.Separator = function(config){
29562     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29563 };
29564
29565 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29566     
29567     getAutoCreate : function(){
29568         var cfg = {
29569             tag : 'li',
29570             cls: 'dropdown-divider divider'
29571         };
29572         
29573         return cfg;
29574     }
29575    
29576 });
29577
29578  
29579
29580  /*
29581  * - LGPL
29582  *
29583  * Tooltip
29584  * 
29585  */
29586
29587 /**
29588  * @class Roo.bootstrap.Tooltip
29589  * Bootstrap Tooltip class
29590  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29591  * to determine which dom element triggers the tooltip.
29592  * 
29593  * It needs to add support for additional attributes like tooltip-position
29594  * 
29595  * @constructor
29596  * Create a new Toolti
29597  * @param {Object} config The config object
29598  */
29599
29600 Roo.bootstrap.Tooltip = function(config){
29601     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29602     
29603     this.alignment = Roo.bootstrap.Tooltip.alignment;
29604     
29605     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29606         this.alignment = config.alignment;
29607     }
29608     
29609 };
29610
29611 Roo.apply(Roo.bootstrap.Tooltip, {
29612     /**
29613      * @function init initialize tooltip monitoring.
29614      * @static
29615      */
29616     currentEl : false,
29617     currentTip : false,
29618     currentRegion : false,
29619     
29620     //  init : delay?
29621     
29622     init : function()
29623     {
29624         Roo.get(document).on('mouseover', this.enter ,this);
29625         Roo.get(document).on('mouseout', this.leave, this);
29626          
29627         
29628         this.currentTip = new Roo.bootstrap.Tooltip();
29629     },
29630     
29631     enter : function(ev)
29632     {
29633         var dom = ev.getTarget();
29634         
29635         //Roo.log(['enter',dom]);
29636         var el = Roo.fly(dom);
29637         if (this.currentEl) {
29638             //Roo.log(dom);
29639             //Roo.log(this.currentEl);
29640             //Roo.log(this.currentEl.contains(dom));
29641             if (this.currentEl == el) {
29642                 return;
29643             }
29644             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29645                 return;
29646             }
29647
29648         }
29649         
29650         if (this.currentTip.el) {
29651             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29652         }    
29653         //Roo.log(ev);
29654         
29655         if(!el || el.dom == document){
29656             return;
29657         }
29658         
29659         var bindEl = el; 
29660         var pel = false;
29661         if (!el.attr('tooltip')) {
29662             pel = el.findParent("[tooltip]");
29663             if (pel) {
29664                 bindEl = Roo.get(pel);
29665             }
29666         }
29667         
29668        
29669         
29670         // you can not look for children, as if el is the body.. then everythign is the child..
29671         if (!pel && !el.attr('tooltip')) { //
29672             if (!el.select("[tooltip]").elements.length) {
29673                 return;
29674             }
29675             // is the mouse over this child...?
29676             bindEl = el.select("[tooltip]").first();
29677             var xy = ev.getXY();
29678             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29679                 //Roo.log("not in region.");
29680                 return;
29681             }
29682             //Roo.log("child element over..");
29683             
29684         }
29685         this.currentEl = el;
29686         this.currentTip.bind(bindEl);
29687         this.currentRegion = Roo.lib.Region.getRegion(dom);
29688         this.currentTip.enter();
29689         
29690     },
29691     leave : function(ev)
29692     {
29693         var dom = ev.getTarget();
29694         //Roo.log(['leave',dom]);
29695         if (!this.currentEl) {
29696             return;
29697         }
29698         
29699         
29700         if (dom != this.currentEl.dom) {
29701             return;
29702         }
29703         var xy = ev.getXY();
29704         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29705             return;
29706         }
29707         // only activate leave if mouse cursor is outside... bounding box..
29708         
29709         
29710         
29711         
29712         if (this.currentTip) {
29713             this.currentTip.leave();
29714         }
29715         //Roo.log('clear currentEl');
29716         this.currentEl = false;
29717         
29718         
29719     },
29720     alignment : {
29721         'left' : ['r-l', [-2,0], 'right'],
29722         'right' : ['l-r', [2,0], 'left'],
29723         'bottom' : ['t-b', [0,2], 'top'],
29724         'top' : [ 'b-t', [0,-2], 'bottom']
29725     }
29726     
29727 });
29728
29729
29730 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29731     
29732     
29733     bindEl : false,
29734     
29735     delay : null, // can be { show : 300 , hide: 500}
29736     
29737     timeout : null,
29738     
29739     hoverState : null, //???
29740     
29741     placement : 'bottom', 
29742     
29743     alignment : false,
29744     
29745     getAutoCreate : function(){
29746     
29747         var cfg = {
29748            cls : 'tooltip',   
29749            role : 'tooltip',
29750            cn : [
29751                 {
29752                     cls : 'tooltip-arrow arrow'
29753                 },
29754                 {
29755                     cls : 'tooltip-inner'
29756                 }
29757            ]
29758         };
29759         
29760         return cfg;
29761     },
29762     bind : function(el)
29763     {
29764         this.bindEl = el;
29765     },
29766     
29767     initEvents : function()
29768     {
29769         this.arrowEl = this.el.select('.arrow', true).first();
29770         this.innerEl = this.el.select('.tooltip-inner', true).first();
29771     },
29772     
29773     enter : function () {
29774        
29775         if (this.timeout != null) {
29776             clearTimeout(this.timeout);
29777         }
29778         
29779         this.hoverState = 'in';
29780          //Roo.log("enter - show");
29781         if (!this.delay || !this.delay.show) {
29782             this.show();
29783             return;
29784         }
29785         var _t = this;
29786         this.timeout = setTimeout(function () {
29787             if (_t.hoverState == 'in') {
29788                 _t.show();
29789             }
29790         }, this.delay.show);
29791     },
29792     leave : function()
29793     {
29794         clearTimeout(this.timeout);
29795     
29796         this.hoverState = 'out';
29797          if (!this.delay || !this.delay.hide) {
29798             this.hide();
29799             return;
29800         }
29801        
29802         var _t = this;
29803         this.timeout = setTimeout(function () {
29804             //Roo.log("leave - timeout");
29805             
29806             if (_t.hoverState == 'out') {
29807                 _t.hide();
29808                 Roo.bootstrap.Tooltip.currentEl = false;
29809             }
29810         }, delay);
29811     },
29812     
29813     show : function (msg)
29814     {
29815         if (!this.el) {
29816             this.render(document.body);
29817         }
29818         // set content.
29819         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29820         
29821         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29822         
29823         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29824         
29825         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29826                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29827         
29828         var placement = typeof this.placement == 'function' ?
29829             this.placement.call(this, this.el, on_el) :
29830             this.placement;
29831             
29832         var autoToken = /\s?auto?\s?/i;
29833         var autoPlace = autoToken.test(placement);
29834         if (autoPlace) {
29835             placement = placement.replace(autoToken, '') || 'top';
29836         }
29837         
29838         //this.el.detach()
29839         //this.el.setXY([0,0]);
29840         this.el.show();
29841         //this.el.dom.style.display='block';
29842         
29843         //this.el.appendTo(on_el);
29844         
29845         var p = this.getPosition();
29846         var box = this.el.getBox();
29847         
29848         if (autoPlace) {
29849             // fixme..
29850         }
29851         
29852         var align = this.alignment[placement];
29853         
29854         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29855         
29856         if(placement == 'top' || placement == 'bottom'){
29857             if(xy[0] < 0){
29858                 placement = 'right';
29859             }
29860             
29861             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29862                 placement = 'left';
29863             }
29864             
29865             var scroll = Roo.select('body', true).first().getScroll();
29866             
29867             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29868                 placement = 'top';
29869             }
29870             
29871             align = this.alignment[placement];
29872             
29873             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29874             
29875         }
29876         
29877         var elems = document.getElementsByTagName('div');
29878         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29879         for (var i = 0; i < elems.length; i++) {
29880           var zindex = Number.parseInt(
29881                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29882                 10
29883           );
29884           if (zindex > highest) {
29885             highest = zindex;
29886           }
29887         }
29888         
29889         
29890         
29891         this.el.dom.style.zIndex = highest;
29892         
29893         this.el.alignTo(this.bindEl, align[0],align[1]);
29894         //var arrow = this.el.select('.arrow',true).first();
29895         //arrow.set(align[2], 
29896         
29897         this.el.addClass(placement);
29898         this.el.addClass("bs-tooltip-"+ placement);
29899         
29900         this.el.addClass('in fade show');
29901         
29902         this.hoverState = null;
29903         
29904         if (this.el.hasClass('fade')) {
29905             // fade it?
29906         }
29907         
29908         
29909         
29910         
29911         
29912     },
29913     hide : function()
29914     {
29915          
29916         if (!this.el) {
29917             return;
29918         }
29919         //this.el.setXY([0,0]);
29920         this.el.removeClass(['show', 'in']);
29921         //this.el.hide();
29922         
29923     }
29924     
29925 });
29926  
29927
29928  /*
29929  * - LGPL
29930  *
29931  * Location Picker
29932  * 
29933  */
29934
29935 /**
29936  * @class Roo.bootstrap.LocationPicker
29937  * @extends Roo.bootstrap.Component
29938  * Bootstrap LocationPicker class
29939  * @cfg {Number} latitude Position when init default 0
29940  * @cfg {Number} longitude Position when init default 0
29941  * @cfg {Number} zoom default 15
29942  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29943  * @cfg {Boolean} mapTypeControl default false
29944  * @cfg {Boolean} disableDoubleClickZoom default false
29945  * @cfg {Boolean} scrollwheel default true
29946  * @cfg {Boolean} streetViewControl default false
29947  * @cfg {Number} radius default 0
29948  * @cfg {String} locationName
29949  * @cfg {Boolean} draggable default true
29950  * @cfg {Boolean} enableAutocomplete default false
29951  * @cfg {Boolean} enableReverseGeocode default true
29952  * @cfg {String} markerTitle
29953  * 
29954  * @constructor
29955  * Create a new LocationPicker
29956  * @param {Object} config The config object
29957  */
29958
29959
29960 Roo.bootstrap.LocationPicker = function(config){
29961     
29962     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29963     
29964     this.addEvents({
29965         /**
29966          * @event initial
29967          * Fires when the picker initialized.
29968          * @param {Roo.bootstrap.LocationPicker} this
29969          * @param {Google Location} location
29970          */
29971         initial : true,
29972         /**
29973          * @event positionchanged
29974          * Fires when the picker position changed.
29975          * @param {Roo.bootstrap.LocationPicker} this
29976          * @param {Google Location} location
29977          */
29978         positionchanged : true,
29979         /**
29980          * @event resize
29981          * Fires when the map resize.
29982          * @param {Roo.bootstrap.LocationPicker} this
29983          */
29984         resize : true,
29985         /**
29986          * @event show
29987          * Fires when the map show.
29988          * @param {Roo.bootstrap.LocationPicker} this
29989          */
29990         show : true,
29991         /**
29992          * @event hide
29993          * Fires when the map hide.
29994          * @param {Roo.bootstrap.LocationPicker} this
29995          */
29996         hide : true,
29997         /**
29998          * @event mapClick
29999          * Fires when click the map.
30000          * @param {Roo.bootstrap.LocationPicker} this
30001          * @param {Map event} e
30002          */
30003         mapClick : true,
30004         /**
30005          * @event mapRightClick
30006          * Fires when right click the map.
30007          * @param {Roo.bootstrap.LocationPicker} this
30008          * @param {Map event} e
30009          */
30010         mapRightClick : true,
30011         /**
30012          * @event markerClick
30013          * Fires when click the marker.
30014          * @param {Roo.bootstrap.LocationPicker} this
30015          * @param {Map event} e
30016          */
30017         markerClick : true,
30018         /**
30019          * @event markerRightClick
30020          * Fires when right click the marker.
30021          * @param {Roo.bootstrap.LocationPicker} this
30022          * @param {Map event} e
30023          */
30024         markerRightClick : true,
30025         /**
30026          * @event OverlayViewDraw
30027          * Fires when OverlayView Draw
30028          * @param {Roo.bootstrap.LocationPicker} this
30029          */
30030         OverlayViewDraw : true,
30031         /**
30032          * @event OverlayViewOnAdd
30033          * Fires when OverlayView Draw
30034          * @param {Roo.bootstrap.LocationPicker} this
30035          */
30036         OverlayViewOnAdd : true,
30037         /**
30038          * @event OverlayViewOnRemove
30039          * Fires when OverlayView Draw
30040          * @param {Roo.bootstrap.LocationPicker} this
30041          */
30042         OverlayViewOnRemove : true,
30043         /**
30044          * @event OverlayViewShow
30045          * Fires when OverlayView Draw
30046          * @param {Roo.bootstrap.LocationPicker} this
30047          * @param {Pixel} cpx
30048          */
30049         OverlayViewShow : true,
30050         /**
30051          * @event OverlayViewHide
30052          * Fires when OverlayView Draw
30053          * @param {Roo.bootstrap.LocationPicker} this
30054          */
30055         OverlayViewHide : true,
30056         /**
30057          * @event loadexception
30058          * Fires when load google lib failed.
30059          * @param {Roo.bootstrap.LocationPicker} this
30060          */
30061         loadexception : true
30062     });
30063         
30064 };
30065
30066 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30067     
30068     gMapContext: false,
30069     
30070     latitude: 0,
30071     longitude: 0,
30072     zoom: 15,
30073     mapTypeId: false,
30074     mapTypeControl: false,
30075     disableDoubleClickZoom: false,
30076     scrollwheel: true,
30077     streetViewControl: false,
30078     radius: 0,
30079     locationName: '',
30080     draggable: true,
30081     enableAutocomplete: false,
30082     enableReverseGeocode: true,
30083     markerTitle: '',
30084     
30085     getAutoCreate: function()
30086     {
30087
30088         var cfg = {
30089             tag: 'div',
30090             cls: 'roo-location-picker'
30091         };
30092         
30093         return cfg
30094     },
30095     
30096     initEvents: function(ct, position)
30097     {       
30098         if(!this.el.getWidth() || this.isApplied()){
30099             return;
30100         }
30101         
30102         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30103         
30104         this.initial();
30105     },
30106     
30107     initial: function()
30108     {
30109         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30110             this.fireEvent('loadexception', this);
30111             return;
30112         }
30113         
30114         if(!this.mapTypeId){
30115             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30116         }
30117         
30118         this.gMapContext = this.GMapContext();
30119         
30120         this.initOverlayView();
30121         
30122         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30123         
30124         var _this = this;
30125                 
30126         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30127             _this.setPosition(_this.gMapContext.marker.position);
30128         });
30129         
30130         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30131             _this.fireEvent('mapClick', this, event);
30132             
30133         });
30134
30135         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30136             _this.fireEvent('mapRightClick', this, event);
30137             
30138         });
30139         
30140         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30141             _this.fireEvent('markerClick', this, event);
30142             
30143         });
30144
30145         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30146             _this.fireEvent('markerRightClick', this, event);
30147             
30148         });
30149         
30150         this.setPosition(this.gMapContext.location);
30151         
30152         this.fireEvent('initial', this, this.gMapContext.location);
30153     },
30154     
30155     initOverlayView: function()
30156     {
30157         var _this = this;
30158         
30159         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30160             
30161             draw: function()
30162             {
30163                 _this.fireEvent('OverlayViewDraw', _this);
30164             },
30165             
30166             onAdd: function()
30167             {
30168                 _this.fireEvent('OverlayViewOnAdd', _this);
30169             },
30170             
30171             onRemove: function()
30172             {
30173                 _this.fireEvent('OverlayViewOnRemove', _this);
30174             },
30175             
30176             show: function(cpx)
30177             {
30178                 _this.fireEvent('OverlayViewShow', _this, cpx);
30179             },
30180             
30181             hide: function()
30182             {
30183                 _this.fireEvent('OverlayViewHide', _this);
30184             }
30185             
30186         });
30187     },
30188     
30189     fromLatLngToContainerPixel: function(event)
30190     {
30191         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30192     },
30193     
30194     isApplied: function() 
30195     {
30196         return this.getGmapContext() == false ? false : true;
30197     },
30198     
30199     getGmapContext: function() 
30200     {
30201         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30202     },
30203     
30204     GMapContext: function() 
30205     {
30206         var position = new google.maps.LatLng(this.latitude, this.longitude);
30207         
30208         var _map = new google.maps.Map(this.el.dom, {
30209             center: position,
30210             zoom: this.zoom,
30211             mapTypeId: this.mapTypeId,
30212             mapTypeControl: this.mapTypeControl,
30213             disableDoubleClickZoom: this.disableDoubleClickZoom,
30214             scrollwheel: this.scrollwheel,
30215             streetViewControl: this.streetViewControl,
30216             locationName: this.locationName,
30217             draggable: this.draggable,
30218             enableAutocomplete: this.enableAutocomplete,
30219             enableReverseGeocode: this.enableReverseGeocode
30220         });
30221         
30222         var _marker = new google.maps.Marker({
30223             position: position,
30224             map: _map,
30225             title: this.markerTitle,
30226             draggable: this.draggable
30227         });
30228         
30229         return {
30230             map: _map,
30231             marker: _marker,
30232             circle: null,
30233             location: position,
30234             radius: this.radius,
30235             locationName: this.locationName,
30236             addressComponents: {
30237                 formatted_address: null,
30238                 addressLine1: null,
30239                 addressLine2: null,
30240                 streetName: null,
30241                 streetNumber: null,
30242                 city: null,
30243                 district: null,
30244                 state: null,
30245                 stateOrProvince: null
30246             },
30247             settings: this,
30248             domContainer: this.el.dom,
30249             geodecoder: new google.maps.Geocoder()
30250         };
30251     },
30252     
30253     drawCircle: function(center, radius, options) 
30254     {
30255         if (this.gMapContext.circle != null) {
30256             this.gMapContext.circle.setMap(null);
30257         }
30258         if (radius > 0) {
30259             radius *= 1;
30260             options = Roo.apply({}, options, {
30261                 strokeColor: "#0000FF",
30262                 strokeOpacity: .35,
30263                 strokeWeight: 2,
30264                 fillColor: "#0000FF",
30265                 fillOpacity: .2
30266             });
30267             
30268             options.map = this.gMapContext.map;
30269             options.radius = radius;
30270             options.center = center;
30271             this.gMapContext.circle = new google.maps.Circle(options);
30272             return this.gMapContext.circle;
30273         }
30274         
30275         return null;
30276     },
30277     
30278     setPosition: function(location) 
30279     {
30280         this.gMapContext.location = location;
30281         this.gMapContext.marker.setPosition(location);
30282         this.gMapContext.map.panTo(location);
30283         this.drawCircle(location, this.gMapContext.radius, {});
30284         
30285         var _this = this;
30286         
30287         if (this.gMapContext.settings.enableReverseGeocode) {
30288             this.gMapContext.geodecoder.geocode({
30289                 latLng: this.gMapContext.location
30290             }, function(results, status) {
30291                 
30292                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30293                     _this.gMapContext.locationName = results[0].formatted_address;
30294                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30295                     
30296                     _this.fireEvent('positionchanged', this, location);
30297                 }
30298             });
30299             
30300             return;
30301         }
30302         
30303         this.fireEvent('positionchanged', this, location);
30304     },
30305     
30306     resize: function()
30307     {
30308         google.maps.event.trigger(this.gMapContext.map, "resize");
30309         
30310         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30311         
30312         this.fireEvent('resize', this);
30313     },
30314     
30315     setPositionByLatLng: function(latitude, longitude)
30316     {
30317         this.setPosition(new google.maps.LatLng(latitude, longitude));
30318     },
30319     
30320     getCurrentPosition: function() 
30321     {
30322         return {
30323             latitude: this.gMapContext.location.lat(),
30324             longitude: this.gMapContext.location.lng()
30325         };
30326     },
30327     
30328     getAddressName: function() 
30329     {
30330         return this.gMapContext.locationName;
30331     },
30332     
30333     getAddressComponents: function() 
30334     {
30335         return this.gMapContext.addressComponents;
30336     },
30337     
30338     address_component_from_google_geocode: function(address_components) 
30339     {
30340         var result = {};
30341         
30342         for (var i = 0; i < address_components.length; i++) {
30343             var component = address_components[i];
30344             if (component.types.indexOf("postal_code") >= 0) {
30345                 result.postalCode = component.short_name;
30346             } else if (component.types.indexOf("street_number") >= 0) {
30347                 result.streetNumber = component.short_name;
30348             } else if (component.types.indexOf("route") >= 0) {
30349                 result.streetName = component.short_name;
30350             } else if (component.types.indexOf("neighborhood") >= 0) {
30351                 result.city = component.short_name;
30352             } else if (component.types.indexOf("locality") >= 0) {
30353                 result.city = component.short_name;
30354             } else if (component.types.indexOf("sublocality") >= 0) {
30355                 result.district = component.short_name;
30356             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30357                 result.stateOrProvince = component.short_name;
30358             } else if (component.types.indexOf("country") >= 0) {
30359                 result.country = component.short_name;
30360             }
30361         }
30362         
30363         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30364         result.addressLine2 = "";
30365         return result;
30366     },
30367     
30368     setZoomLevel: function(zoom)
30369     {
30370         this.gMapContext.map.setZoom(zoom);
30371     },
30372     
30373     show: function()
30374     {
30375         if(!this.el){
30376             return;
30377         }
30378         
30379         this.el.show();
30380         
30381         this.resize();
30382         
30383         this.fireEvent('show', this);
30384     },
30385     
30386     hide: function()
30387     {
30388         if(!this.el){
30389             return;
30390         }
30391         
30392         this.el.hide();
30393         
30394         this.fireEvent('hide', this);
30395     }
30396     
30397 });
30398
30399 Roo.apply(Roo.bootstrap.LocationPicker, {
30400     
30401     OverlayView : function(map, options)
30402     {
30403         options = options || {};
30404         
30405         this.setMap(map);
30406     }
30407     
30408     
30409 });/**
30410  * @class Roo.bootstrap.Alert
30411  * @extends Roo.bootstrap.Component
30412  * Bootstrap Alert class - shows an alert area box
30413  * eg
30414  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30415   Enter a valid email address
30416 </div>
30417  * @licence LGPL
30418  * @cfg {String} title The title of alert
30419  * @cfg {String} html The content of alert
30420  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30421  * @cfg {String} fa font-awesomeicon
30422  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30423  * @cfg {Boolean} close true to show a x closer
30424  * 
30425  * 
30426  * @constructor
30427  * Create a new alert
30428  * @param {Object} config The config object
30429  */
30430
30431
30432 Roo.bootstrap.Alert = function(config){
30433     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30434     
30435 };
30436
30437 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30438     
30439     title: '',
30440     html: '',
30441     weight: false,
30442     fa: false,
30443     faicon: false, // BC
30444     close : false,
30445     
30446     
30447     getAutoCreate : function()
30448     {
30449         
30450         var cfg = {
30451             tag : 'div',
30452             cls : 'alert',
30453             cn : [
30454                 {
30455                     tag: 'button',
30456                     type :  "button",
30457                     cls: "close",
30458                     html : '×',
30459                     style : this.close ? '' : 'display:none'
30460                 },
30461                 {
30462                     tag : 'i',
30463                     cls : 'roo-alert-icon'
30464                     
30465                 },
30466                 {
30467                     tag : 'b',
30468                     cls : 'roo-alert-title',
30469                     html : this.title
30470                 },
30471                 {
30472                     tag : 'span',
30473                     cls : 'roo-alert-text',
30474                     html : this.html
30475                 }
30476             ]
30477         };
30478         
30479         if(this.faicon){
30480             cfg.cn[0].cls += ' fa ' + this.faicon;
30481         }
30482         if(this.fa){
30483             cfg.cn[0].cls += ' fa ' + this.fa;
30484         }
30485         
30486         if(this.weight){
30487             cfg.cls += ' alert-' + this.weight;
30488         }
30489         
30490         return cfg;
30491     },
30492     
30493     initEvents: function() 
30494     {
30495         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30496         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30497         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30498         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30499         if (this.seconds > 0) {
30500             this.hide.defer(this.seconds, this);
30501         }
30502     },
30503     /**
30504      * Set the Title Message HTML
30505      * @param {String} html
30506      */
30507     setTitle : function(str)
30508     {
30509         this.titleEl.dom.innerHTML = str;
30510     },
30511      
30512      /**
30513      * Set the Body Message HTML
30514      * @param {String} html
30515      */
30516     setHtml : function(str)
30517     {
30518         this.htmlEl.dom.innerHTML = str;
30519     },
30520     /**
30521      * Set the Weight of the alert
30522      * @param {String} (success|info|warning|danger) weight
30523      */
30524     
30525     setWeight : function(weight)
30526     {
30527         if(this.weight){
30528             this.el.removeClass('alert-' + this.weight);
30529         }
30530         
30531         this.weight = weight;
30532         
30533         this.el.addClass('alert-' + this.weight);
30534     },
30535       /**
30536      * Set the Icon of the alert
30537      * @param {String} see fontawsome names (name without the 'fa-' bit)
30538      */
30539     setIcon : function(icon)
30540     {
30541         if(this.faicon){
30542             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30543         }
30544         
30545         this.faicon = icon;
30546         
30547         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30548     },
30549     /**
30550      * Hide the Alert
30551      */
30552     hide: function() 
30553     {
30554         this.el.hide();   
30555     },
30556     /**
30557      * Show the Alert
30558      */
30559     show: function() 
30560     {  
30561         this.el.show();   
30562     }
30563     
30564 });
30565
30566  
30567 /*
30568 * Licence: LGPL
30569 */
30570
30571 /**
30572  * @class Roo.bootstrap.UploadCropbox
30573  * @extends Roo.bootstrap.Component
30574  * Bootstrap UploadCropbox class
30575  * @cfg {String} emptyText show when image has been loaded
30576  * @cfg {String} rotateNotify show when image too small to rotate
30577  * @cfg {Number} errorTimeout default 3000
30578  * @cfg {Number} minWidth default 300
30579  * @cfg {Number} minHeight default 300
30580  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30581  * @cfg {Boolean} isDocument (true|false) default false
30582  * @cfg {String} url action url
30583  * @cfg {String} paramName default 'imageUpload'
30584  * @cfg {String} method default POST
30585  * @cfg {Boolean} loadMask (true|false) default true
30586  * @cfg {Boolean} loadingText default 'Loading...'
30587  * 
30588  * @constructor
30589  * Create a new UploadCropbox
30590  * @param {Object} config The config object
30591  */
30592
30593 Roo.bootstrap.UploadCropbox = function(config){
30594     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30595     
30596     this.addEvents({
30597         /**
30598          * @event beforeselectfile
30599          * Fire before select file
30600          * @param {Roo.bootstrap.UploadCropbox} this
30601          */
30602         "beforeselectfile" : true,
30603         /**
30604          * @event initial
30605          * Fire after initEvent
30606          * @param {Roo.bootstrap.UploadCropbox} this
30607          */
30608         "initial" : true,
30609         /**
30610          * @event crop
30611          * Fire after initEvent
30612          * @param {Roo.bootstrap.UploadCropbox} this
30613          * @param {String} data
30614          */
30615         "crop" : true,
30616         /**
30617          * @event prepare
30618          * Fire when preparing the file data
30619          * @param {Roo.bootstrap.UploadCropbox} this
30620          * @param {Object} file
30621          */
30622         "prepare" : true,
30623         /**
30624          * @event exception
30625          * Fire when get exception
30626          * @param {Roo.bootstrap.UploadCropbox} this
30627          * @param {XMLHttpRequest} xhr
30628          */
30629         "exception" : true,
30630         /**
30631          * @event beforeloadcanvas
30632          * Fire before load the canvas
30633          * @param {Roo.bootstrap.UploadCropbox} this
30634          * @param {String} src
30635          */
30636         "beforeloadcanvas" : true,
30637         /**
30638          * @event trash
30639          * Fire when trash image
30640          * @param {Roo.bootstrap.UploadCropbox} this
30641          */
30642         "trash" : true,
30643         /**
30644          * @event download
30645          * Fire when download the image
30646          * @param {Roo.bootstrap.UploadCropbox} this
30647          */
30648         "download" : true,
30649         /**
30650          * @event footerbuttonclick
30651          * Fire when footerbuttonclick
30652          * @param {Roo.bootstrap.UploadCropbox} this
30653          * @param {String} type
30654          */
30655         "footerbuttonclick" : true,
30656         /**
30657          * @event resize
30658          * Fire when resize
30659          * @param {Roo.bootstrap.UploadCropbox} this
30660          */
30661         "resize" : true,
30662         /**
30663          * @event rotate
30664          * Fire when rotate the image
30665          * @param {Roo.bootstrap.UploadCropbox} this
30666          * @param {String} pos
30667          */
30668         "rotate" : true,
30669         /**
30670          * @event inspect
30671          * Fire when inspect the file
30672          * @param {Roo.bootstrap.UploadCropbox} this
30673          * @param {Object} file
30674          */
30675         "inspect" : true,
30676         /**
30677          * @event upload
30678          * Fire when xhr upload the file
30679          * @param {Roo.bootstrap.UploadCropbox} this
30680          * @param {Object} data
30681          */
30682         "upload" : true,
30683         /**
30684          * @event arrange
30685          * Fire when arrange the file data
30686          * @param {Roo.bootstrap.UploadCropbox} this
30687          * @param {Object} formData
30688          */
30689         "arrange" : true
30690     });
30691     
30692     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30693 };
30694
30695 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30696     
30697     emptyText : 'Click to upload image',
30698     rotateNotify : 'Image is too small to rotate',
30699     errorTimeout : 3000,
30700     scale : 0,
30701     baseScale : 1,
30702     rotate : 0,
30703     dragable : false,
30704     pinching : false,
30705     mouseX : 0,
30706     mouseY : 0,
30707     cropData : false,
30708     minWidth : 300,
30709     minHeight : 300,
30710     file : false,
30711     exif : {},
30712     baseRotate : 1,
30713     cropType : 'image/jpeg',
30714     buttons : false,
30715     canvasLoaded : false,
30716     isDocument : false,
30717     method : 'POST',
30718     paramName : 'imageUpload',
30719     loadMask : true,
30720     loadingText : 'Loading...',
30721     maskEl : false,
30722     
30723     getAutoCreate : function()
30724     {
30725         var cfg = {
30726             tag : 'div',
30727             cls : 'roo-upload-cropbox',
30728             cn : [
30729                 {
30730                     tag : 'input',
30731                     cls : 'roo-upload-cropbox-selector',
30732                     type : 'file'
30733                 },
30734                 {
30735                     tag : 'div',
30736                     cls : 'roo-upload-cropbox-body',
30737                     style : 'cursor:pointer',
30738                     cn : [
30739                         {
30740                             tag : 'div',
30741                             cls : 'roo-upload-cropbox-preview'
30742                         },
30743                         {
30744                             tag : 'div',
30745                             cls : 'roo-upload-cropbox-thumb'
30746                         },
30747                         {
30748                             tag : 'div',
30749                             cls : 'roo-upload-cropbox-empty-notify',
30750                             html : this.emptyText
30751                         },
30752                         {
30753                             tag : 'div',
30754                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30755                             html : this.rotateNotify
30756                         }
30757                     ]
30758                 },
30759                 {
30760                     tag : 'div',
30761                     cls : 'roo-upload-cropbox-footer',
30762                     cn : {
30763                         tag : 'div',
30764                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30765                         cn : []
30766                     }
30767                 }
30768             ]
30769         };
30770         
30771         return cfg;
30772     },
30773     
30774     onRender : function(ct, position)
30775     {
30776         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30777         
30778         if (this.buttons.length) {
30779             
30780             Roo.each(this.buttons, function(bb) {
30781                 
30782                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30783                 
30784                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30785                 
30786             }, this);
30787         }
30788         
30789         if(this.loadMask){
30790             this.maskEl = this.el;
30791         }
30792     },
30793     
30794     initEvents : function()
30795     {
30796         this.urlAPI = (window.createObjectURL && window) || 
30797                                 (window.URL && URL.revokeObjectURL && URL) || 
30798                                 (window.webkitURL && webkitURL);
30799                         
30800         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30801         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30802         
30803         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30804         this.selectorEl.hide();
30805         
30806         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30807         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30808         
30809         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30810         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30811         this.thumbEl.hide();
30812         
30813         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30814         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30815         
30816         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30817         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30818         this.errorEl.hide();
30819         
30820         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30821         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30822         this.footerEl.hide();
30823         
30824         this.setThumbBoxSize();
30825         
30826         this.bind();
30827         
30828         this.resize();
30829         
30830         this.fireEvent('initial', this);
30831     },
30832
30833     bind : function()
30834     {
30835         var _this = this;
30836         
30837         window.addEventListener("resize", function() { _this.resize(); } );
30838         
30839         this.bodyEl.on('click', this.beforeSelectFile, this);
30840         
30841         if(Roo.isTouch){
30842             this.bodyEl.on('touchstart', this.onTouchStart, this);
30843             this.bodyEl.on('touchmove', this.onTouchMove, this);
30844             this.bodyEl.on('touchend', this.onTouchEnd, this);
30845         }
30846         
30847         if(!Roo.isTouch){
30848             this.bodyEl.on('mousedown', this.onMouseDown, this);
30849             this.bodyEl.on('mousemove', this.onMouseMove, this);
30850             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30851             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30852             Roo.get(document).on('mouseup', this.onMouseUp, this);
30853         }
30854         
30855         this.selectorEl.on('change', this.onFileSelected, this);
30856     },
30857     
30858     reset : function()
30859     {    
30860         this.scale = 0;
30861         this.baseScale = 1;
30862         this.rotate = 0;
30863         this.baseRotate = 1;
30864         this.dragable = false;
30865         this.pinching = false;
30866         this.mouseX = 0;
30867         this.mouseY = 0;
30868         this.cropData = false;
30869         this.notifyEl.dom.innerHTML = this.emptyText;
30870         
30871         this.selectorEl.dom.value = '';
30872         
30873     },
30874     
30875     resize : function()
30876     {
30877         if(this.fireEvent('resize', this) != false){
30878             this.setThumbBoxPosition();
30879             this.setCanvasPosition();
30880         }
30881     },
30882     
30883     onFooterButtonClick : function(e, el, o, type)
30884     {
30885         switch (type) {
30886             case 'rotate-left' :
30887                 this.onRotateLeft(e);
30888                 break;
30889             case 'rotate-right' :
30890                 this.onRotateRight(e);
30891                 break;
30892             case 'picture' :
30893                 this.beforeSelectFile(e);
30894                 break;
30895             case 'trash' :
30896                 this.trash(e);
30897                 break;
30898             case 'crop' :
30899                 this.crop(e);
30900                 break;
30901             case 'download' :
30902                 this.download(e);
30903                 break;
30904             default :
30905                 break;
30906         }
30907         
30908         this.fireEvent('footerbuttonclick', this, type);
30909     },
30910     
30911     beforeSelectFile : function(e)
30912     {
30913         e.preventDefault();
30914         
30915         if(this.fireEvent('beforeselectfile', this) != false){
30916             this.selectorEl.dom.click();
30917         }
30918     },
30919     
30920     onFileSelected : function(e)
30921     {
30922         e.preventDefault();
30923         
30924         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30925             return;
30926         }
30927         
30928         var file = this.selectorEl.dom.files[0];
30929         
30930         if(this.fireEvent('inspect', this, file) != false){
30931             this.prepare(file);
30932         }
30933         
30934     },
30935     
30936     trash : function(e)
30937     {
30938         this.fireEvent('trash', this);
30939     },
30940     
30941     download : function(e)
30942     {
30943         this.fireEvent('download', this);
30944     },
30945     
30946     loadCanvas : function(src)
30947     {   
30948         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30949             
30950             this.reset();
30951             
30952             this.imageEl = document.createElement('img');
30953             
30954             var _this = this;
30955             
30956             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30957             
30958             this.imageEl.src = src;
30959         }
30960     },
30961     
30962     onLoadCanvas : function()
30963     {   
30964         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30965         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30966         
30967         this.bodyEl.un('click', this.beforeSelectFile, this);
30968         
30969         this.notifyEl.hide();
30970         this.thumbEl.show();
30971         this.footerEl.show();
30972         
30973         this.baseRotateLevel();
30974         
30975         if(this.isDocument){
30976             this.setThumbBoxSize();
30977         }
30978         
30979         this.setThumbBoxPosition();
30980         
30981         this.baseScaleLevel();
30982         
30983         this.draw();
30984         
30985         this.resize();
30986         
30987         this.canvasLoaded = true;
30988         
30989         if(this.loadMask){
30990             this.maskEl.unmask();
30991         }
30992         
30993     },
30994     
30995     setCanvasPosition : function()
30996     {   
30997         if(!this.canvasEl){
30998             return;
30999         }
31000         
31001         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31002         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31003         
31004         this.previewEl.setLeft(pw);
31005         this.previewEl.setTop(ph);
31006         
31007     },
31008     
31009     onMouseDown : function(e)
31010     {   
31011         e.stopEvent();
31012         
31013         this.dragable = true;
31014         this.pinching = false;
31015         
31016         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31017             this.dragable = false;
31018             return;
31019         }
31020         
31021         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31022         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31023         
31024     },
31025     
31026     onMouseMove : function(e)
31027     {   
31028         e.stopEvent();
31029         
31030         if(!this.canvasLoaded){
31031             return;
31032         }
31033         
31034         if (!this.dragable){
31035             return;
31036         }
31037         
31038         var minX = Math.ceil(this.thumbEl.getLeft(true));
31039         var minY = Math.ceil(this.thumbEl.getTop(true));
31040         
31041         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31042         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31043         
31044         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31045         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31046         
31047         x = x - this.mouseX;
31048         y = y - this.mouseY;
31049         
31050         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31051         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31052         
31053         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31054         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31055         
31056         this.previewEl.setLeft(bgX);
31057         this.previewEl.setTop(bgY);
31058         
31059         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31060         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31061     },
31062     
31063     onMouseUp : function(e)
31064     {   
31065         e.stopEvent();
31066         
31067         this.dragable = false;
31068     },
31069     
31070     onMouseWheel : function(e)
31071     {   
31072         e.stopEvent();
31073         
31074         this.startScale = this.scale;
31075         
31076         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31077         
31078         if(!this.zoomable()){
31079             this.scale = this.startScale;
31080             return;
31081         }
31082         
31083         this.draw();
31084         
31085         return;
31086     },
31087     
31088     zoomable : function()
31089     {
31090         var minScale = this.thumbEl.getWidth() / this.minWidth;
31091         
31092         if(this.minWidth < this.minHeight){
31093             minScale = this.thumbEl.getHeight() / this.minHeight;
31094         }
31095         
31096         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31097         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31098         
31099         if(
31100                 this.isDocument &&
31101                 (this.rotate == 0 || this.rotate == 180) && 
31102                 (
31103                     width > this.imageEl.OriginWidth || 
31104                     height > this.imageEl.OriginHeight ||
31105                     (width < this.minWidth && height < this.minHeight)
31106                 )
31107         ){
31108             return false;
31109         }
31110         
31111         if(
31112                 this.isDocument &&
31113                 (this.rotate == 90 || this.rotate == 270) && 
31114                 (
31115                     width > this.imageEl.OriginWidth || 
31116                     height > this.imageEl.OriginHeight ||
31117                     (width < this.minHeight && height < this.minWidth)
31118                 )
31119         ){
31120             return false;
31121         }
31122         
31123         if(
31124                 !this.isDocument &&
31125                 (this.rotate == 0 || this.rotate == 180) && 
31126                 (
31127                     width < this.minWidth || 
31128                     width > this.imageEl.OriginWidth || 
31129                     height < this.minHeight || 
31130                     height > this.imageEl.OriginHeight
31131                 )
31132         ){
31133             return false;
31134         }
31135         
31136         if(
31137                 !this.isDocument &&
31138                 (this.rotate == 90 || this.rotate == 270) && 
31139                 (
31140                     width < this.minHeight || 
31141                     width > this.imageEl.OriginWidth || 
31142                     height < this.minWidth || 
31143                     height > this.imageEl.OriginHeight
31144                 )
31145         ){
31146             return false;
31147         }
31148         
31149         return true;
31150         
31151     },
31152     
31153     onRotateLeft : function(e)
31154     {   
31155         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31156             
31157             var minScale = this.thumbEl.getWidth() / this.minWidth;
31158             
31159             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31160             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31161             
31162             this.startScale = this.scale;
31163             
31164             while (this.getScaleLevel() < minScale){
31165             
31166                 this.scale = this.scale + 1;
31167                 
31168                 if(!this.zoomable()){
31169                     break;
31170                 }
31171                 
31172                 if(
31173                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31174                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31175                 ){
31176                     continue;
31177                 }
31178                 
31179                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31180
31181                 this.draw();
31182                 
31183                 return;
31184             }
31185             
31186             this.scale = this.startScale;
31187             
31188             this.onRotateFail();
31189             
31190             return false;
31191         }
31192         
31193         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31194
31195         if(this.isDocument){
31196             this.setThumbBoxSize();
31197             this.setThumbBoxPosition();
31198             this.setCanvasPosition();
31199         }
31200         
31201         this.draw();
31202         
31203         this.fireEvent('rotate', this, 'left');
31204         
31205     },
31206     
31207     onRotateRight : function(e)
31208     {
31209         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31210             
31211             var minScale = this.thumbEl.getWidth() / this.minWidth;
31212         
31213             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31214             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31215             
31216             this.startScale = this.scale;
31217             
31218             while (this.getScaleLevel() < minScale){
31219             
31220                 this.scale = this.scale + 1;
31221                 
31222                 if(!this.zoomable()){
31223                     break;
31224                 }
31225                 
31226                 if(
31227                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31228                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31229                 ){
31230                     continue;
31231                 }
31232                 
31233                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31234
31235                 this.draw();
31236                 
31237                 return;
31238             }
31239             
31240             this.scale = this.startScale;
31241             
31242             this.onRotateFail();
31243             
31244             return false;
31245         }
31246         
31247         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31248
31249         if(this.isDocument){
31250             this.setThumbBoxSize();
31251             this.setThumbBoxPosition();
31252             this.setCanvasPosition();
31253         }
31254         
31255         this.draw();
31256         
31257         this.fireEvent('rotate', this, 'right');
31258     },
31259     
31260     onRotateFail : function()
31261     {
31262         this.errorEl.show(true);
31263         
31264         var _this = this;
31265         
31266         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31267     },
31268     
31269     draw : function()
31270     {
31271         this.previewEl.dom.innerHTML = '';
31272         
31273         var canvasEl = document.createElement("canvas");
31274         
31275         var contextEl = canvasEl.getContext("2d");
31276         
31277         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31278         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31279         var center = this.imageEl.OriginWidth / 2;
31280         
31281         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31282             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31283             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31284             center = this.imageEl.OriginHeight / 2;
31285         }
31286         
31287         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31288         
31289         contextEl.translate(center, center);
31290         contextEl.rotate(this.rotate * Math.PI / 180);
31291
31292         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31293         
31294         this.canvasEl = document.createElement("canvas");
31295         
31296         this.contextEl = this.canvasEl.getContext("2d");
31297         
31298         switch (this.rotate) {
31299             case 0 :
31300                 
31301                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31302                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31303                 
31304                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31305                 
31306                 break;
31307             case 90 : 
31308                 
31309                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31310                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31311                 
31312                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31313                     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);
31314                     break;
31315                 }
31316                 
31317                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31318                 
31319                 break;
31320             case 180 :
31321                 
31322                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31323                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31324                 
31325                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31326                     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);
31327                     break;
31328                 }
31329                 
31330                 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);
31331                 
31332                 break;
31333             case 270 :
31334                 
31335                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31336                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31337         
31338                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31339                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31340                     break;
31341                 }
31342                 
31343                 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);
31344                 
31345                 break;
31346             default : 
31347                 break;
31348         }
31349         
31350         this.previewEl.appendChild(this.canvasEl);
31351         
31352         this.setCanvasPosition();
31353     },
31354     
31355     crop : function()
31356     {
31357         if(!this.canvasLoaded){
31358             return;
31359         }
31360         
31361         var imageCanvas = document.createElement("canvas");
31362         
31363         var imageContext = imageCanvas.getContext("2d");
31364         
31365         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31366         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31367         
31368         var center = imageCanvas.width / 2;
31369         
31370         imageContext.translate(center, center);
31371         
31372         imageContext.rotate(this.rotate * Math.PI / 180);
31373         
31374         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31375         
31376         var canvas = document.createElement("canvas");
31377         
31378         var context = canvas.getContext("2d");
31379                 
31380         canvas.width = this.minWidth;
31381         canvas.height = this.minHeight;
31382
31383         switch (this.rotate) {
31384             case 0 :
31385                 
31386                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31387                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31388                 
31389                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31390                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31391                 
31392                 var targetWidth = this.minWidth - 2 * x;
31393                 var targetHeight = this.minHeight - 2 * y;
31394                 
31395                 var scale = 1;
31396                 
31397                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31398                     scale = targetWidth / width;
31399                 }
31400                 
31401                 if(x > 0 && y == 0){
31402                     scale = targetHeight / height;
31403                 }
31404                 
31405                 if(x > 0 && y > 0){
31406                     scale = targetWidth / width;
31407                     
31408                     if(width < height){
31409                         scale = targetHeight / height;
31410                     }
31411                 }
31412                 
31413                 context.scale(scale, scale);
31414                 
31415                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31416                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31417
31418                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31419                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31420
31421                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31422                 
31423                 break;
31424             case 90 : 
31425                 
31426                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31427                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31428                 
31429                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31430                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31431                 
31432                 var targetWidth = this.minWidth - 2 * x;
31433                 var targetHeight = this.minHeight - 2 * y;
31434                 
31435                 var scale = 1;
31436                 
31437                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31438                     scale = targetWidth / width;
31439                 }
31440                 
31441                 if(x > 0 && y == 0){
31442                     scale = targetHeight / height;
31443                 }
31444                 
31445                 if(x > 0 && y > 0){
31446                     scale = targetWidth / width;
31447                     
31448                     if(width < height){
31449                         scale = targetHeight / height;
31450                     }
31451                 }
31452                 
31453                 context.scale(scale, scale);
31454                 
31455                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31456                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31457
31458                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31459                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31460                 
31461                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31462                 
31463                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31464                 
31465                 break;
31466             case 180 :
31467                 
31468                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31469                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31470                 
31471                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31472                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31473                 
31474                 var targetWidth = this.minWidth - 2 * x;
31475                 var targetHeight = this.minHeight - 2 * y;
31476                 
31477                 var scale = 1;
31478                 
31479                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31480                     scale = targetWidth / width;
31481                 }
31482                 
31483                 if(x > 0 && y == 0){
31484                     scale = targetHeight / height;
31485                 }
31486                 
31487                 if(x > 0 && y > 0){
31488                     scale = targetWidth / width;
31489                     
31490                     if(width < height){
31491                         scale = targetHeight / height;
31492                     }
31493                 }
31494                 
31495                 context.scale(scale, scale);
31496                 
31497                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31498                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31499
31500                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31501                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31502
31503                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31504                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31505                 
31506                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31507                 
31508                 break;
31509             case 270 :
31510                 
31511                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31512                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31513                 
31514                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31515                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31516                 
31517                 var targetWidth = this.minWidth - 2 * x;
31518                 var targetHeight = this.minHeight - 2 * y;
31519                 
31520                 var scale = 1;
31521                 
31522                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31523                     scale = targetWidth / width;
31524                 }
31525                 
31526                 if(x > 0 && y == 0){
31527                     scale = targetHeight / height;
31528                 }
31529                 
31530                 if(x > 0 && y > 0){
31531                     scale = targetWidth / width;
31532                     
31533                     if(width < height){
31534                         scale = targetHeight / height;
31535                     }
31536                 }
31537                 
31538                 context.scale(scale, scale);
31539                 
31540                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31541                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31542
31543                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31544                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31545                 
31546                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31547                 
31548                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31549                 
31550                 break;
31551             default : 
31552                 break;
31553         }
31554         
31555         this.cropData = canvas.toDataURL(this.cropType);
31556         
31557         if(this.fireEvent('crop', this, this.cropData) !== false){
31558             this.process(this.file, this.cropData);
31559         }
31560         
31561         return;
31562         
31563     },
31564     
31565     setThumbBoxSize : function()
31566     {
31567         var width, height;
31568         
31569         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31570             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31571             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31572             
31573             this.minWidth = width;
31574             this.minHeight = height;
31575             
31576             if(this.rotate == 90 || this.rotate == 270){
31577                 this.minWidth = height;
31578                 this.minHeight = width;
31579             }
31580         }
31581         
31582         height = 300;
31583         width = Math.ceil(this.minWidth * height / this.minHeight);
31584         
31585         if(this.minWidth > this.minHeight){
31586             width = 300;
31587             height = Math.ceil(this.minHeight * width / this.minWidth);
31588         }
31589         
31590         this.thumbEl.setStyle({
31591             width : width + 'px',
31592             height : height + 'px'
31593         });
31594
31595         return;
31596             
31597     },
31598     
31599     setThumbBoxPosition : function()
31600     {
31601         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31602         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31603         
31604         this.thumbEl.setLeft(x);
31605         this.thumbEl.setTop(y);
31606         
31607     },
31608     
31609     baseRotateLevel : function()
31610     {
31611         this.baseRotate = 1;
31612         
31613         if(
31614                 typeof(this.exif) != 'undefined' &&
31615                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31616                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31617         ){
31618             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31619         }
31620         
31621         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31622         
31623     },
31624     
31625     baseScaleLevel : function()
31626     {
31627         var width, height;
31628         
31629         if(this.isDocument){
31630             
31631             if(this.baseRotate == 6 || this.baseRotate == 8){
31632             
31633                 height = this.thumbEl.getHeight();
31634                 this.baseScale = height / this.imageEl.OriginWidth;
31635
31636                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31637                     width = this.thumbEl.getWidth();
31638                     this.baseScale = width / this.imageEl.OriginHeight;
31639                 }
31640
31641                 return;
31642             }
31643
31644             height = this.thumbEl.getHeight();
31645             this.baseScale = height / this.imageEl.OriginHeight;
31646
31647             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31648                 width = this.thumbEl.getWidth();
31649                 this.baseScale = width / this.imageEl.OriginWidth;
31650             }
31651
31652             return;
31653         }
31654         
31655         if(this.baseRotate == 6 || this.baseRotate == 8){
31656             
31657             width = this.thumbEl.getHeight();
31658             this.baseScale = width / this.imageEl.OriginHeight;
31659             
31660             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31661                 height = this.thumbEl.getWidth();
31662                 this.baseScale = height / this.imageEl.OriginHeight;
31663             }
31664             
31665             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31666                 height = this.thumbEl.getWidth();
31667                 this.baseScale = height / this.imageEl.OriginHeight;
31668                 
31669                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31670                     width = this.thumbEl.getHeight();
31671                     this.baseScale = width / this.imageEl.OriginWidth;
31672                 }
31673             }
31674             
31675             return;
31676         }
31677         
31678         width = this.thumbEl.getWidth();
31679         this.baseScale = width / this.imageEl.OriginWidth;
31680         
31681         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31682             height = this.thumbEl.getHeight();
31683             this.baseScale = height / this.imageEl.OriginHeight;
31684         }
31685         
31686         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31687             
31688             height = this.thumbEl.getHeight();
31689             this.baseScale = height / this.imageEl.OriginHeight;
31690             
31691             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31692                 width = this.thumbEl.getWidth();
31693                 this.baseScale = width / this.imageEl.OriginWidth;
31694             }
31695             
31696         }
31697         
31698         return;
31699     },
31700     
31701     getScaleLevel : function()
31702     {
31703         return this.baseScale * Math.pow(1.1, this.scale);
31704     },
31705     
31706     onTouchStart : function(e)
31707     {
31708         if(!this.canvasLoaded){
31709             this.beforeSelectFile(e);
31710             return;
31711         }
31712         
31713         var touches = e.browserEvent.touches;
31714         
31715         if(!touches){
31716             return;
31717         }
31718         
31719         if(touches.length == 1){
31720             this.onMouseDown(e);
31721             return;
31722         }
31723         
31724         if(touches.length != 2){
31725             return;
31726         }
31727         
31728         var coords = [];
31729         
31730         for(var i = 0, finger; finger = touches[i]; i++){
31731             coords.push(finger.pageX, finger.pageY);
31732         }
31733         
31734         var x = Math.pow(coords[0] - coords[2], 2);
31735         var y = Math.pow(coords[1] - coords[3], 2);
31736         
31737         this.startDistance = Math.sqrt(x + y);
31738         
31739         this.startScale = this.scale;
31740         
31741         this.pinching = true;
31742         this.dragable = false;
31743         
31744     },
31745     
31746     onTouchMove : function(e)
31747     {
31748         if(!this.pinching && !this.dragable){
31749             return;
31750         }
31751         
31752         var touches = e.browserEvent.touches;
31753         
31754         if(!touches){
31755             return;
31756         }
31757         
31758         if(this.dragable){
31759             this.onMouseMove(e);
31760             return;
31761         }
31762         
31763         var coords = [];
31764         
31765         for(var i = 0, finger; finger = touches[i]; i++){
31766             coords.push(finger.pageX, finger.pageY);
31767         }
31768         
31769         var x = Math.pow(coords[0] - coords[2], 2);
31770         var y = Math.pow(coords[1] - coords[3], 2);
31771         
31772         this.endDistance = Math.sqrt(x + y);
31773         
31774         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31775         
31776         if(!this.zoomable()){
31777             this.scale = this.startScale;
31778             return;
31779         }
31780         
31781         this.draw();
31782         
31783     },
31784     
31785     onTouchEnd : function(e)
31786     {
31787         this.pinching = false;
31788         this.dragable = false;
31789         
31790     },
31791     
31792     process : function(file, crop)
31793     {
31794         if(this.loadMask){
31795             this.maskEl.mask(this.loadingText);
31796         }
31797         
31798         this.xhr = new XMLHttpRequest();
31799         
31800         file.xhr = this.xhr;
31801
31802         this.xhr.open(this.method, this.url, true);
31803         
31804         var headers = {
31805             "Accept": "application/json",
31806             "Cache-Control": "no-cache",
31807             "X-Requested-With": "XMLHttpRequest"
31808         };
31809         
31810         for (var headerName in headers) {
31811             var headerValue = headers[headerName];
31812             if (headerValue) {
31813                 this.xhr.setRequestHeader(headerName, headerValue);
31814             }
31815         }
31816         
31817         var _this = this;
31818         
31819         this.xhr.onload = function()
31820         {
31821             _this.xhrOnLoad(_this.xhr);
31822         }
31823         
31824         this.xhr.onerror = function()
31825         {
31826             _this.xhrOnError(_this.xhr);
31827         }
31828         
31829         var formData = new FormData();
31830
31831         formData.append('returnHTML', 'NO');
31832         
31833         if(crop){
31834             formData.append('crop', crop);
31835         }
31836         
31837         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31838             formData.append(this.paramName, file, file.name);
31839         }
31840         
31841         if(typeof(file.filename) != 'undefined'){
31842             formData.append('filename', file.filename);
31843         }
31844         
31845         if(typeof(file.mimetype) != 'undefined'){
31846             formData.append('mimetype', file.mimetype);
31847         }
31848         
31849         if(this.fireEvent('arrange', this, formData) != false){
31850             this.xhr.send(formData);
31851         };
31852     },
31853     
31854     xhrOnLoad : function(xhr)
31855     {
31856         if(this.loadMask){
31857             this.maskEl.unmask();
31858         }
31859         
31860         if (xhr.readyState !== 4) {
31861             this.fireEvent('exception', this, xhr);
31862             return;
31863         }
31864
31865         var response = Roo.decode(xhr.responseText);
31866         
31867         if(!response.success){
31868             this.fireEvent('exception', this, xhr);
31869             return;
31870         }
31871         
31872         var response = Roo.decode(xhr.responseText);
31873         
31874         this.fireEvent('upload', this, response);
31875         
31876     },
31877     
31878     xhrOnError : function()
31879     {
31880         if(this.loadMask){
31881             this.maskEl.unmask();
31882         }
31883         
31884         Roo.log('xhr on error');
31885         
31886         var response = Roo.decode(xhr.responseText);
31887           
31888         Roo.log(response);
31889         
31890     },
31891     
31892     prepare : function(file)
31893     {   
31894         if(this.loadMask){
31895             this.maskEl.mask(this.loadingText);
31896         }
31897         
31898         this.file = false;
31899         this.exif = {};
31900         
31901         if(typeof(file) === 'string'){
31902             this.loadCanvas(file);
31903             return;
31904         }
31905         
31906         if(!file || !this.urlAPI){
31907             return;
31908         }
31909         
31910         this.file = file;
31911         this.cropType = file.type;
31912         
31913         var _this = this;
31914         
31915         if(this.fireEvent('prepare', this, this.file) != false){
31916             
31917             var reader = new FileReader();
31918             
31919             reader.onload = function (e) {
31920                 if (e.target.error) {
31921                     Roo.log(e.target.error);
31922                     return;
31923                 }
31924                 
31925                 var buffer = e.target.result,
31926                     dataView = new DataView(buffer),
31927                     offset = 2,
31928                     maxOffset = dataView.byteLength - 4,
31929                     markerBytes,
31930                     markerLength;
31931                 
31932                 if (dataView.getUint16(0) === 0xffd8) {
31933                     while (offset < maxOffset) {
31934                         markerBytes = dataView.getUint16(offset);
31935                         
31936                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31937                             markerLength = dataView.getUint16(offset + 2) + 2;
31938                             if (offset + markerLength > dataView.byteLength) {
31939                                 Roo.log('Invalid meta data: Invalid segment size.');
31940                                 break;
31941                             }
31942                             
31943                             if(markerBytes == 0xffe1){
31944                                 _this.parseExifData(
31945                                     dataView,
31946                                     offset,
31947                                     markerLength
31948                                 );
31949                             }
31950                             
31951                             offset += markerLength;
31952                             
31953                             continue;
31954                         }
31955                         
31956                         break;
31957                     }
31958                     
31959                 }
31960                 
31961                 var url = _this.urlAPI.createObjectURL(_this.file);
31962                 
31963                 _this.loadCanvas(url);
31964                 
31965                 return;
31966             }
31967             
31968             reader.readAsArrayBuffer(this.file);
31969             
31970         }
31971         
31972     },
31973     
31974     parseExifData : function(dataView, offset, length)
31975     {
31976         var tiffOffset = offset + 10,
31977             littleEndian,
31978             dirOffset;
31979     
31980         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31981             // No Exif data, might be XMP data instead
31982             return;
31983         }
31984         
31985         // Check for the ASCII code for "Exif" (0x45786966):
31986         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31987             // No Exif data, might be XMP data instead
31988             return;
31989         }
31990         if (tiffOffset + 8 > dataView.byteLength) {
31991             Roo.log('Invalid Exif data: Invalid segment size.');
31992             return;
31993         }
31994         // Check for the two null bytes:
31995         if (dataView.getUint16(offset + 8) !== 0x0000) {
31996             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31997             return;
31998         }
31999         // Check the byte alignment:
32000         switch (dataView.getUint16(tiffOffset)) {
32001         case 0x4949:
32002             littleEndian = true;
32003             break;
32004         case 0x4D4D:
32005             littleEndian = false;
32006             break;
32007         default:
32008             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32009             return;
32010         }
32011         // Check for the TIFF tag marker (0x002A):
32012         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32013             Roo.log('Invalid Exif data: Missing TIFF marker.');
32014             return;
32015         }
32016         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32017         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32018         
32019         this.parseExifTags(
32020             dataView,
32021             tiffOffset,
32022             tiffOffset + dirOffset,
32023             littleEndian
32024         );
32025     },
32026     
32027     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32028     {
32029         var tagsNumber,
32030             dirEndOffset,
32031             i;
32032         if (dirOffset + 6 > dataView.byteLength) {
32033             Roo.log('Invalid Exif data: Invalid directory offset.');
32034             return;
32035         }
32036         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32037         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32038         if (dirEndOffset + 4 > dataView.byteLength) {
32039             Roo.log('Invalid Exif data: Invalid directory size.');
32040             return;
32041         }
32042         for (i = 0; i < tagsNumber; i += 1) {
32043             this.parseExifTag(
32044                 dataView,
32045                 tiffOffset,
32046                 dirOffset + 2 + 12 * i, // tag offset
32047                 littleEndian
32048             );
32049         }
32050         // Return the offset to the next directory:
32051         return dataView.getUint32(dirEndOffset, littleEndian);
32052     },
32053     
32054     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32055     {
32056         var tag = dataView.getUint16(offset, littleEndian);
32057         
32058         this.exif[tag] = this.getExifValue(
32059             dataView,
32060             tiffOffset,
32061             offset,
32062             dataView.getUint16(offset + 2, littleEndian), // tag type
32063             dataView.getUint32(offset + 4, littleEndian), // tag length
32064             littleEndian
32065         );
32066     },
32067     
32068     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32069     {
32070         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32071             tagSize,
32072             dataOffset,
32073             values,
32074             i,
32075             str,
32076             c;
32077     
32078         if (!tagType) {
32079             Roo.log('Invalid Exif data: Invalid tag type.');
32080             return;
32081         }
32082         
32083         tagSize = tagType.size * length;
32084         // Determine if the value is contained in the dataOffset bytes,
32085         // or if the value at the dataOffset is a pointer to the actual data:
32086         dataOffset = tagSize > 4 ?
32087                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32088         if (dataOffset + tagSize > dataView.byteLength) {
32089             Roo.log('Invalid Exif data: Invalid data offset.');
32090             return;
32091         }
32092         if (length === 1) {
32093             return tagType.getValue(dataView, dataOffset, littleEndian);
32094         }
32095         values = [];
32096         for (i = 0; i < length; i += 1) {
32097             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32098         }
32099         
32100         if (tagType.ascii) {
32101             str = '';
32102             // Concatenate the chars:
32103             for (i = 0; i < values.length; i += 1) {
32104                 c = values[i];
32105                 // Ignore the terminating NULL byte(s):
32106                 if (c === '\u0000') {
32107                     break;
32108                 }
32109                 str += c;
32110             }
32111             return str;
32112         }
32113         return values;
32114     }
32115     
32116 });
32117
32118 Roo.apply(Roo.bootstrap.UploadCropbox, {
32119     tags : {
32120         'Orientation': 0x0112
32121     },
32122     
32123     Orientation: {
32124             1: 0, //'top-left',
32125 //            2: 'top-right',
32126             3: 180, //'bottom-right',
32127 //            4: 'bottom-left',
32128 //            5: 'left-top',
32129             6: 90, //'right-top',
32130 //            7: 'right-bottom',
32131             8: 270 //'left-bottom'
32132     },
32133     
32134     exifTagTypes : {
32135         // byte, 8-bit unsigned int:
32136         1: {
32137             getValue: function (dataView, dataOffset) {
32138                 return dataView.getUint8(dataOffset);
32139             },
32140             size: 1
32141         },
32142         // ascii, 8-bit byte:
32143         2: {
32144             getValue: function (dataView, dataOffset) {
32145                 return String.fromCharCode(dataView.getUint8(dataOffset));
32146             },
32147             size: 1,
32148             ascii: true
32149         },
32150         // short, 16 bit int:
32151         3: {
32152             getValue: function (dataView, dataOffset, littleEndian) {
32153                 return dataView.getUint16(dataOffset, littleEndian);
32154             },
32155             size: 2
32156         },
32157         // long, 32 bit int:
32158         4: {
32159             getValue: function (dataView, dataOffset, littleEndian) {
32160                 return dataView.getUint32(dataOffset, littleEndian);
32161             },
32162             size: 4
32163         },
32164         // rational = two long values, first is numerator, second is denominator:
32165         5: {
32166             getValue: function (dataView, dataOffset, littleEndian) {
32167                 return dataView.getUint32(dataOffset, littleEndian) /
32168                     dataView.getUint32(dataOffset + 4, littleEndian);
32169             },
32170             size: 8
32171         },
32172         // slong, 32 bit signed int:
32173         9: {
32174             getValue: function (dataView, dataOffset, littleEndian) {
32175                 return dataView.getInt32(dataOffset, littleEndian);
32176             },
32177             size: 4
32178         },
32179         // srational, two slongs, first is numerator, second is denominator:
32180         10: {
32181             getValue: function (dataView, dataOffset, littleEndian) {
32182                 return dataView.getInt32(dataOffset, littleEndian) /
32183                     dataView.getInt32(dataOffset + 4, littleEndian);
32184             },
32185             size: 8
32186         }
32187     },
32188     
32189     footer : {
32190         STANDARD : [
32191             {
32192                 tag : 'div',
32193                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32194                 action : 'rotate-left',
32195                 cn : [
32196                     {
32197                         tag : 'button',
32198                         cls : 'btn btn-default',
32199                         html : '<i class="fa fa-undo"></i>'
32200                     }
32201                 ]
32202             },
32203             {
32204                 tag : 'div',
32205                 cls : 'btn-group roo-upload-cropbox-picture',
32206                 action : 'picture',
32207                 cn : [
32208                     {
32209                         tag : 'button',
32210                         cls : 'btn btn-default',
32211                         html : '<i class="fa fa-picture-o"></i>'
32212                     }
32213                 ]
32214             },
32215             {
32216                 tag : 'div',
32217                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32218                 action : 'rotate-right',
32219                 cn : [
32220                     {
32221                         tag : 'button',
32222                         cls : 'btn btn-default',
32223                         html : '<i class="fa fa-repeat"></i>'
32224                     }
32225                 ]
32226             }
32227         ],
32228         DOCUMENT : [
32229             {
32230                 tag : 'div',
32231                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32232                 action : 'rotate-left',
32233                 cn : [
32234                     {
32235                         tag : 'button',
32236                         cls : 'btn btn-default',
32237                         html : '<i class="fa fa-undo"></i>'
32238                     }
32239                 ]
32240             },
32241             {
32242                 tag : 'div',
32243                 cls : 'btn-group roo-upload-cropbox-download',
32244                 action : 'download',
32245                 cn : [
32246                     {
32247                         tag : 'button',
32248                         cls : 'btn btn-default',
32249                         html : '<i class="fa fa-download"></i>'
32250                     }
32251                 ]
32252             },
32253             {
32254                 tag : 'div',
32255                 cls : 'btn-group roo-upload-cropbox-crop',
32256                 action : 'crop',
32257                 cn : [
32258                     {
32259                         tag : 'button',
32260                         cls : 'btn btn-default',
32261                         html : '<i class="fa fa-crop"></i>'
32262                     }
32263                 ]
32264             },
32265             {
32266                 tag : 'div',
32267                 cls : 'btn-group roo-upload-cropbox-trash',
32268                 action : 'trash',
32269                 cn : [
32270                     {
32271                         tag : 'button',
32272                         cls : 'btn btn-default',
32273                         html : '<i class="fa fa-trash"></i>'
32274                     }
32275                 ]
32276             },
32277             {
32278                 tag : 'div',
32279                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32280                 action : 'rotate-right',
32281                 cn : [
32282                     {
32283                         tag : 'button',
32284                         cls : 'btn btn-default',
32285                         html : '<i class="fa fa-repeat"></i>'
32286                     }
32287                 ]
32288             }
32289         ],
32290         ROTATOR : [
32291             {
32292                 tag : 'div',
32293                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32294                 action : 'rotate-left',
32295                 cn : [
32296                     {
32297                         tag : 'button',
32298                         cls : 'btn btn-default',
32299                         html : '<i class="fa fa-undo"></i>'
32300                     }
32301                 ]
32302             },
32303             {
32304                 tag : 'div',
32305                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32306                 action : 'rotate-right',
32307                 cn : [
32308                     {
32309                         tag : 'button',
32310                         cls : 'btn btn-default',
32311                         html : '<i class="fa fa-repeat"></i>'
32312                     }
32313                 ]
32314             }
32315         ]
32316     }
32317 });
32318
32319 /*
32320 * Licence: LGPL
32321 */
32322
32323 /**
32324  * @class Roo.bootstrap.DocumentManager
32325  * @extends Roo.bootstrap.Component
32326  * Bootstrap DocumentManager class
32327  * @cfg {String} paramName default 'imageUpload'
32328  * @cfg {String} toolTipName default 'filename'
32329  * @cfg {String} method default POST
32330  * @cfg {String} url action url
32331  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32332  * @cfg {Boolean} multiple multiple upload default true
32333  * @cfg {Number} thumbSize default 300
32334  * @cfg {String} fieldLabel
32335  * @cfg {Number} labelWidth default 4
32336  * @cfg {String} labelAlign (left|top) default left
32337  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32338 * @cfg {Number} labellg set the width of label (1-12)
32339  * @cfg {Number} labelmd set the width of label (1-12)
32340  * @cfg {Number} labelsm set the width of label (1-12)
32341  * @cfg {Number} labelxs set the width of label (1-12)
32342  * 
32343  * @constructor
32344  * Create a new DocumentManager
32345  * @param {Object} config The config object
32346  */
32347
32348 Roo.bootstrap.DocumentManager = function(config){
32349     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32350     
32351     this.files = [];
32352     this.delegates = [];
32353     
32354     this.addEvents({
32355         /**
32356          * @event initial
32357          * Fire when initial the DocumentManager
32358          * @param {Roo.bootstrap.DocumentManager} this
32359          */
32360         "initial" : true,
32361         /**
32362          * @event inspect
32363          * inspect selected file
32364          * @param {Roo.bootstrap.DocumentManager} this
32365          * @param {File} file
32366          */
32367         "inspect" : true,
32368         /**
32369          * @event exception
32370          * Fire when xhr load exception
32371          * @param {Roo.bootstrap.DocumentManager} this
32372          * @param {XMLHttpRequest} xhr
32373          */
32374         "exception" : true,
32375         /**
32376          * @event afterupload
32377          * Fire when xhr load exception
32378          * @param {Roo.bootstrap.DocumentManager} this
32379          * @param {XMLHttpRequest} xhr
32380          */
32381         "afterupload" : true,
32382         /**
32383          * @event prepare
32384          * prepare the form data
32385          * @param {Roo.bootstrap.DocumentManager} this
32386          * @param {Object} formData
32387          */
32388         "prepare" : true,
32389         /**
32390          * @event remove
32391          * Fire when remove the file
32392          * @param {Roo.bootstrap.DocumentManager} this
32393          * @param {Object} file
32394          */
32395         "remove" : true,
32396         /**
32397          * @event refresh
32398          * Fire after refresh the file
32399          * @param {Roo.bootstrap.DocumentManager} this
32400          */
32401         "refresh" : true,
32402         /**
32403          * @event click
32404          * Fire after click the image
32405          * @param {Roo.bootstrap.DocumentManager} this
32406          * @param {Object} file
32407          */
32408         "click" : true,
32409         /**
32410          * @event edit
32411          * Fire when upload a image and editable set to true
32412          * @param {Roo.bootstrap.DocumentManager} this
32413          * @param {Object} file
32414          */
32415         "edit" : true,
32416         /**
32417          * @event beforeselectfile
32418          * Fire before select file
32419          * @param {Roo.bootstrap.DocumentManager} this
32420          */
32421         "beforeselectfile" : true,
32422         /**
32423          * @event process
32424          * Fire before process file
32425          * @param {Roo.bootstrap.DocumentManager} this
32426          * @param {Object} file
32427          */
32428         "process" : true,
32429         /**
32430          * @event previewrendered
32431          * Fire when preview rendered
32432          * @param {Roo.bootstrap.DocumentManager} this
32433          * @param {Object} file
32434          */
32435         "previewrendered" : true,
32436         /**
32437          */
32438         "previewResize" : true
32439         
32440     });
32441 };
32442
32443 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32444     
32445     boxes : 0,
32446     inputName : '',
32447     thumbSize : 300,
32448     multiple : true,
32449     files : false,
32450     method : 'POST',
32451     url : '',
32452     paramName : 'imageUpload',
32453     toolTipName : 'filename',
32454     fieldLabel : '',
32455     labelWidth : 4,
32456     labelAlign : 'left',
32457     editable : true,
32458     delegates : false,
32459     xhr : false, 
32460     
32461     labellg : 0,
32462     labelmd : 0,
32463     labelsm : 0,
32464     labelxs : 0,
32465     
32466     getAutoCreate : function()
32467     {   
32468         var managerWidget = {
32469             tag : 'div',
32470             cls : 'roo-document-manager',
32471             cn : [
32472                 {
32473                     tag : 'input',
32474                     cls : 'roo-document-manager-selector',
32475                     type : 'file'
32476                 },
32477                 {
32478                     tag : 'div',
32479                     cls : 'roo-document-manager-uploader',
32480                     cn : [
32481                         {
32482                             tag : 'div',
32483                             cls : 'roo-document-manager-upload-btn',
32484                             html : '<i class="fa fa-plus"></i>'
32485                         }
32486                     ]
32487                     
32488                 }
32489             ]
32490         };
32491         
32492         var content = [
32493             {
32494                 tag : 'div',
32495                 cls : 'column col-md-12',
32496                 cn : managerWidget
32497             }
32498         ];
32499         
32500         if(this.fieldLabel.length){
32501             
32502             content = [
32503                 {
32504                     tag : 'div',
32505                     cls : 'column col-md-12',
32506                     html : this.fieldLabel
32507                 },
32508                 {
32509                     tag : 'div',
32510                     cls : 'column col-md-12',
32511                     cn : managerWidget
32512                 }
32513             ];
32514
32515             if(this.labelAlign == 'left'){
32516                 content = [
32517                     {
32518                         tag : 'div',
32519                         cls : 'column',
32520                         html : this.fieldLabel
32521                     },
32522                     {
32523                         tag : 'div',
32524                         cls : 'column',
32525                         cn : managerWidget
32526                     }
32527                 ];
32528                 
32529                 if(this.labelWidth > 12){
32530                     content[0].style = "width: " + this.labelWidth + 'px';
32531                 }
32532
32533                 if(this.labelWidth < 13 && this.labelmd == 0){
32534                     this.labelmd = this.labelWidth;
32535                 }
32536
32537                 if(this.labellg > 0){
32538                     content[0].cls += ' col-lg-' + this.labellg;
32539                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32540                 }
32541
32542                 if(this.labelmd > 0){
32543                     content[0].cls += ' col-md-' + this.labelmd;
32544                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32545                 }
32546
32547                 if(this.labelsm > 0){
32548                     content[0].cls += ' col-sm-' + this.labelsm;
32549                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32550                 }
32551
32552                 if(this.labelxs > 0){
32553                     content[0].cls += ' col-xs-' + this.labelxs;
32554                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32555                 }
32556                 
32557             }
32558         }
32559         
32560         var cfg = {
32561             tag : 'div',
32562             cls : 'row clearfix',
32563             cn : content
32564         };
32565         
32566         return cfg;
32567         
32568     },
32569     
32570     initEvents : function()
32571     {
32572         this.managerEl = this.el.select('.roo-document-manager', true).first();
32573         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32574         
32575         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32576         this.selectorEl.hide();
32577         
32578         if(this.multiple){
32579             this.selectorEl.attr('multiple', 'multiple');
32580         }
32581         
32582         this.selectorEl.on('change', this.onFileSelected, this);
32583         
32584         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32585         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32586         
32587         this.uploader.on('click', this.onUploaderClick, this);
32588         
32589         this.renderProgressDialog();
32590         
32591         var _this = this;
32592         
32593         window.addEventListener("resize", function() { _this.refresh(); } );
32594         
32595         this.fireEvent('initial', this);
32596     },
32597     
32598     renderProgressDialog : function()
32599     {
32600         var _this = this;
32601         
32602         this.progressDialog = new Roo.bootstrap.Modal({
32603             cls : 'roo-document-manager-progress-dialog',
32604             allow_close : false,
32605             animate : false,
32606             title : '',
32607             buttons : [
32608                 {
32609                     name  :'cancel',
32610                     weight : 'danger',
32611                     html : 'Cancel'
32612                 }
32613             ], 
32614             listeners : { 
32615                 btnclick : function() {
32616                     _this.uploadCancel();
32617                     this.hide();
32618                 }
32619             }
32620         });
32621          
32622         this.progressDialog.render(Roo.get(document.body));
32623          
32624         this.progress = new Roo.bootstrap.Progress({
32625             cls : 'roo-document-manager-progress',
32626             active : true,
32627             striped : true
32628         });
32629         
32630         this.progress.render(this.progressDialog.getChildContainer());
32631         
32632         this.progressBar = new Roo.bootstrap.ProgressBar({
32633             cls : 'roo-document-manager-progress-bar',
32634             aria_valuenow : 0,
32635             aria_valuemin : 0,
32636             aria_valuemax : 12,
32637             panel : 'success'
32638         });
32639         
32640         this.progressBar.render(this.progress.getChildContainer());
32641     },
32642     
32643     onUploaderClick : function(e)
32644     {
32645         e.preventDefault();
32646      
32647         if(this.fireEvent('beforeselectfile', this) != false){
32648             this.selectorEl.dom.click();
32649         }
32650         
32651     },
32652     
32653     onFileSelected : function(e)
32654     {
32655         e.preventDefault();
32656         
32657         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32658             return;
32659         }
32660         
32661         Roo.each(this.selectorEl.dom.files, function(file){
32662             if(this.fireEvent('inspect', this, file) != false){
32663                 this.files.push(file);
32664             }
32665         }, this);
32666         
32667         this.queue();
32668         
32669     },
32670     
32671     queue : function()
32672     {
32673         this.selectorEl.dom.value = '';
32674         
32675         if(!this.files || !this.files.length){
32676             return;
32677         }
32678         
32679         if(this.boxes > 0 && this.files.length > this.boxes){
32680             this.files = this.files.slice(0, this.boxes);
32681         }
32682         
32683         this.uploader.show();
32684         
32685         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32686             this.uploader.hide();
32687         }
32688         
32689         var _this = this;
32690         
32691         var files = [];
32692         
32693         var docs = [];
32694         
32695         Roo.each(this.files, function(file){
32696             
32697             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32698                 var f = this.renderPreview(file);
32699                 files.push(f);
32700                 return;
32701             }
32702             
32703             if(file.type.indexOf('image') != -1){
32704                 this.delegates.push(
32705                     (function(){
32706                         _this.process(file);
32707                     }).createDelegate(this)
32708                 );
32709         
32710                 return;
32711             }
32712             
32713             docs.push(
32714                 (function(){
32715                     _this.process(file);
32716                 }).createDelegate(this)
32717             );
32718             
32719         }, this);
32720         
32721         this.files = files;
32722         
32723         this.delegates = this.delegates.concat(docs);
32724         
32725         if(!this.delegates.length){
32726             this.refresh();
32727             return;
32728         }
32729         
32730         this.progressBar.aria_valuemax = this.delegates.length;
32731         
32732         this.arrange();
32733         
32734         return;
32735     },
32736     
32737     arrange : function()
32738     {
32739         if(!this.delegates.length){
32740             this.progressDialog.hide();
32741             this.refresh();
32742             return;
32743         }
32744         
32745         var delegate = this.delegates.shift();
32746         
32747         this.progressDialog.show();
32748         
32749         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32750         
32751         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32752         
32753         delegate();
32754     },
32755     
32756     refresh : function()
32757     {
32758         this.uploader.show();
32759         
32760         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32761             this.uploader.hide();
32762         }
32763         
32764         Roo.isTouch ? this.closable(false) : this.closable(true);
32765         
32766         this.fireEvent('refresh', this);
32767     },
32768     
32769     onRemove : function(e, el, o)
32770     {
32771         e.preventDefault();
32772         
32773         this.fireEvent('remove', this, o);
32774         
32775     },
32776     
32777     remove : function(o)
32778     {
32779         var files = [];
32780         
32781         Roo.each(this.files, function(file){
32782             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32783                 files.push(file);
32784                 return;
32785             }
32786
32787             o.target.remove();
32788
32789         }, this);
32790         
32791         this.files = files;
32792         
32793         this.refresh();
32794     },
32795     
32796     clear : function()
32797     {
32798         Roo.each(this.files, function(file){
32799             if(!file.target){
32800                 return;
32801             }
32802             
32803             file.target.remove();
32804
32805         }, this);
32806         
32807         this.files = [];
32808         
32809         this.refresh();
32810     },
32811     
32812     onClick : function(e, el, o)
32813     {
32814         e.preventDefault();
32815         
32816         this.fireEvent('click', this, o);
32817         
32818     },
32819     
32820     closable : function(closable)
32821     {
32822         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32823             
32824             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32825             
32826             if(closable){
32827                 el.show();
32828                 return;
32829             }
32830             
32831             el.hide();
32832             
32833         }, this);
32834     },
32835     
32836     xhrOnLoad : function(xhr)
32837     {
32838         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32839             el.remove();
32840         }, this);
32841         
32842         if (xhr.readyState !== 4) {
32843             this.arrange();
32844             this.fireEvent('exception', this, xhr);
32845             return;
32846         }
32847
32848         var response = Roo.decode(xhr.responseText);
32849         
32850         if(!response.success){
32851             this.arrange();
32852             this.fireEvent('exception', this, xhr);
32853             return;
32854         }
32855         
32856         var file = this.renderPreview(response.data);
32857         
32858         this.files.push(file);
32859         
32860         this.arrange();
32861         
32862         this.fireEvent('afterupload', this, xhr);
32863         
32864     },
32865     
32866     xhrOnError : function(xhr)
32867     {
32868         Roo.log('xhr on error');
32869         
32870         var response = Roo.decode(xhr.responseText);
32871           
32872         Roo.log(response);
32873         
32874         this.arrange();
32875     },
32876     
32877     process : function(file)
32878     {
32879         if(this.fireEvent('process', this, file) !== false){
32880             if(this.editable && file.type.indexOf('image') != -1){
32881                 this.fireEvent('edit', this, file);
32882                 return;
32883             }
32884
32885             this.uploadStart(file, false);
32886
32887             return;
32888         }
32889         
32890     },
32891     
32892     uploadStart : function(file, crop)
32893     {
32894         this.xhr = new XMLHttpRequest();
32895         
32896         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32897             this.arrange();
32898             return;
32899         }
32900         
32901         file.xhr = this.xhr;
32902             
32903         this.managerEl.createChild({
32904             tag : 'div',
32905             cls : 'roo-document-manager-loading',
32906             cn : [
32907                 {
32908                     tag : 'div',
32909                     tooltip : file.name,
32910                     cls : 'roo-document-manager-thumb',
32911                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32912                 }
32913             ]
32914
32915         });
32916
32917         this.xhr.open(this.method, this.url, true);
32918         
32919         var headers = {
32920             "Accept": "application/json",
32921             "Cache-Control": "no-cache",
32922             "X-Requested-With": "XMLHttpRequest"
32923         };
32924         
32925         for (var headerName in headers) {
32926             var headerValue = headers[headerName];
32927             if (headerValue) {
32928                 this.xhr.setRequestHeader(headerName, headerValue);
32929             }
32930         }
32931         
32932         var _this = this;
32933         
32934         this.xhr.onload = function()
32935         {
32936             _this.xhrOnLoad(_this.xhr);
32937         }
32938         
32939         this.xhr.onerror = function()
32940         {
32941             _this.xhrOnError(_this.xhr);
32942         }
32943         
32944         var formData = new FormData();
32945
32946         formData.append('returnHTML', 'NO');
32947         
32948         if(crop){
32949             formData.append('crop', crop);
32950         }
32951         
32952         formData.append(this.paramName, file, file.name);
32953         
32954         var options = {
32955             file : file, 
32956             manually : false
32957         };
32958         
32959         if(this.fireEvent('prepare', this, formData, options) != false){
32960             
32961             if(options.manually){
32962                 return;
32963             }
32964             
32965             this.xhr.send(formData);
32966             return;
32967         };
32968         
32969         this.uploadCancel();
32970     },
32971     
32972     uploadCancel : function()
32973     {
32974         if (this.xhr) {
32975             this.xhr.abort();
32976         }
32977         
32978         this.delegates = [];
32979         
32980         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32981             el.remove();
32982         }, this);
32983         
32984         this.arrange();
32985     },
32986     
32987     renderPreview : function(file)
32988     {
32989         if(typeof(file.target) != 'undefined' && file.target){
32990             return file;
32991         }
32992         
32993         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32994         
32995         var previewEl = this.managerEl.createChild({
32996             tag : 'div',
32997             cls : 'roo-document-manager-preview',
32998             cn : [
32999                 {
33000                     tag : 'div',
33001                     tooltip : file[this.toolTipName],
33002                     cls : 'roo-document-manager-thumb',
33003                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33004                 },
33005                 {
33006                     tag : 'button',
33007                     cls : 'close',
33008                     html : '<i class="fa fa-times-circle"></i>'
33009                 }
33010             ]
33011         });
33012
33013         var close = previewEl.select('button.close', true).first();
33014
33015         close.on('click', this.onRemove, this, file);
33016
33017         file.target = previewEl;
33018
33019         var image = previewEl.select('img', true).first();
33020         
33021         var _this = this;
33022         
33023         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33024         
33025         image.on('click', this.onClick, this, file);
33026         
33027         this.fireEvent('previewrendered', this, file);
33028         
33029         return file;
33030         
33031     },
33032     
33033     onPreviewLoad : function(file, image)
33034     {
33035         if(typeof(file.target) == 'undefined' || !file.target){
33036             return;
33037         }
33038         
33039         var width = image.dom.naturalWidth || image.dom.width;
33040         var height = image.dom.naturalHeight || image.dom.height;
33041         
33042         if(!this.previewResize) {
33043             return;
33044         }
33045         
33046         if(width > height){
33047             file.target.addClass('wide');
33048             return;
33049         }
33050         
33051         file.target.addClass('tall');
33052         return;
33053         
33054     },
33055     
33056     uploadFromSource : function(file, crop)
33057     {
33058         this.xhr = new XMLHttpRequest();
33059         
33060         this.managerEl.createChild({
33061             tag : 'div',
33062             cls : 'roo-document-manager-loading',
33063             cn : [
33064                 {
33065                     tag : 'div',
33066                     tooltip : file.name,
33067                     cls : 'roo-document-manager-thumb',
33068                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33069                 }
33070             ]
33071
33072         });
33073
33074         this.xhr.open(this.method, this.url, true);
33075         
33076         var headers = {
33077             "Accept": "application/json",
33078             "Cache-Control": "no-cache",
33079             "X-Requested-With": "XMLHttpRequest"
33080         };
33081         
33082         for (var headerName in headers) {
33083             var headerValue = headers[headerName];
33084             if (headerValue) {
33085                 this.xhr.setRequestHeader(headerName, headerValue);
33086             }
33087         }
33088         
33089         var _this = this;
33090         
33091         this.xhr.onload = function()
33092         {
33093             _this.xhrOnLoad(_this.xhr);
33094         }
33095         
33096         this.xhr.onerror = function()
33097         {
33098             _this.xhrOnError(_this.xhr);
33099         }
33100         
33101         var formData = new FormData();
33102
33103         formData.append('returnHTML', 'NO');
33104         
33105         formData.append('crop', crop);
33106         
33107         if(typeof(file.filename) != 'undefined'){
33108             formData.append('filename', file.filename);
33109         }
33110         
33111         if(typeof(file.mimetype) != 'undefined'){
33112             formData.append('mimetype', file.mimetype);
33113         }
33114         
33115         Roo.log(formData);
33116         
33117         if(this.fireEvent('prepare', this, formData) != false){
33118             this.xhr.send(formData);
33119         };
33120     }
33121 });
33122
33123 /*
33124 * Licence: LGPL
33125 */
33126
33127 /**
33128  * @class Roo.bootstrap.DocumentViewer
33129  * @extends Roo.bootstrap.Component
33130  * Bootstrap DocumentViewer class
33131  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33132  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33133  * 
33134  * @constructor
33135  * Create a new DocumentViewer
33136  * @param {Object} config The config object
33137  */
33138
33139 Roo.bootstrap.DocumentViewer = function(config){
33140     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33141     
33142     this.addEvents({
33143         /**
33144          * @event initial
33145          * Fire after initEvent
33146          * @param {Roo.bootstrap.DocumentViewer} this
33147          */
33148         "initial" : true,
33149         /**
33150          * @event click
33151          * Fire after click
33152          * @param {Roo.bootstrap.DocumentViewer} this
33153          */
33154         "click" : true,
33155         /**
33156          * @event download
33157          * Fire after download button
33158          * @param {Roo.bootstrap.DocumentViewer} this
33159          */
33160         "download" : true,
33161         /**
33162          * @event trash
33163          * Fire after trash button
33164          * @param {Roo.bootstrap.DocumentViewer} this
33165          */
33166         "trash" : true
33167         
33168     });
33169 };
33170
33171 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33172     
33173     showDownload : true,
33174     
33175     showTrash : true,
33176     
33177     getAutoCreate : function()
33178     {
33179         var cfg = {
33180             tag : 'div',
33181             cls : 'roo-document-viewer',
33182             cn : [
33183                 {
33184                     tag : 'div',
33185                     cls : 'roo-document-viewer-body',
33186                     cn : [
33187                         {
33188                             tag : 'div',
33189                             cls : 'roo-document-viewer-thumb',
33190                             cn : [
33191                                 {
33192                                     tag : 'img',
33193                                     cls : 'roo-document-viewer-image'
33194                                 }
33195                             ]
33196                         }
33197                     ]
33198                 },
33199                 {
33200                     tag : 'div',
33201                     cls : 'roo-document-viewer-footer',
33202                     cn : {
33203                         tag : 'div',
33204                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33205                         cn : [
33206                             {
33207                                 tag : 'div',
33208                                 cls : 'btn-group roo-document-viewer-download',
33209                                 cn : [
33210                                     {
33211                                         tag : 'button',
33212                                         cls : 'btn btn-default',
33213                                         html : '<i class="fa fa-download"></i>'
33214                                     }
33215                                 ]
33216                             },
33217                             {
33218                                 tag : 'div',
33219                                 cls : 'btn-group roo-document-viewer-trash',
33220                                 cn : [
33221                                     {
33222                                         tag : 'button',
33223                                         cls : 'btn btn-default',
33224                                         html : '<i class="fa fa-trash"></i>'
33225                                     }
33226                                 ]
33227                             }
33228                         ]
33229                     }
33230                 }
33231             ]
33232         };
33233         
33234         return cfg;
33235     },
33236     
33237     initEvents : function()
33238     {
33239         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33240         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33241         
33242         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33243         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33244         
33245         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33246         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33247         
33248         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33249         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33250         
33251         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33252         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33253         
33254         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33255         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33256         
33257         this.bodyEl.on('click', this.onClick, this);
33258         this.downloadBtn.on('click', this.onDownload, this);
33259         this.trashBtn.on('click', this.onTrash, this);
33260         
33261         this.downloadBtn.hide();
33262         this.trashBtn.hide();
33263         
33264         if(this.showDownload){
33265             this.downloadBtn.show();
33266         }
33267         
33268         if(this.showTrash){
33269             this.trashBtn.show();
33270         }
33271         
33272         if(!this.showDownload && !this.showTrash) {
33273             this.footerEl.hide();
33274         }
33275         
33276     },
33277     
33278     initial : function()
33279     {
33280         this.fireEvent('initial', this);
33281         
33282     },
33283     
33284     onClick : function(e)
33285     {
33286         e.preventDefault();
33287         
33288         this.fireEvent('click', this);
33289     },
33290     
33291     onDownload : function(e)
33292     {
33293         e.preventDefault();
33294         
33295         this.fireEvent('download', this);
33296     },
33297     
33298     onTrash : function(e)
33299     {
33300         e.preventDefault();
33301         
33302         this.fireEvent('trash', this);
33303     }
33304     
33305 });
33306 /*
33307  * - LGPL
33308  *
33309  * nav progress bar
33310  * 
33311  */
33312
33313 /**
33314  * @class Roo.bootstrap.NavProgressBar
33315  * @extends Roo.bootstrap.Component
33316  * Bootstrap NavProgressBar class
33317  * 
33318  * @constructor
33319  * Create a new nav progress bar
33320  * @param {Object} config The config object
33321  */
33322
33323 Roo.bootstrap.NavProgressBar = function(config){
33324     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33325
33326     this.bullets = this.bullets || [];
33327    
33328 //    Roo.bootstrap.NavProgressBar.register(this);
33329      this.addEvents({
33330         /**
33331              * @event changed
33332              * Fires when the active item changes
33333              * @param {Roo.bootstrap.NavProgressBar} this
33334              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33335              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33336          */
33337         'changed': true
33338      });
33339     
33340 };
33341
33342 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33343     
33344     bullets : [],
33345     barItems : [],
33346     
33347     getAutoCreate : function()
33348     {
33349         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33350         
33351         cfg = {
33352             tag : 'div',
33353             cls : 'roo-navigation-bar-group',
33354             cn : [
33355                 {
33356                     tag : 'div',
33357                     cls : 'roo-navigation-top-bar'
33358                 },
33359                 {
33360                     tag : 'div',
33361                     cls : 'roo-navigation-bullets-bar',
33362                     cn : [
33363                         {
33364                             tag : 'ul',
33365                             cls : 'roo-navigation-bar'
33366                         }
33367                     ]
33368                 },
33369                 
33370                 {
33371                     tag : 'div',
33372                     cls : 'roo-navigation-bottom-bar'
33373                 }
33374             ]
33375             
33376         };
33377         
33378         return cfg;
33379         
33380     },
33381     
33382     initEvents: function() 
33383     {
33384         
33385     },
33386     
33387     onRender : function(ct, position) 
33388     {
33389         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33390         
33391         if(this.bullets.length){
33392             Roo.each(this.bullets, function(b){
33393                this.addItem(b);
33394             }, this);
33395         }
33396         
33397         this.format();
33398         
33399     },
33400     
33401     addItem : function(cfg)
33402     {
33403         var item = new Roo.bootstrap.NavProgressItem(cfg);
33404         
33405         item.parentId = this.id;
33406         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33407         
33408         if(cfg.html){
33409             var top = new Roo.bootstrap.Element({
33410                 tag : 'div',
33411                 cls : 'roo-navigation-bar-text'
33412             });
33413             
33414             var bottom = new Roo.bootstrap.Element({
33415                 tag : 'div',
33416                 cls : 'roo-navigation-bar-text'
33417             });
33418             
33419             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33420             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33421             
33422             var topText = new Roo.bootstrap.Element({
33423                 tag : 'span',
33424                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33425             });
33426             
33427             var bottomText = new Roo.bootstrap.Element({
33428                 tag : 'span',
33429                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33430             });
33431             
33432             topText.onRender(top.el, null);
33433             bottomText.onRender(bottom.el, null);
33434             
33435             item.topEl = top;
33436             item.bottomEl = bottom;
33437         }
33438         
33439         this.barItems.push(item);
33440         
33441         return item;
33442     },
33443     
33444     getActive : function()
33445     {
33446         var active = false;
33447         
33448         Roo.each(this.barItems, function(v){
33449             
33450             if (!v.isActive()) {
33451                 return;
33452             }
33453             
33454             active = v;
33455             return false;
33456             
33457         });
33458         
33459         return active;
33460     },
33461     
33462     setActiveItem : function(item)
33463     {
33464         var prev = false;
33465         
33466         Roo.each(this.barItems, function(v){
33467             if (v.rid == item.rid) {
33468                 return ;
33469             }
33470             
33471             if (v.isActive()) {
33472                 v.setActive(false);
33473                 prev = v;
33474             }
33475         });
33476
33477         item.setActive(true);
33478         
33479         this.fireEvent('changed', this, item, prev);
33480     },
33481     
33482     getBarItem: function(rid)
33483     {
33484         var ret = false;
33485         
33486         Roo.each(this.barItems, function(e) {
33487             if (e.rid != rid) {
33488                 return;
33489             }
33490             
33491             ret =  e;
33492             return false;
33493         });
33494         
33495         return ret;
33496     },
33497     
33498     indexOfItem : function(item)
33499     {
33500         var index = false;
33501         
33502         Roo.each(this.barItems, function(v, i){
33503             
33504             if (v.rid != item.rid) {
33505                 return;
33506             }
33507             
33508             index = i;
33509             return false
33510         });
33511         
33512         return index;
33513     },
33514     
33515     setActiveNext : function()
33516     {
33517         var i = this.indexOfItem(this.getActive());
33518         
33519         if (i > this.barItems.length) {
33520             return;
33521         }
33522         
33523         this.setActiveItem(this.barItems[i+1]);
33524     },
33525     
33526     setActivePrev : function()
33527     {
33528         var i = this.indexOfItem(this.getActive());
33529         
33530         if (i  < 1) {
33531             return;
33532         }
33533         
33534         this.setActiveItem(this.barItems[i-1]);
33535     },
33536     
33537     format : function()
33538     {
33539         if(!this.barItems.length){
33540             return;
33541         }
33542      
33543         var width = 100 / this.barItems.length;
33544         
33545         Roo.each(this.barItems, function(i){
33546             i.el.setStyle('width', width + '%');
33547             i.topEl.el.setStyle('width', width + '%');
33548             i.bottomEl.el.setStyle('width', width + '%');
33549         }, this);
33550         
33551     }
33552     
33553 });
33554 /*
33555  * - LGPL
33556  *
33557  * Nav Progress Item
33558  * 
33559  */
33560
33561 /**
33562  * @class Roo.bootstrap.NavProgressItem
33563  * @extends Roo.bootstrap.Component
33564  * Bootstrap NavProgressItem class
33565  * @cfg {String} rid the reference id
33566  * @cfg {Boolean} active (true|false) Is item active default false
33567  * @cfg {Boolean} disabled (true|false) Is item active default false
33568  * @cfg {String} html
33569  * @cfg {String} position (top|bottom) text position default bottom
33570  * @cfg {String} icon show icon instead of number
33571  * 
33572  * @constructor
33573  * Create a new NavProgressItem
33574  * @param {Object} config The config object
33575  */
33576 Roo.bootstrap.NavProgressItem = function(config){
33577     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33578     this.addEvents({
33579         // raw events
33580         /**
33581          * @event click
33582          * The raw click event for the entire grid.
33583          * @param {Roo.bootstrap.NavProgressItem} this
33584          * @param {Roo.EventObject} e
33585          */
33586         "click" : true
33587     });
33588    
33589 };
33590
33591 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33592     
33593     rid : '',
33594     active : false,
33595     disabled : false,
33596     html : '',
33597     position : 'bottom',
33598     icon : false,
33599     
33600     getAutoCreate : function()
33601     {
33602         var iconCls = 'roo-navigation-bar-item-icon';
33603         
33604         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33605         
33606         var cfg = {
33607             tag: 'li',
33608             cls: 'roo-navigation-bar-item',
33609             cn : [
33610                 {
33611                     tag : 'i',
33612                     cls : iconCls
33613                 }
33614             ]
33615         };
33616         
33617         if(this.active){
33618             cfg.cls += ' active';
33619         }
33620         if(this.disabled){
33621             cfg.cls += ' disabled';
33622         }
33623         
33624         return cfg;
33625     },
33626     
33627     disable : function()
33628     {
33629         this.setDisabled(true);
33630     },
33631     
33632     enable : function()
33633     {
33634         this.setDisabled(false);
33635     },
33636     
33637     initEvents: function() 
33638     {
33639         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33640         
33641         this.iconEl.on('click', this.onClick, this);
33642     },
33643     
33644     onClick : function(e)
33645     {
33646         e.preventDefault();
33647         
33648         if(this.disabled){
33649             return;
33650         }
33651         
33652         if(this.fireEvent('click', this, e) === false){
33653             return;
33654         };
33655         
33656         this.parent().setActiveItem(this);
33657     },
33658     
33659     isActive: function () 
33660     {
33661         return this.active;
33662     },
33663     
33664     setActive : function(state)
33665     {
33666         if(this.active == state){
33667             return;
33668         }
33669         
33670         this.active = state;
33671         
33672         if (state) {
33673             this.el.addClass('active');
33674             return;
33675         }
33676         
33677         this.el.removeClass('active');
33678         
33679         return;
33680     },
33681     
33682     setDisabled : function(state)
33683     {
33684         if(this.disabled == state){
33685             return;
33686         }
33687         
33688         this.disabled = state;
33689         
33690         if (state) {
33691             this.el.addClass('disabled');
33692             return;
33693         }
33694         
33695         this.el.removeClass('disabled');
33696     },
33697     
33698     tooltipEl : function()
33699     {
33700         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33701     }
33702 });
33703  
33704
33705  /*
33706  * - LGPL
33707  *
33708  * FieldLabel
33709  * 
33710  */
33711
33712 /**
33713  * @class Roo.bootstrap.FieldLabel
33714  * @extends Roo.bootstrap.Component
33715  * Bootstrap FieldLabel class
33716  * @cfg {String} html contents of the element
33717  * @cfg {String} tag tag of the element default label
33718  * @cfg {String} cls class of the element
33719  * @cfg {String} target label target 
33720  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33721  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33722  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33723  * @cfg {String} iconTooltip default "This field is required"
33724  * @cfg {String} indicatorpos (left|right) default left
33725  * 
33726  * @constructor
33727  * Create a new FieldLabel
33728  * @param {Object} config The config object
33729  */
33730
33731 Roo.bootstrap.FieldLabel = function(config){
33732     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33733     
33734     this.addEvents({
33735             /**
33736              * @event invalid
33737              * Fires after the field has been marked as invalid.
33738              * @param {Roo.form.FieldLabel} this
33739              * @param {String} msg The validation message
33740              */
33741             invalid : true,
33742             /**
33743              * @event valid
33744              * Fires after the field has been validated with no errors.
33745              * @param {Roo.form.FieldLabel} this
33746              */
33747             valid : true
33748         });
33749 };
33750
33751 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33752     
33753     tag: 'label',
33754     cls: '',
33755     html: '',
33756     target: '',
33757     allowBlank : true,
33758     invalidClass : 'has-warning',
33759     validClass : 'has-success',
33760     iconTooltip : 'This field is required',
33761     indicatorpos : 'left',
33762     
33763     getAutoCreate : function(){
33764         
33765         var cls = "";
33766         if (!this.allowBlank) {
33767             cls  = "visible";
33768         }
33769         
33770         var cfg = {
33771             tag : this.tag,
33772             cls : 'roo-bootstrap-field-label ' + this.cls,
33773             for : this.target,
33774             cn : [
33775                 {
33776                     tag : 'i',
33777                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33778                     tooltip : this.iconTooltip
33779                 },
33780                 {
33781                     tag : 'span',
33782                     html : this.html
33783                 }
33784             ] 
33785         };
33786         
33787         if(this.indicatorpos == 'right'){
33788             var cfg = {
33789                 tag : this.tag,
33790                 cls : 'roo-bootstrap-field-label ' + this.cls,
33791                 for : this.target,
33792                 cn : [
33793                     {
33794                         tag : 'span',
33795                         html : this.html
33796                     },
33797                     {
33798                         tag : 'i',
33799                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33800                         tooltip : this.iconTooltip
33801                     }
33802                 ] 
33803             };
33804         }
33805         
33806         return cfg;
33807     },
33808     
33809     initEvents: function() 
33810     {
33811         Roo.bootstrap.Element.superclass.initEvents.call(this);
33812         
33813         this.indicator = this.indicatorEl();
33814         
33815         if(this.indicator){
33816             this.indicator.removeClass('visible');
33817             this.indicator.addClass('invisible');
33818         }
33819         
33820         Roo.bootstrap.FieldLabel.register(this);
33821     },
33822     
33823     indicatorEl : function()
33824     {
33825         var indicator = this.el.select('i.roo-required-indicator',true).first();
33826         
33827         if(!indicator){
33828             return false;
33829         }
33830         
33831         return indicator;
33832         
33833     },
33834     
33835     /**
33836      * Mark this field as valid
33837      */
33838     markValid : function()
33839     {
33840         if(this.indicator){
33841             this.indicator.removeClass('visible');
33842             this.indicator.addClass('invisible');
33843         }
33844         if (Roo.bootstrap.version == 3) {
33845             this.el.removeClass(this.invalidClass);
33846             this.el.addClass(this.validClass);
33847         } else {
33848             this.el.removeClass('is-invalid');
33849             this.el.addClass('is-valid');
33850         }
33851         
33852         
33853         this.fireEvent('valid', this);
33854     },
33855     
33856     /**
33857      * Mark this field as invalid
33858      * @param {String} msg The validation message
33859      */
33860     markInvalid : function(msg)
33861     {
33862         if(this.indicator){
33863             this.indicator.removeClass('invisible');
33864             this.indicator.addClass('visible');
33865         }
33866           if (Roo.bootstrap.version == 3) {
33867             this.el.removeClass(this.validClass);
33868             this.el.addClass(this.invalidClass);
33869         } else {
33870             this.el.removeClass('is-valid');
33871             this.el.addClass('is-invalid');
33872         }
33873         
33874         
33875         this.fireEvent('invalid', this, msg);
33876     }
33877     
33878    
33879 });
33880
33881 Roo.apply(Roo.bootstrap.FieldLabel, {
33882     
33883     groups: {},
33884     
33885      /**
33886     * register a FieldLabel Group
33887     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33888     */
33889     register : function(label)
33890     {
33891         if(this.groups.hasOwnProperty(label.target)){
33892             return;
33893         }
33894      
33895         this.groups[label.target] = label;
33896         
33897     },
33898     /**
33899     * fetch a FieldLabel Group based on the target
33900     * @param {string} target
33901     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33902     */
33903     get: function(target) {
33904         if (typeof(this.groups[target]) == 'undefined') {
33905             return false;
33906         }
33907         
33908         return this.groups[target] ;
33909     }
33910 });
33911
33912  
33913
33914  /*
33915  * - LGPL
33916  *
33917  * page DateSplitField.
33918  * 
33919  */
33920
33921
33922 /**
33923  * @class Roo.bootstrap.DateSplitField
33924  * @extends Roo.bootstrap.Component
33925  * Bootstrap DateSplitField class
33926  * @cfg {string} fieldLabel - the label associated
33927  * @cfg {Number} labelWidth set the width of label (0-12)
33928  * @cfg {String} labelAlign (top|left)
33929  * @cfg {Boolean} dayAllowBlank (true|false) default false
33930  * @cfg {Boolean} monthAllowBlank (true|false) default false
33931  * @cfg {Boolean} yearAllowBlank (true|false) default false
33932  * @cfg {string} dayPlaceholder 
33933  * @cfg {string} monthPlaceholder
33934  * @cfg {string} yearPlaceholder
33935  * @cfg {string} dayFormat default 'd'
33936  * @cfg {string} monthFormat default 'm'
33937  * @cfg {string} yearFormat default 'Y'
33938  * @cfg {Number} labellg set the width of label (1-12)
33939  * @cfg {Number} labelmd set the width of label (1-12)
33940  * @cfg {Number} labelsm set the width of label (1-12)
33941  * @cfg {Number} labelxs set the width of label (1-12)
33942
33943  *     
33944  * @constructor
33945  * Create a new DateSplitField
33946  * @param {Object} config The config object
33947  */
33948
33949 Roo.bootstrap.DateSplitField = function(config){
33950     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33951     
33952     this.addEvents({
33953         // raw events
33954          /**
33955          * @event years
33956          * getting the data of years
33957          * @param {Roo.bootstrap.DateSplitField} this
33958          * @param {Object} years
33959          */
33960         "years" : true,
33961         /**
33962          * @event days
33963          * getting the data of days
33964          * @param {Roo.bootstrap.DateSplitField} this
33965          * @param {Object} days
33966          */
33967         "days" : true,
33968         /**
33969          * @event invalid
33970          * Fires after the field has been marked as invalid.
33971          * @param {Roo.form.Field} this
33972          * @param {String} msg The validation message
33973          */
33974         invalid : true,
33975        /**
33976          * @event valid
33977          * Fires after the field has been validated with no errors.
33978          * @param {Roo.form.Field} this
33979          */
33980         valid : true
33981     });
33982 };
33983
33984 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33985     
33986     fieldLabel : '',
33987     labelAlign : 'top',
33988     labelWidth : 3,
33989     dayAllowBlank : false,
33990     monthAllowBlank : false,
33991     yearAllowBlank : false,
33992     dayPlaceholder : '',
33993     monthPlaceholder : '',
33994     yearPlaceholder : '',
33995     dayFormat : 'd',
33996     monthFormat : 'm',
33997     yearFormat : 'Y',
33998     isFormField : true,
33999     labellg : 0,
34000     labelmd : 0,
34001     labelsm : 0,
34002     labelxs : 0,
34003     
34004     getAutoCreate : function()
34005     {
34006         var cfg = {
34007             tag : 'div',
34008             cls : 'row roo-date-split-field-group',
34009             cn : [
34010                 {
34011                     tag : 'input',
34012                     type : 'hidden',
34013                     cls : 'form-hidden-field roo-date-split-field-group-value',
34014                     name : this.name
34015                 }
34016             ]
34017         };
34018         
34019         var labelCls = 'col-md-12';
34020         var contentCls = 'col-md-4';
34021         
34022         if(this.fieldLabel){
34023             
34024             var label = {
34025                 tag : 'div',
34026                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34027                 cn : [
34028                     {
34029                         tag : 'label',
34030                         html : this.fieldLabel
34031                     }
34032                 ]
34033             };
34034             
34035             if(this.labelAlign == 'left'){
34036             
34037                 if(this.labelWidth > 12){
34038                     label.style = "width: " + this.labelWidth + 'px';
34039                 }
34040
34041                 if(this.labelWidth < 13 && this.labelmd == 0){
34042                     this.labelmd = this.labelWidth;
34043                 }
34044
34045                 if(this.labellg > 0){
34046                     labelCls = ' col-lg-' + this.labellg;
34047                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34048                 }
34049
34050                 if(this.labelmd > 0){
34051                     labelCls = ' col-md-' + this.labelmd;
34052                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34053                 }
34054
34055                 if(this.labelsm > 0){
34056                     labelCls = ' col-sm-' + this.labelsm;
34057                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34058                 }
34059
34060                 if(this.labelxs > 0){
34061                     labelCls = ' col-xs-' + this.labelxs;
34062                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34063                 }
34064             }
34065             
34066             label.cls += ' ' + labelCls;
34067             
34068             cfg.cn.push(label);
34069         }
34070         
34071         Roo.each(['day', 'month', 'year'], function(t){
34072             cfg.cn.push({
34073                 tag : 'div',
34074                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34075             });
34076         }, this);
34077         
34078         return cfg;
34079     },
34080     
34081     inputEl: function ()
34082     {
34083         return this.el.select('.roo-date-split-field-group-value', true).first();
34084     },
34085     
34086     onRender : function(ct, position) 
34087     {
34088         var _this = this;
34089         
34090         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34091         
34092         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34093         
34094         this.dayField = new Roo.bootstrap.ComboBox({
34095             allowBlank : this.dayAllowBlank,
34096             alwaysQuery : true,
34097             displayField : 'value',
34098             editable : false,
34099             fieldLabel : '',
34100             forceSelection : true,
34101             mode : 'local',
34102             placeholder : this.dayPlaceholder,
34103             selectOnFocus : true,
34104             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34105             triggerAction : 'all',
34106             typeAhead : true,
34107             valueField : 'value',
34108             store : new Roo.data.SimpleStore({
34109                 data : (function() {    
34110                     var days = [];
34111                     _this.fireEvent('days', _this, days);
34112                     return days;
34113                 })(),
34114                 fields : [ 'value' ]
34115             }),
34116             listeners : {
34117                 select : function (_self, record, index)
34118                 {
34119                     _this.setValue(_this.getValue());
34120                 }
34121             }
34122         });
34123
34124         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34125         
34126         this.monthField = new Roo.bootstrap.MonthField({
34127             after : '<i class=\"fa fa-calendar\"></i>',
34128             allowBlank : this.monthAllowBlank,
34129             placeholder : this.monthPlaceholder,
34130             readOnly : true,
34131             listeners : {
34132                 render : function (_self)
34133                 {
34134                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34135                         e.preventDefault();
34136                         _self.focus();
34137                     });
34138                 },
34139                 select : function (_self, oldvalue, newvalue)
34140                 {
34141                     _this.setValue(_this.getValue());
34142                 }
34143             }
34144         });
34145         
34146         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34147         
34148         this.yearField = new Roo.bootstrap.ComboBox({
34149             allowBlank : this.yearAllowBlank,
34150             alwaysQuery : true,
34151             displayField : 'value',
34152             editable : false,
34153             fieldLabel : '',
34154             forceSelection : true,
34155             mode : 'local',
34156             placeholder : this.yearPlaceholder,
34157             selectOnFocus : true,
34158             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34159             triggerAction : 'all',
34160             typeAhead : true,
34161             valueField : 'value',
34162             store : new Roo.data.SimpleStore({
34163                 data : (function() {
34164                     var years = [];
34165                     _this.fireEvent('years', _this, years);
34166                     return years;
34167                 })(),
34168                 fields : [ 'value' ]
34169             }),
34170             listeners : {
34171                 select : function (_self, record, index)
34172                 {
34173                     _this.setValue(_this.getValue());
34174                 }
34175             }
34176         });
34177
34178         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34179     },
34180     
34181     setValue : function(v, format)
34182     {
34183         this.inputEl.dom.value = v;
34184         
34185         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34186         
34187         var d = Date.parseDate(v, f);
34188         
34189         if(!d){
34190             this.validate();
34191             return;
34192         }
34193         
34194         this.setDay(d.format(this.dayFormat));
34195         this.setMonth(d.format(this.monthFormat));
34196         this.setYear(d.format(this.yearFormat));
34197         
34198         this.validate();
34199         
34200         return;
34201     },
34202     
34203     setDay : function(v)
34204     {
34205         this.dayField.setValue(v);
34206         this.inputEl.dom.value = this.getValue();
34207         this.validate();
34208         return;
34209     },
34210     
34211     setMonth : function(v)
34212     {
34213         this.monthField.setValue(v, true);
34214         this.inputEl.dom.value = this.getValue();
34215         this.validate();
34216         return;
34217     },
34218     
34219     setYear : function(v)
34220     {
34221         this.yearField.setValue(v);
34222         this.inputEl.dom.value = this.getValue();
34223         this.validate();
34224         return;
34225     },
34226     
34227     getDay : function()
34228     {
34229         return this.dayField.getValue();
34230     },
34231     
34232     getMonth : function()
34233     {
34234         return this.monthField.getValue();
34235     },
34236     
34237     getYear : function()
34238     {
34239         return this.yearField.getValue();
34240     },
34241     
34242     getValue : function()
34243     {
34244         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34245         
34246         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34247         
34248         return date;
34249     },
34250     
34251     reset : function()
34252     {
34253         this.setDay('');
34254         this.setMonth('');
34255         this.setYear('');
34256         this.inputEl.dom.value = '';
34257         this.validate();
34258         return;
34259     },
34260     
34261     validate : function()
34262     {
34263         var d = this.dayField.validate();
34264         var m = this.monthField.validate();
34265         var y = this.yearField.validate();
34266         
34267         var valid = true;
34268         
34269         if(
34270                 (!this.dayAllowBlank && !d) ||
34271                 (!this.monthAllowBlank && !m) ||
34272                 (!this.yearAllowBlank && !y)
34273         ){
34274             valid = false;
34275         }
34276         
34277         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34278             return valid;
34279         }
34280         
34281         if(valid){
34282             this.markValid();
34283             return valid;
34284         }
34285         
34286         this.markInvalid();
34287         
34288         return valid;
34289     },
34290     
34291     markValid : function()
34292     {
34293         
34294         var label = this.el.select('label', true).first();
34295         var icon = this.el.select('i.fa-star', true).first();
34296
34297         if(label && icon){
34298             icon.remove();
34299         }
34300         
34301         this.fireEvent('valid', this);
34302     },
34303     
34304      /**
34305      * Mark this field as invalid
34306      * @param {String} msg The validation message
34307      */
34308     markInvalid : function(msg)
34309     {
34310         
34311         var label = this.el.select('label', true).first();
34312         var icon = this.el.select('i.fa-star', true).first();
34313
34314         if(label && !icon){
34315             this.el.select('.roo-date-split-field-label', true).createChild({
34316                 tag : 'i',
34317                 cls : 'text-danger fa fa-lg fa-star',
34318                 tooltip : 'This field is required',
34319                 style : 'margin-right:5px;'
34320             }, label, true);
34321         }
34322         
34323         this.fireEvent('invalid', this, msg);
34324     },
34325     
34326     clearInvalid : function()
34327     {
34328         var label = this.el.select('label', true).first();
34329         var icon = this.el.select('i.fa-star', true).first();
34330
34331         if(label && icon){
34332             icon.remove();
34333         }
34334         
34335         this.fireEvent('valid', this);
34336     },
34337     
34338     getName: function()
34339     {
34340         return this.name;
34341     }
34342     
34343 });
34344
34345  /**
34346  *
34347  * This is based on 
34348  * http://masonry.desandro.com
34349  *
34350  * The idea is to render all the bricks based on vertical width...
34351  *
34352  * The original code extends 'outlayer' - we might need to use that....
34353  * 
34354  */
34355
34356
34357 /**
34358  * @class Roo.bootstrap.LayoutMasonry
34359  * @extends Roo.bootstrap.Component
34360  * Bootstrap Layout Masonry class
34361  * 
34362  * @constructor
34363  * Create a new Element
34364  * @param {Object} config The config object
34365  */
34366
34367 Roo.bootstrap.LayoutMasonry = function(config){
34368     
34369     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34370     
34371     this.bricks = [];
34372     
34373     Roo.bootstrap.LayoutMasonry.register(this);
34374     
34375     this.addEvents({
34376         // raw events
34377         /**
34378          * @event layout
34379          * Fire after layout the items
34380          * @param {Roo.bootstrap.LayoutMasonry} this
34381          * @param {Roo.EventObject} e
34382          */
34383         "layout" : true
34384     });
34385     
34386 };
34387
34388 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34389     
34390     /**
34391      * @cfg {Boolean} isLayoutInstant = no animation?
34392      */   
34393     isLayoutInstant : false, // needed?
34394    
34395     /**
34396      * @cfg {Number} boxWidth  width of the columns
34397      */   
34398     boxWidth : 450,
34399     
34400       /**
34401      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34402      */   
34403     boxHeight : 0,
34404     
34405     /**
34406      * @cfg {Number} padWidth padding below box..
34407      */   
34408     padWidth : 10, 
34409     
34410     /**
34411      * @cfg {Number} gutter gutter width..
34412      */   
34413     gutter : 10,
34414     
34415      /**
34416      * @cfg {Number} maxCols maximum number of columns
34417      */   
34418     
34419     maxCols: 0,
34420     
34421     /**
34422      * @cfg {Boolean} isAutoInitial defalut true
34423      */   
34424     isAutoInitial : true, 
34425     
34426     containerWidth: 0,
34427     
34428     /**
34429      * @cfg {Boolean} isHorizontal defalut false
34430      */   
34431     isHorizontal : false, 
34432
34433     currentSize : null,
34434     
34435     tag: 'div',
34436     
34437     cls: '',
34438     
34439     bricks: null, //CompositeElement
34440     
34441     cols : 1,
34442     
34443     _isLayoutInited : false,
34444     
34445 //    isAlternative : false, // only use for vertical layout...
34446     
34447     /**
34448      * @cfg {Number} alternativePadWidth padding below box..
34449      */   
34450     alternativePadWidth : 50,
34451     
34452     selectedBrick : [],
34453     
34454     getAutoCreate : function(){
34455         
34456         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34457         
34458         var cfg = {
34459             tag: this.tag,
34460             cls: 'blog-masonary-wrapper ' + this.cls,
34461             cn : {
34462                 cls : 'mas-boxes masonary'
34463             }
34464         };
34465         
34466         return cfg;
34467     },
34468     
34469     getChildContainer: function( )
34470     {
34471         if (this.boxesEl) {
34472             return this.boxesEl;
34473         }
34474         
34475         this.boxesEl = this.el.select('.mas-boxes').first();
34476         
34477         return this.boxesEl;
34478     },
34479     
34480     
34481     initEvents : function()
34482     {
34483         var _this = this;
34484         
34485         if(this.isAutoInitial){
34486             Roo.log('hook children rendered');
34487             this.on('childrenrendered', function() {
34488                 Roo.log('children rendered');
34489                 _this.initial();
34490             } ,this);
34491         }
34492     },
34493     
34494     initial : function()
34495     {
34496         this.selectedBrick = [];
34497         
34498         this.currentSize = this.el.getBox(true);
34499         
34500         Roo.EventManager.onWindowResize(this.resize, this); 
34501
34502         if(!this.isAutoInitial){
34503             this.layout();
34504             return;
34505         }
34506         
34507         this.layout();
34508         
34509         return;
34510         //this.layout.defer(500,this);
34511         
34512     },
34513     
34514     resize : function()
34515     {
34516         var cs = this.el.getBox(true);
34517         
34518         if (
34519                 this.currentSize.width == cs.width && 
34520                 this.currentSize.x == cs.x && 
34521                 this.currentSize.height == cs.height && 
34522                 this.currentSize.y == cs.y 
34523         ) {
34524             Roo.log("no change in with or X or Y");
34525             return;
34526         }
34527         
34528         this.currentSize = cs;
34529         
34530         this.layout();
34531         
34532     },
34533     
34534     layout : function()
34535     {   
34536         this._resetLayout();
34537         
34538         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34539         
34540         this.layoutItems( isInstant );
34541       
34542         this._isLayoutInited = true;
34543         
34544         this.fireEvent('layout', this);
34545         
34546     },
34547     
34548     _resetLayout : function()
34549     {
34550         if(this.isHorizontal){
34551             this.horizontalMeasureColumns();
34552             return;
34553         }
34554         
34555         this.verticalMeasureColumns();
34556         
34557     },
34558     
34559     verticalMeasureColumns : function()
34560     {
34561         this.getContainerWidth();
34562         
34563 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34564 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34565 //            return;
34566 //        }
34567         
34568         var boxWidth = this.boxWidth + this.padWidth;
34569         
34570         if(this.containerWidth < this.boxWidth){
34571             boxWidth = this.containerWidth
34572         }
34573         
34574         var containerWidth = this.containerWidth;
34575         
34576         var cols = Math.floor(containerWidth / boxWidth);
34577         
34578         this.cols = Math.max( cols, 1 );
34579         
34580         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34581         
34582         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34583         
34584         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34585         
34586         this.colWidth = boxWidth + avail - this.padWidth;
34587         
34588         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34589         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34590     },
34591     
34592     horizontalMeasureColumns : function()
34593     {
34594         this.getContainerWidth();
34595         
34596         var boxWidth = this.boxWidth;
34597         
34598         if(this.containerWidth < boxWidth){
34599             boxWidth = this.containerWidth;
34600         }
34601         
34602         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34603         
34604         this.el.setHeight(boxWidth);
34605         
34606     },
34607     
34608     getContainerWidth : function()
34609     {
34610         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34611     },
34612     
34613     layoutItems : function( isInstant )
34614     {
34615         Roo.log(this.bricks);
34616         
34617         var items = Roo.apply([], this.bricks);
34618         
34619         if(this.isHorizontal){
34620             this._horizontalLayoutItems( items , isInstant );
34621             return;
34622         }
34623         
34624 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34625 //            this._verticalAlternativeLayoutItems( items , isInstant );
34626 //            return;
34627 //        }
34628         
34629         this._verticalLayoutItems( items , isInstant );
34630         
34631     },
34632     
34633     _verticalLayoutItems : function ( items , isInstant)
34634     {
34635         if ( !items || !items.length ) {
34636             return;
34637         }
34638         
34639         var standard = [
34640             ['xs', 'xs', 'xs', 'tall'],
34641             ['xs', 'xs', 'tall'],
34642             ['xs', 'xs', 'sm'],
34643             ['xs', 'xs', 'xs'],
34644             ['xs', 'tall'],
34645             ['xs', 'sm'],
34646             ['xs', 'xs'],
34647             ['xs'],
34648             
34649             ['sm', 'xs', 'xs'],
34650             ['sm', 'xs'],
34651             ['sm'],
34652             
34653             ['tall', 'xs', 'xs', 'xs'],
34654             ['tall', 'xs', 'xs'],
34655             ['tall', 'xs'],
34656             ['tall']
34657             
34658         ];
34659         
34660         var queue = [];
34661         
34662         var boxes = [];
34663         
34664         var box = [];
34665         
34666         Roo.each(items, function(item, k){
34667             
34668             switch (item.size) {
34669                 // these layouts take up a full box,
34670                 case 'md' :
34671                 case 'md-left' :
34672                 case 'md-right' :
34673                 case 'wide' :
34674                     
34675                     if(box.length){
34676                         boxes.push(box);
34677                         box = [];
34678                     }
34679                     
34680                     boxes.push([item]);
34681                     
34682                     break;
34683                     
34684                 case 'xs' :
34685                 case 'sm' :
34686                 case 'tall' :
34687                     
34688                     box.push(item);
34689                     
34690                     break;
34691                 default :
34692                     break;
34693                     
34694             }
34695             
34696         }, this);
34697         
34698         if(box.length){
34699             boxes.push(box);
34700             box = [];
34701         }
34702         
34703         var filterPattern = function(box, length)
34704         {
34705             if(!box.length){
34706                 return;
34707             }
34708             
34709             var match = false;
34710             
34711             var pattern = box.slice(0, length);
34712             
34713             var format = [];
34714             
34715             Roo.each(pattern, function(i){
34716                 format.push(i.size);
34717             }, this);
34718             
34719             Roo.each(standard, function(s){
34720                 
34721                 if(String(s) != String(format)){
34722                     return;
34723                 }
34724                 
34725                 match = true;
34726                 return false;
34727                 
34728             }, this);
34729             
34730             if(!match && length == 1){
34731                 return;
34732             }
34733             
34734             if(!match){
34735                 filterPattern(box, length - 1);
34736                 return;
34737             }
34738                 
34739             queue.push(pattern);
34740
34741             box = box.slice(length, box.length);
34742
34743             filterPattern(box, 4);
34744
34745             return;
34746             
34747         }
34748         
34749         Roo.each(boxes, function(box, k){
34750             
34751             if(!box.length){
34752                 return;
34753             }
34754             
34755             if(box.length == 1){
34756                 queue.push(box);
34757                 return;
34758             }
34759             
34760             filterPattern(box, 4);
34761             
34762         }, this);
34763         
34764         this._processVerticalLayoutQueue( queue, isInstant );
34765         
34766     },
34767     
34768 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34769 //    {
34770 //        if ( !items || !items.length ) {
34771 //            return;
34772 //        }
34773 //
34774 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34775 //        
34776 //    },
34777     
34778     _horizontalLayoutItems : function ( items , isInstant)
34779     {
34780         if ( !items || !items.length || items.length < 3) {
34781             return;
34782         }
34783         
34784         items.reverse();
34785         
34786         var eItems = items.slice(0, 3);
34787         
34788         items = items.slice(3, items.length);
34789         
34790         var standard = [
34791             ['xs', 'xs', 'xs', 'wide'],
34792             ['xs', 'xs', 'wide'],
34793             ['xs', 'xs', 'sm'],
34794             ['xs', 'xs', 'xs'],
34795             ['xs', 'wide'],
34796             ['xs', 'sm'],
34797             ['xs', 'xs'],
34798             ['xs'],
34799             
34800             ['sm', 'xs', 'xs'],
34801             ['sm', 'xs'],
34802             ['sm'],
34803             
34804             ['wide', 'xs', 'xs', 'xs'],
34805             ['wide', 'xs', 'xs'],
34806             ['wide', 'xs'],
34807             ['wide'],
34808             
34809             ['wide-thin']
34810         ];
34811         
34812         var queue = [];
34813         
34814         var boxes = [];
34815         
34816         var box = [];
34817         
34818         Roo.each(items, function(item, k){
34819             
34820             switch (item.size) {
34821                 case 'md' :
34822                 case 'md-left' :
34823                 case 'md-right' :
34824                 case 'tall' :
34825                     
34826                     if(box.length){
34827                         boxes.push(box);
34828                         box = [];
34829                     }
34830                     
34831                     boxes.push([item]);
34832                     
34833                     break;
34834                     
34835                 case 'xs' :
34836                 case 'sm' :
34837                 case 'wide' :
34838                 case 'wide-thin' :
34839                     
34840                     box.push(item);
34841                     
34842                     break;
34843                 default :
34844                     break;
34845                     
34846             }
34847             
34848         }, this);
34849         
34850         if(box.length){
34851             boxes.push(box);
34852             box = [];
34853         }
34854         
34855         var filterPattern = function(box, length)
34856         {
34857             if(!box.length){
34858                 return;
34859             }
34860             
34861             var match = false;
34862             
34863             var pattern = box.slice(0, length);
34864             
34865             var format = [];
34866             
34867             Roo.each(pattern, function(i){
34868                 format.push(i.size);
34869             }, this);
34870             
34871             Roo.each(standard, function(s){
34872                 
34873                 if(String(s) != String(format)){
34874                     return;
34875                 }
34876                 
34877                 match = true;
34878                 return false;
34879                 
34880             }, this);
34881             
34882             if(!match && length == 1){
34883                 return;
34884             }
34885             
34886             if(!match){
34887                 filterPattern(box, length - 1);
34888                 return;
34889             }
34890                 
34891             queue.push(pattern);
34892
34893             box = box.slice(length, box.length);
34894
34895             filterPattern(box, 4);
34896
34897             return;
34898             
34899         }
34900         
34901         Roo.each(boxes, function(box, k){
34902             
34903             if(!box.length){
34904                 return;
34905             }
34906             
34907             if(box.length == 1){
34908                 queue.push(box);
34909                 return;
34910             }
34911             
34912             filterPattern(box, 4);
34913             
34914         }, this);
34915         
34916         
34917         var prune = [];
34918         
34919         var pos = this.el.getBox(true);
34920         
34921         var minX = pos.x;
34922         
34923         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34924         
34925         var hit_end = false;
34926         
34927         Roo.each(queue, function(box){
34928             
34929             if(hit_end){
34930                 
34931                 Roo.each(box, function(b){
34932                 
34933                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34934                     b.el.hide();
34935
34936                 }, this);
34937
34938                 return;
34939             }
34940             
34941             var mx = 0;
34942             
34943             Roo.each(box, function(b){
34944                 
34945                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34946                 b.el.show();
34947
34948                 mx = Math.max(mx, b.x);
34949                 
34950             }, this);
34951             
34952             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34953             
34954             if(maxX < minX){
34955                 
34956                 Roo.each(box, function(b){
34957                 
34958                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34959                     b.el.hide();
34960                     
34961                 }, this);
34962                 
34963                 hit_end = true;
34964                 
34965                 return;
34966             }
34967             
34968             prune.push(box);
34969             
34970         }, this);
34971         
34972         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34973     },
34974     
34975     /** Sets position of item in DOM
34976     * @param {Element} item
34977     * @param {Number} x - horizontal position
34978     * @param {Number} y - vertical position
34979     * @param {Boolean} isInstant - disables transitions
34980     */
34981     _processVerticalLayoutQueue : function( queue, isInstant )
34982     {
34983         var pos = this.el.getBox(true);
34984         var x = pos.x;
34985         var y = pos.y;
34986         var maxY = [];
34987         
34988         for (var i = 0; i < this.cols; i++){
34989             maxY[i] = pos.y;
34990         }
34991         
34992         Roo.each(queue, function(box, k){
34993             
34994             var col = k % this.cols;
34995             
34996             Roo.each(box, function(b,kk){
34997                 
34998                 b.el.position('absolute');
34999                 
35000                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35001                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35002                 
35003                 if(b.size == 'md-left' || b.size == 'md-right'){
35004                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35005                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35006                 }
35007                 
35008                 b.el.setWidth(width);
35009                 b.el.setHeight(height);
35010                 // iframe?
35011                 b.el.select('iframe',true).setSize(width,height);
35012                 
35013             }, this);
35014             
35015             for (var i = 0; i < this.cols; i++){
35016                 
35017                 if(maxY[i] < maxY[col]){
35018                     col = i;
35019                     continue;
35020                 }
35021                 
35022                 col = Math.min(col, i);
35023                 
35024             }
35025             
35026             x = pos.x + col * (this.colWidth + this.padWidth);
35027             
35028             y = maxY[col];
35029             
35030             var positions = [];
35031             
35032             switch (box.length){
35033                 case 1 :
35034                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35035                     break;
35036                 case 2 :
35037                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35038                     break;
35039                 case 3 :
35040                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35041                     break;
35042                 case 4 :
35043                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35044                     break;
35045                 default :
35046                     break;
35047             }
35048             
35049             Roo.each(box, function(b,kk){
35050                 
35051                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35052                 
35053                 var sz = b.el.getSize();
35054                 
35055                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35056                 
35057             }, this);
35058             
35059         }, this);
35060         
35061         var mY = 0;
35062         
35063         for (var i = 0; i < this.cols; i++){
35064             mY = Math.max(mY, maxY[i]);
35065         }
35066         
35067         this.el.setHeight(mY - pos.y);
35068         
35069     },
35070     
35071 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35072 //    {
35073 //        var pos = this.el.getBox(true);
35074 //        var x = pos.x;
35075 //        var y = pos.y;
35076 //        var maxX = pos.right;
35077 //        
35078 //        var maxHeight = 0;
35079 //        
35080 //        Roo.each(items, function(item, k){
35081 //            
35082 //            var c = k % 2;
35083 //            
35084 //            item.el.position('absolute');
35085 //                
35086 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35087 //
35088 //            item.el.setWidth(width);
35089 //
35090 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35091 //
35092 //            item.el.setHeight(height);
35093 //            
35094 //            if(c == 0){
35095 //                item.el.setXY([x, y], isInstant ? false : true);
35096 //            } else {
35097 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35098 //            }
35099 //            
35100 //            y = y + height + this.alternativePadWidth;
35101 //            
35102 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35103 //            
35104 //        }, this);
35105 //        
35106 //        this.el.setHeight(maxHeight);
35107 //        
35108 //    },
35109     
35110     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35111     {
35112         var pos = this.el.getBox(true);
35113         
35114         var minX = pos.x;
35115         var minY = pos.y;
35116         
35117         var maxX = pos.right;
35118         
35119         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35120         
35121         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35122         
35123         Roo.each(queue, function(box, k){
35124             
35125             Roo.each(box, function(b, kk){
35126                 
35127                 b.el.position('absolute');
35128                 
35129                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35130                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35131                 
35132                 if(b.size == 'md-left' || b.size == 'md-right'){
35133                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35134                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35135                 }
35136                 
35137                 b.el.setWidth(width);
35138                 b.el.setHeight(height);
35139                 
35140             }, this);
35141             
35142             if(!box.length){
35143                 return;
35144             }
35145             
35146             var positions = [];
35147             
35148             switch (box.length){
35149                 case 1 :
35150                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35151                     break;
35152                 case 2 :
35153                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35154                     break;
35155                 case 3 :
35156                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35157                     break;
35158                 case 4 :
35159                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35160                     break;
35161                 default :
35162                     break;
35163             }
35164             
35165             Roo.each(box, function(b,kk){
35166                 
35167                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35168                 
35169                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35170                 
35171             }, this);
35172             
35173         }, this);
35174         
35175     },
35176     
35177     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35178     {
35179         Roo.each(eItems, function(b,k){
35180             
35181             b.size = (k == 0) ? 'sm' : 'xs';
35182             b.x = (k == 0) ? 2 : 1;
35183             b.y = (k == 0) ? 2 : 1;
35184             
35185             b.el.position('absolute');
35186             
35187             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35188                 
35189             b.el.setWidth(width);
35190             
35191             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35192             
35193             b.el.setHeight(height);
35194             
35195         }, this);
35196
35197         var positions = [];
35198         
35199         positions.push({
35200             x : maxX - this.unitWidth * 2 - this.gutter,
35201             y : minY
35202         });
35203         
35204         positions.push({
35205             x : maxX - this.unitWidth,
35206             y : minY + (this.unitWidth + this.gutter) * 2
35207         });
35208         
35209         positions.push({
35210             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35211             y : minY
35212         });
35213         
35214         Roo.each(eItems, function(b,k){
35215             
35216             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35217
35218         }, this);
35219         
35220     },
35221     
35222     getVerticalOneBoxColPositions : function(x, y, box)
35223     {
35224         var pos = [];
35225         
35226         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35227         
35228         if(box[0].size == 'md-left'){
35229             rand = 0;
35230         }
35231         
35232         if(box[0].size == 'md-right'){
35233             rand = 1;
35234         }
35235         
35236         pos.push({
35237             x : x + (this.unitWidth + this.gutter) * rand,
35238             y : y
35239         });
35240         
35241         return pos;
35242     },
35243     
35244     getVerticalTwoBoxColPositions : function(x, y, box)
35245     {
35246         var pos = [];
35247         
35248         if(box[0].size == 'xs'){
35249             
35250             pos.push({
35251                 x : x,
35252                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35253             });
35254
35255             pos.push({
35256                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35257                 y : y
35258             });
35259             
35260             return pos;
35261             
35262         }
35263         
35264         pos.push({
35265             x : x,
35266             y : y
35267         });
35268
35269         pos.push({
35270             x : x + (this.unitWidth + this.gutter) * 2,
35271             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35272         });
35273         
35274         return pos;
35275         
35276     },
35277     
35278     getVerticalThreeBoxColPositions : function(x, y, box)
35279     {
35280         var pos = [];
35281         
35282         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35283             
35284             pos.push({
35285                 x : x,
35286                 y : y
35287             });
35288
35289             pos.push({
35290                 x : x + (this.unitWidth + this.gutter) * 1,
35291                 y : y
35292             });
35293             
35294             pos.push({
35295                 x : x + (this.unitWidth + this.gutter) * 2,
35296                 y : y
35297             });
35298             
35299             return pos;
35300             
35301         }
35302         
35303         if(box[0].size == 'xs' && box[1].size == 'xs'){
35304             
35305             pos.push({
35306                 x : x,
35307                 y : y
35308             });
35309
35310             pos.push({
35311                 x : x,
35312                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35313             });
35314             
35315             pos.push({
35316                 x : x + (this.unitWidth + this.gutter) * 1,
35317                 y : y
35318             });
35319             
35320             return pos;
35321             
35322         }
35323         
35324         pos.push({
35325             x : x,
35326             y : y
35327         });
35328
35329         pos.push({
35330             x : x + (this.unitWidth + this.gutter) * 2,
35331             y : y
35332         });
35333
35334         pos.push({
35335             x : x + (this.unitWidth + this.gutter) * 2,
35336             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35337         });
35338             
35339         return pos;
35340         
35341     },
35342     
35343     getVerticalFourBoxColPositions : function(x, y, box)
35344     {
35345         var pos = [];
35346         
35347         if(box[0].size == 'xs'){
35348             
35349             pos.push({
35350                 x : x,
35351                 y : y
35352             });
35353
35354             pos.push({
35355                 x : x,
35356                 y : y + (this.unitHeight + this.gutter) * 1
35357             });
35358             
35359             pos.push({
35360                 x : x,
35361                 y : y + (this.unitHeight + this.gutter) * 2
35362             });
35363             
35364             pos.push({
35365                 x : x + (this.unitWidth + this.gutter) * 1,
35366                 y : y
35367             });
35368             
35369             return pos;
35370             
35371         }
35372         
35373         pos.push({
35374             x : x,
35375             y : y
35376         });
35377
35378         pos.push({
35379             x : x + (this.unitWidth + this.gutter) * 2,
35380             y : y
35381         });
35382
35383         pos.push({
35384             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35385             y : y + (this.unitHeight + this.gutter) * 1
35386         });
35387
35388         pos.push({
35389             x : x + (this.unitWidth + this.gutter) * 2,
35390             y : y + (this.unitWidth + this.gutter) * 2
35391         });
35392
35393         return pos;
35394         
35395     },
35396     
35397     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35398     {
35399         var pos = [];
35400         
35401         if(box[0].size == 'md-left'){
35402             pos.push({
35403                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35404                 y : minY
35405             });
35406             
35407             return pos;
35408         }
35409         
35410         if(box[0].size == 'md-right'){
35411             pos.push({
35412                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35413                 y : minY + (this.unitWidth + this.gutter) * 1
35414             });
35415             
35416             return pos;
35417         }
35418         
35419         var rand = Math.floor(Math.random() * (4 - box[0].y));
35420         
35421         pos.push({
35422             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35423             y : minY + (this.unitWidth + this.gutter) * rand
35424         });
35425         
35426         return pos;
35427         
35428     },
35429     
35430     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35431     {
35432         var pos = [];
35433         
35434         if(box[0].size == 'xs'){
35435             
35436             pos.push({
35437                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35438                 y : minY
35439             });
35440
35441             pos.push({
35442                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35443                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35444             });
35445             
35446             return pos;
35447             
35448         }
35449         
35450         pos.push({
35451             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35452             y : minY
35453         });
35454
35455         pos.push({
35456             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35457             y : minY + (this.unitWidth + this.gutter) * 2
35458         });
35459         
35460         return pos;
35461         
35462     },
35463     
35464     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35465     {
35466         var pos = [];
35467         
35468         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35469             
35470             pos.push({
35471                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35472                 y : minY
35473             });
35474
35475             pos.push({
35476                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35477                 y : minY + (this.unitWidth + this.gutter) * 1
35478             });
35479             
35480             pos.push({
35481                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35482                 y : minY + (this.unitWidth + this.gutter) * 2
35483             });
35484             
35485             return pos;
35486             
35487         }
35488         
35489         if(box[0].size == 'xs' && box[1].size == 'xs'){
35490             
35491             pos.push({
35492                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35493                 y : minY
35494             });
35495
35496             pos.push({
35497                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35498                 y : minY
35499             });
35500             
35501             pos.push({
35502                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35503                 y : minY + (this.unitWidth + this.gutter) * 1
35504             });
35505             
35506             return pos;
35507             
35508         }
35509         
35510         pos.push({
35511             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35512             y : minY
35513         });
35514
35515         pos.push({
35516             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35517             y : minY + (this.unitWidth + this.gutter) * 2
35518         });
35519
35520         pos.push({
35521             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35522             y : minY + (this.unitWidth + this.gutter) * 2
35523         });
35524             
35525         return pos;
35526         
35527     },
35528     
35529     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35530     {
35531         var pos = [];
35532         
35533         if(box[0].size == 'xs'){
35534             
35535             pos.push({
35536                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35537                 y : minY
35538             });
35539
35540             pos.push({
35541                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35542                 y : minY
35543             });
35544             
35545             pos.push({
35546                 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),
35547                 y : minY
35548             });
35549             
35550             pos.push({
35551                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35552                 y : minY + (this.unitWidth + this.gutter) * 1
35553             });
35554             
35555             return pos;
35556             
35557         }
35558         
35559         pos.push({
35560             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35561             y : minY
35562         });
35563         
35564         pos.push({
35565             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35566             y : minY + (this.unitWidth + this.gutter) * 2
35567         });
35568         
35569         pos.push({
35570             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35571             y : minY + (this.unitWidth + this.gutter) * 2
35572         });
35573         
35574         pos.push({
35575             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),
35576             y : minY + (this.unitWidth + this.gutter) * 2
35577         });
35578
35579         return pos;
35580         
35581     },
35582     
35583     /**
35584     * remove a Masonry Brick
35585     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35586     */
35587     removeBrick : function(brick_id)
35588     {
35589         if (!brick_id) {
35590             return;
35591         }
35592         
35593         for (var i = 0; i<this.bricks.length; i++) {
35594             if (this.bricks[i].id == brick_id) {
35595                 this.bricks.splice(i,1);
35596                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35597                 this.initial();
35598             }
35599         }
35600     },
35601     
35602     /**
35603     * adds a Masonry Brick
35604     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35605     */
35606     addBrick : function(cfg)
35607     {
35608         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35609         //this.register(cn);
35610         cn.parentId = this.id;
35611         cn.render(this.el);
35612         return cn;
35613     },
35614     
35615     /**
35616     * register a Masonry Brick
35617     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35618     */
35619     
35620     register : function(brick)
35621     {
35622         this.bricks.push(brick);
35623         brick.masonryId = this.id;
35624     },
35625     
35626     /**
35627     * clear all the Masonry Brick
35628     */
35629     clearAll : function()
35630     {
35631         this.bricks = [];
35632         //this.getChildContainer().dom.innerHTML = "";
35633         this.el.dom.innerHTML = '';
35634     },
35635     
35636     getSelected : function()
35637     {
35638         if (!this.selectedBrick) {
35639             return false;
35640         }
35641         
35642         return this.selectedBrick;
35643     }
35644 });
35645
35646 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35647     
35648     groups: {},
35649      /**
35650     * register a Masonry Layout
35651     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35652     */
35653     
35654     register : function(layout)
35655     {
35656         this.groups[layout.id] = layout;
35657     },
35658     /**
35659     * fetch a  Masonry Layout based on the masonry layout ID
35660     * @param {string} the masonry layout to add
35661     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35662     */
35663     
35664     get: function(layout_id) {
35665         if (typeof(this.groups[layout_id]) == 'undefined') {
35666             return false;
35667         }
35668         return this.groups[layout_id] ;
35669     }
35670     
35671     
35672     
35673 });
35674
35675  
35676
35677  /**
35678  *
35679  * This is based on 
35680  * http://masonry.desandro.com
35681  *
35682  * The idea is to render all the bricks based on vertical width...
35683  *
35684  * The original code extends 'outlayer' - we might need to use that....
35685  * 
35686  */
35687
35688
35689 /**
35690  * @class Roo.bootstrap.LayoutMasonryAuto
35691  * @extends Roo.bootstrap.Component
35692  * Bootstrap Layout Masonry class
35693  * 
35694  * @constructor
35695  * Create a new Element
35696  * @param {Object} config The config object
35697  */
35698
35699 Roo.bootstrap.LayoutMasonryAuto = function(config){
35700     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35701 };
35702
35703 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35704     
35705       /**
35706      * @cfg {Boolean} isFitWidth  - resize the width..
35707      */   
35708     isFitWidth : false,  // options..
35709     /**
35710      * @cfg {Boolean} isOriginLeft = left align?
35711      */   
35712     isOriginLeft : true,
35713     /**
35714      * @cfg {Boolean} isOriginTop = top align?
35715      */   
35716     isOriginTop : false,
35717     /**
35718      * @cfg {Boolean} isLayoutInstant = no animation?
35719      */   
35720     isLayoutInstant : false, // needed?
35721     /**
35722      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35723      */   
35724     isResizingContainer : true,
35725     /**
35726      * @cfg {Number} columnWidth  width of the columns 
35727      */   
35728     
35729     columnWidth : 0,
35730     
35731     /**
35732      * @cfg {Number} maxCols maximum number of columns
35733      */   
35734     
35735     maxCols: 0,
35736     /**
35737      * @cfg {Number} padHeight padding below box..
35738      */   
35739     
35740     padHeight : 10, 
35741     
35742     /**
35743      * @cfg {Boolean} isAutoInitial defalut true
35744      */   
35745     
35746     isAutoInitial : true, 
35747     
35748     // private?
35749     gutter : 0,
35750     
35751     containerWidth: 0,
35752     initialColumnWidth : 0,
35753     currentSize : null,
35754     
35755     colYs : null, // array.
35756     maxY : 0,
35757     padWidth: 10,
35758     
35759     
35760     tag: 'div',
35761     cls: '',
35762     bricks: null, //CompositeElement
35763     cols : 0, // array?
35764     // element : null, // wrapped now this.el
35765     _isLayoutInited : null, 
35766     
35767     
35768     getAutoCreate : function(){
35769         
35770         var cfg = {
35771             tag: this.tag,
35772             cls: 'blog-masonary-wrapper ' + this.cls,
35773             cn : {
35774                 cls : 'mas-boxes masonary'
35775             }
35776         };
35777         
35778         return cfg;
35779     },
35780     
35781     getChildContainer: function( )
35782     {
35783         if (this.boxesEl) {
35784             return this.boxesEl;
35785         }
35786         
35787         this.boxesEl = this.el.select('.mas-boxes').first();
35788         
35789         return this.boxesEl;
35790     },
35791     
35792     
35793     initEvents : function()
35794     {
35795         var _this = this;
35796         
35797         if(this.isAutoInitial){
35798             Roo.log('hook children rendered');
35799             this.on('childrenrendered', function() {
35800                 Roo.log('children rendered');
35801                 _this.initial();
35802             } ,this);
35803         }
35804         
35805     },
35806     
35807     initial : function()
35808     {
35809         this.reloadItems();
35810
35811         this.currentSize = this.el.getBox(true);
35812
35813         /// was window resize... - let's see if this works..
35814         Roo.EventManager.onWindowResize(this.resize, this); 
35815
35816         if(!this.isAutoInitial){
35817             this.layout();
35818             return;
35819         }
35820         
35821         this.layout.defer(500,this);
35822     },
35823     
35824     reloadItems: function()
35825     {
35826         this.bricks = this.el.select('.masonry-brick', true);
35827         
35828         this.bricks.each(function(b) {
35829             //Roo.log(b.getSize());
35830             if (!b.attr('originalwidth')) {
35831                 b.attr('originalwidth',  b.getSize().width);
35832             }
35833             
35834         });
35835         
35836         Roo.log(this.bricks.elements.length);
35837     },
35838     
35839     resize : function()
35840     {
35841         Roo.log('resize');
35842         var cs = this.el.getBox(true);
35843         
35844         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35845             Roo.log("no change in with or X");
35846             return;
35847         }
35848         this.currentSize = cs;
35849         this.layout();
35850     },
35851     
35852     layout : function()
35853     {
35854          Roo.log('layout');
35855         this._resetLayout();
35856         //this._manageStamps();
35857       
35858         // don't animate first layout
35859         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35860         this.layoutItems( isInstant );
35861       
35862         // flag for initalized
35863         this._isLayoutInited = true;
35864     },
35865     
35866     layoutItems : function( isInstant )
35867     {
35868         //var items = this._getItemsForLayout( this.items );
35869         // original code supports filtering layout items.. we just ignore it..
35870         
35871         this._layoutItems( this.bricks , isInstant );
35872       
35873         this._postLayout();
35874     },
35875     _layoutItems : function ( items , isInstant)
35876     {
35877        //this.fireEvent( 'layout', this, items );
35878     
35879
35880         if ( !items || !items.elements.length ) {
35881           // no items, emit event with empty array
35882             return;
35883         }
35884
35885         var queue = [];
35886         items.each(function(item) {
35887             Roo.log("layout item");
35888             Roo.log(item);
35889             // get x/y object from method
35890             var position = this._getItemLayoutPosition( item );
35891             // enqueue
35892             position.item = item;
35893             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35894             queue.push( position );
35895         }, this);
35896       
35897         this._processLayoutQueue( queue );
35898     },
35899     /** Sets position of item in DOM
35900     * @param {Element} item
35901     * @param {Number} x - horizontal position
35902     * @param {Number} y - vertical position
35903     * @param {Boolean} isInstant - disables transitions
35904     */
35905     _processLayoutQueue : function( queue )
35906     {
35907         for ( var i=0, len = queue.length; i < len; i++ ) {
35908             var obj = queue[i];
35909             obj.item.position('absolute');
35910             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35911         }
35912     },
35913       
35914     
35915     /**
35916     * Any logic you want to do after each layout,
35917     * i.e. size the container
35918     */
35919     _postLayout : function()
35920     {
35921         this.resizeContainer();
35922     },
35923     
35924     resizeContainer : function()
35925     {
35926         if ( !this.isResizingContainer ) {
35927             return;
35928         }
35929         var size = this._getContainerSize();
35930         if ( size ) {
35931             this.el.setSize(size.width,size.height);
35932             this.boxesEl.setSize(size.width,size.height);
35933         }
35934     },
35935     
35936     
35937     
35938     _resetLayout : function()
35939     {
35940         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35941         this.colWidth = this.el.getWidth();
35942         //this.gutter = this.el.getWidth(); 
35943         
35944         this.measureColumns();
35945
35946         // reset column Y
35947         var i = this.cols;
35948         this.colYs = [];
35949         while (i--) {
35950             this.colYs.push( 0 );
35951         }
35952     
35953         this.maxY = 0;
35954     },
35955
35956     measureColumns : function()
35957     {
35958         this.getContainerWidth();
35959       // if columnWidth is 0, default to outerWidth of first item
35960         if ( !this.columnWidth ) {
35961             var firstItem = this.bricks.first();
35962             Roo.log(firstItem);
35963             this.columnWidth  = this.containerWidth;
35964             if (firstItem && firstItem.attr('originalwidth') ) {
35965                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35966             }
35967             // columnWidth fall back to item of first element
35968             Roo.log("set column width?");
35969                         this.initialColumnWidth = this.columnWidth  ;
35970
35971             // if first elem has no width, default to size of container
35972             
35973         }
35974         
35975         
35976         if (this.initialColumnWidth) {
35977             this.columnWidth = this.initialColumnWidth;
35978         }
35979         
35980         
35981             
35982         // column width is fixed at the top - however if container width get's smaller we should
35983         // reduce it...
35984         
35985         // this bit calcs how man columns..
35986             
35987         var columnWidth = this.columnWidth += this.gutter;
35988       
35989         // calculate columns
35990         var containerWidth = this.containerWidth + this.gutter;
35991         
35992         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35993         // fix rounding errors, typically with gutters
35994         var excess = columnWidth - containerWidth % columnWidth;
35995         
35996         
35997         // if overshoot is less than a pixel, round up, otherwise floor it
35998         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35999         cols = Math[ mathMethod ]( cols );
36000         this.cols = Math.max( cols, 1 );
36001         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36002         
36003          // padding positioning..
36004         var totalColWidth = this.cols * this.columnWidth;
36005         var padavail = this.containerWidth - totalColWidth;
36006         // so for 2 columns - we need 3 'pads'
36007         
36008         var padNeeded = (1+this.cols) * this.padWidth;
36009         
36010         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36011         
36012         this.columnWidth += padExtra
36013         //this.padWidth = Math.floor(padavail /  ( this.cols));
36014         
36015         // adjust colum width so that padding is fixed??
36016         
36017         // we have 3 columns ... total = width * 3
36018         // we have X left over... that should be used by 
36019         
36020         //if (this.expandC) {
36021             
36022         //}
36023         
36024         
36025         
36026     },
36027     
36028     getContainerWidth : function()
36029     {
36030        /* // container is parent if fit width
36031         var container = this.isFitWidth ? this.element.parentNode : this.element;
36032         // check that this.size and size are there
36033         // IE8 triggers resize on body size change, so they might not be
36034         
36035         var size = getSize( container );  //FIXME
36036         this.containerWidth = size && size.innerWidth; //FIXME
36037         */
36038          
36039         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36040         
36041     },
36042     
36043     _getItemLayoutPosition : function( item )  // what is item?
36044     {
36045         // we resize the item to our columnWidth..
36046       
36047         item.setWidth(this.columnWidth);
36048         item.autoBoxAdjust  = false;
36049         
36050         var sz = item.getSize();
36051  
36052         // how many columns does this brick span
36053         var remainder = this.containerWidth % this.columnWidth;
36054         
36055         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36056         // round if off by 1 pixel, otherwise use ceil
36057         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36058         colSpan = Math.min( colSpan, this.cols );
36059         
36060         // normally this should be '1' as we dont' currently allow multi width columns..
36061         
36062         var colGroup = this._getColGroup( colSpan );
36063         // get the minimum Y value from the columns
36064         var minimumY = Math.min.apply( Math, colGroup );
36065         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36066         
36067         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36068          
36069         // position the brick
36070         var position = {
36071             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36072             y: this.currentSize.y + minimumY + this.padHeight
36073         };
36074         
36075         Roo.log(position);
36076         // apply setHeight to necessary columns
36077         var setHeight = minimumY + sz.height + this.padHeight;
36078         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36079         
36080         var setSpan = this.cols + 1 - colGroup.length;
36081         for ( var i = 0; i < setSpan; i++ ) {
36082           this.colYs[ shortColIndex + i ] = setHeight ;
36083         }
36084       
36085         return position;
36086     },
36087     
36088     /**
36089      * @param {Number} colSpan - number of columns the element spans
36090      * @returns {Array} colGroup
36091      */
36092     _getColGroup : function( colSpan )
36093     {
36094         if ( colSpan < 2 ) {
36095           // if brick spans only one column, use all the column Ys
36096           return this.colYs;
36097         }
36098       
36099         var colGroup = [];
36100         // how many different places could this brick fit horizontally
36101         var groupCount = this.cols + 1 - colSpan;
36102         // for each group potential horizontal position
36103         for ( var i = 0; i < groupCount; i++ ) {
36104           // make an array of colY values for that one group
36105           var groupColYs = this.colYs.slice( i, i + colSpan );
36106           // and get the max value of the array
36107           colGroup[i] = Math.max.apply( Math, groupColYs );
36108         }
36109         return colGroup;
36110     },
36111     /*
36112     _manageStamp : function( stamp )
36113     {
36114         var stampSize =  stamp.getSize();
36115         var offset = stamp.getBox();
36116         // get the columns that this stamp affects
36117         var firstX = this.isOriginLeft ? offset.x : offset.right;
36118         var lastX = firstX + stampSize.width;
36119         var firstCol = Math.floor( firstX / this.columnWidth );
36120         firstCol = Math.max( 0, firstCol );
36121         
36122         var lastCol = Math.floor( lastX / this.columnWidth );
36123         // lastCol should not go over if multiple of columnWidth #425
36124         lastCol -= lastX % this.columnWidth ? 0 : 1;
36125         lastCol = Math.min( this.cols - 1, lastCol );
36126         
36127         // set colYs to bottom of the stamp
36128         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36129             stampSize.height;
36130             
36131         for ( var i = firstCol; i <= lastCol; i++ ) {
36132           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36133         }
36134     },
36135     */
36136     
36137     _getContainerSize : function()
36138     {
36139         this.maxY = Math.max.apply( Math, this.colYs );
36140         var size = {
36141             height: this.maxY
36142         };
36143       
36144         if ( this.isFitWidth ) {
36145             size.width = this._getContainerFitWidth();
36146         }
36147       
36148         return size;
36149     },
36150     
36151     _getContainerFitWidth : function()
36152     {
36153         var unusedCols = 0;
36154         // count unused columns
36155         var i = this.cols;
36156         while ( --i ) {
36157           if ( this.colYs[i] !== 0 ) {
36158             break;
36159           }
36160           unusedCols++;
36161         }
36162         // fit container to columns that have been used
36163         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36164     },
36165     
36166     needsResizeLayout : function()
36167     {
36168         var previousWidth = this.containerWidth;
36169         this.getContainerWidth();
36170         return previousWidth !== this.containerWidth;
36171     }
36172  
36173 });
36174
36175  
36176
36177  /*
36178  * - LGPL
36179  *
36180  * element
36181  * 
36182  */
36183
36184 /**
36185  * @class Roo.bootstrap.MasonryBrick
36186  * @extends Roo.bootstrap.Component
36187  * Bootstrap MasonryBrick class
36188  * 
36189  * @constructor
36190  * Create a new MasonryBrick
36191  * @param {Object} config The config object
36192  */
36193
36194 Roo.bootstrap.MasonryBrick = function(config){
36195     
36196     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36197     
36198     Roo.bootstrap.MasonryBrick.register(this);
36199     
36200     this.addEvents({
36201         // raw events
36202         /**
36203          * @event click
36204          * When a MasonryBrick is clcik
36205          * @param {Roo.bootstrap.MasonryBrick} this
36206          * @param {Roo.EventObject} e
36207          */
36208         "click" : true
36209     });
36210 };
36211
36212 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36213     
36214     /**
36215      * @cfg {String} title
36216      */   
36217     title : '',
36218     /**
36219      * @cfg {String} html
36220      */   
36221     html : '',
36222     /**
36223      * @cfg {String} bgimage
36224      */   
36225     bgimage : '',
36226     /**
36227      * @cfg {String} videourl
36228      */   
36229     videourl : '',
36230     /**
36231      * @cfg {String} cls
36232      */   
36233     cls : '',
36234     /**
36235      * @cfg {String} href
36236      */   
36237     href : '',
36238     /**
36239      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36240      */   
36241     size : 'xs',
36242     
36243     /**
36244      * @cfg {String} placetitle (center|bottom)
36245      */   
36246     placetitle : '',
36247     
36248     /**
36249      * @cfg {Boolean} isFitContainer defalut true
36250      */   
36251     isFitContainer : true, 
36252     
36253     /**
36254      * @cfg {Boolean} preventDefault defalut false
36255      */   
36256     preventDefault : false, 
36257     
36258     /**
36259      * @cfg {Boolean} inverse defalut false
36260      */   
36261     maskInverse : false, 
36262     
36263     getAutoCreate : function()
36264     {
36265         if(!this.isFitContainer){
36266             return this.getSplitAutoCreate();
36267         }
36268         
36269         var cls = 'masonry-brick masonry-brick-full';
36270         
36271         if(this.href.length){
36272             cls += ' masonry-brick-link';
36273         }
36274         
36275         if(this.bgimage.length){
36276             cls += ' masonry-brick-image';
36277         }
36278         
36279         if(this.maskInverse){
36280             cls += ' mask-inverse';
36281         }
36282         
36283         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36284             cls += ' enable-mask';
36285         }
36286         
36287         if(this.size){
36288             cls += ' masonry-' + this.size + '-brick';
36289         }
36290         
36291         if(this.placetitle.length){
36292             
36293             switch (this.placetitle) {
36294                 case 'center' :
36295                     cls += ' masonry-center-title';
36296                     break;
36297                 case 'bottom' :
36298                     cls += ' masonry-bottom-title';
36299                     break;
36300                 default:
36301                     break;
36302             }
36303             
36304         } else {
36305             if(!this.html.length && !this.bgimage.length){
36306                 cls += ' masonry-center-title';
36307             }
36308
36309             if(!this.html.length && this.bgimage.length){
36310                 cls += ' masonry-bottom-title';
36311             }
36312         }
36313         
36314         if(this.cls){
36315             cls += ' ' + this.cls;
36316         }
36317         
36318         var cfg = {
36319             tag: (this.href.length) ? 'a' : 'div',
36320             cls: cls,
36321             cn: [
36322                 {
36323                     tag: 'div',
36324                     cls: 'masonry-brick-mask'
36325                 },
36326                 {
36327                     tag: 'div',
36328                     cls: 'masonry-brick-paragraph',
36329                     cn: []
36330                 }
36331             ]
36332         };
36333         
36334         if(this.href.length){
36335             cfg.href = this.href;
36336         }
36337         
36338         var cn = cfg.cn[1].cn;
36339         
36340         if(this.title.length){
36341             cn.push({
36342                 tag: 'h4',
36343                 cls: 'masonry-brick-title',
36344                 html: this.title
36345             });
36346         }
36347         
36348         if(this.html.length){
36349             cn.push({
36350                 tag: 'p',
36351                 cls: 'masonry-brick-text',
36352                 html: this.html
36353             });
36354         }
36355         
36356         if (!this.title.length && !this.html.length) {
36357             cfg.cn[1].cls += ' hide';
36358         }
36359         
36360         if(this.bgimage.length){
36361             cfg.cn.push({
36362                 tag: 'img',
36363                 cls: 'masonry-brick-image-view',
36364                 src: this.bgimage
36365             });
36366         }
36367         
36368         if(this.videourl.length){
36369             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36370             // youtube support only?
36371             cfg.cn.push({
36372                 tag: 'iframe',
36373                 cls: 'masonry-brick-image-view',
36374                 src: vurl,
36375                 frameborder : 0,
36376                 allowfullscreen : true
36377             });
36378         }
36379         
36380         return cfg;
36381         
36382     },
36383     
36384     getSplitAutoCreate : function()
36385     {
36386         var cls = 'masonry-brick masonry-brick-split';
36387         
36388         if(this.href.length){
36389             cls += ' masonry-brick-link';
36390         }
36391         
36392         if(this.bgimage.length){
36393             cls += ' masonry-brick-image';
36394         }
36395         
36396         if(this.size){
36397             cls += ' masonry-' + this.size + '-brick';
36398         }
36399         
36400         switch (this.placetitle) {
36401             case 'center' :
36402                 cls += ' masonry-center-title';
36403                 break;
36404             case 'bottom' :
36405                 cls += ' masonry-bottom-title';
36406                 break;
36407             default:
36408                 if(!this.bgimage.length){
36409                     cls += ' masonry-center-title';
36410                 }
36411
36412                 if(this.bgimage.length){
36413                     cls += ' masonry-bottom-title';
36414                 }
36415                 break;
36416         }
36417         
36418         if(this.cls){
36419             cls += ' ' + this.cls;
36420         }
36421         
36422         var cfg = {
36423             tag: (this.href.length) ? 'a' : 'div',
36424             cls: cls,
36425             cn: [
36426                 {
36427                     tag: 'div',
36428                     cls: 'masonry-brick-split-head',
36429                     cn: [
36430                         {
36431                             tag: 'div',
36432                             cls: 'masonry-brick-paragraph',
36433                             cn: []
36434                         }
36435                     ]
36436                 },
36437                 {
36438                     tag: 'div',
36439                     cls: 'masonry-brick-split-body',
36440                     cn: []
36441                 }
36442             ]
36443         };
36444         
36445         if(this.href.length){
36446             cfg.href = this.href;
36447         }
36448         
36449         if(this.title.length){
36450             cfg.cn[0].cn[0].cn.push({
36451                 tag: 'h4',
36452                 cls: 'masonry-brick-title',
36453                 html: this.title
36454             });
36455         }
36456         
36457         if(this.html.length){
36458             cfg.cn[1].cn.push({
36459                 tag: 'p',
36460                 cls: 'masonry-brick-text',
36461                 html: this.html
36462             });
36463         }
36464
36465         if(this.bgimage.length){
36466             cfg.cn[0].cn.push({
36467                 tag: 'img',
36468                 cls: 'masonry-brick-image-view',
36469                 src: this.bgimage
36470             });
36471         }
36472         
36473         if(this.videourl.length){
36474             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36475             // youtube support only?
36476             cfg.cn[0].cn.cn.push({
36477                 tag: 'iframe',
36478                 cls: 'masonry-brick-image-view',
36479                 src: vurl,
36480                 frameborder : 0,
36481                 allowfullscreen : true
36482             });
36483         }
36484         
36485         return cfg;
36486     },
36487     
36488     initEvents: function() 
36489     {
36490         switch (this.size) {
36491             case 'xs' :
36492                 this.x = 1;
36493                 this.y = 1;
36494                 break;
36495             case 'sm' :
36496                 this.x = 2;
36497                 this.y = 2;
36498                 break;
36499             case 'md' :
36500             case 'md-left' :
36501             case 'md-right' :
36502                 this.x = 3;
36503                 this.y = 3;
36504                 break;
36505             case 'tall' :
36506                 this.x = 2;
36507                 this.y = 3;
36508                 break;
36509             case 'wide' :
36510                 this.x = 3;
36511                 this.y = 2;
36512                 break;
36513             case 'wide-thin' :
36514                 this.x = 3;
36515                 this.y = 1;
36516                 break;
36517                         
36518             default :
36519                 break;
36520         }
36521         
36522         if(Roo.isTouch){
36523             this.el.on('touchstart', this.onTouchStart, this);
36524             this.el.on('touchmove', this.onTouchMove, this);
36525             this.el.on('touchend', this.onTouchEnd, this);
36526             this.el.on('contextmenu', this.onContextMenu, this);
36527         } else {
36528             this.el.on('mouseenter'  ,this.enter, this);
36529             this.el.on('mouseleave', this.leave, this);
36530             this.el.on('click', this.onClick, this);
36531         }
36532         
36533         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36534             this.parent().bricks.push(this);   
36535         }
36536         
36537     },
36538     
36539     onClick: function(e, el)
36540     {
36541         var time = this.endTimer - this.startTimer;
36542         // Roo.log(e.preventDefault());
36543         if(Roo.isTouch){
36544             if(time > 1000){
36545                 e.preventDefault();
36546                 return;
36547             }
36548         }
36549         
36550         if(!this.preventDefault){
36551             return;
36552         }
36553         
36554         e.preventDefault();
36555         
36556         if (this.activeClass != '') {
36557             this.selectBrick();
36558         }
36559         
36560         this.fireEvent('click', this, e);
36561     },
36562     
36563     enter: function(e, el)
36564     {
36565         e.preventDefault();
36566         
36567         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36568             return;
36569         }
36570         
36571         if(this.bgimage.length && this.html.length){
36572             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36573         }
36574     },
36575     
36576     leave: function(e, el)
36577     {
36578         e.preventDefault();
36579         
36580         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36581             return;
36582         }
36583         
36584         if(this.bgimage.length && this.html.length){
36585             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36586         }
36587     },
36588     
36589     onTouchStart: function(e, el)
36590     {
36591 //        e.preventDefault();
36592         
36593         this.touchmoved = false;
36594         
36595         if(!this.isFitContainer){
36596             return;
36597         }
36598         
36599         if(!this.bgimage.length || !this.html.length){
36600             return;
36601         }
36602         
36603         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36604         
36605         this.timer = new Date().getTime();
36606         
36607     },
36608     
36609     onTouchMove: function(e, el)
36610     {
36611         this.touchmoved = true;
36612     },
36613     
36614     onContextMenu : function(e,el)
36615     {
36616         e.preventDefault();
36617         e.stopPropagation();
36618         return false;
36619     },
36620     
36621     onTouchEnd: function(e, el)
36622     {
36623 //        e.preventDefault();
36624         
36625         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36626         
36627             this.leave(e,el);
36628             
36629             return;
36630         }
36631         
36632         if(!this.bgimage.length || !this.html.length){
36633             
36634             if(this.href.length){
36635                 window.location.href = this.href;
36636             }
36637             
36638             return;
36639         }
36640         
36641         if(!this.isFitContainer){
36642             return;
36643         }
36644         
36645         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36646         
36647         window.location.href = this.href;
36648     },
36649     
36650     //selection on single brick only
36651     selectBrick : function() {
36652         
36653         if (!this.parentId) {
36654             return;
36655         }
36656         
36657         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36658         var index = m.selectedBrick.indexOf(this.id);
36659         
36660         if ( index > -1) {
36661             m.selectedBrick.splice(index,1);
36662             this.el.removeClass(this.activeClass);
36663             return;
36664         }
36665         
36666         for(var i = 0; i < m.selectedBrick.length; i++) {
36667             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36668             b.el.removeClass(b.activeClass);
36669         }
36670         
36671         m.selectedBrick = [];
36672         
36673         m.selectedBrick.push(this.id);
36674         this.el.addClass(this.activeClass);
36675         return;
36676     },
36677     
36678     isSelected : function(){
36679         return this.el.hasClass(this.activeClass);
36680         
36681     }
36682 });
36683
36684 Roo.apply(Roo.bootstrap.MasonryBrick, {
36685     
36686     //groups: {},
36687     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36688      /**
36689     * register a Masonry Brick
36690     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36691     */
36692     
36693     register : function(brick)
36694     {
36695         //this.groups[brick.id] = brick;
36696         this.groups.add(brick.id, brick);
36697     },
36698     /**
36699     * fetch a  masonry brick based on the masonry brick ID
36700     * @param {string} the masonry brick to add
36701     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36702     */
36703     
36704     get: function(brick_id) 
36705     {
36706         // if (typeof(this.groups[brick_id]) == 'undefined') {
36707         //     return false;
36708         // }
36709         // return this.groups[brick_id] ;
36710         
36711         if(this.groups.key(brick_id)) {
36712             return this.groups.key(brick_id);
36713         }
36714         
36715         return false;
36716     }
36717     
36718     
36719     
36720 });
36721
36722  /*
36723  * - LGPL
36724  *
36725  * element
36726  * 
36727  */
36728
36729 /**
36730  * @class Roo.bootstrap.Brick
36731  * @extends Roo.bootstrap.Component
36732  * Bootstrap Brick class
36733  * 
36734  * @constructor
36735  * Create a new Brick
36736  * @param {Object} config The config object
36737  */
36738
36739 Roo.bootstrap.Brick = function(config){
36740     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36741     
36742     this.addEvents({
36743         // raw events
36744         /**
36745          * @event click
36746          * When a Brick is click
36747          * @param {Roo.bootstrap.Brick} this
36748          * @param {Roo.EventObject} e
36749          */
36750         "click" : true
36751     });
36752 };
36753
36754 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36755     
36756     /**
36757      * @cfg {String} title
36758      */   
36759     title : '',
36760     /**
36761      * @cfg {String} html
36762      */   
36763     html : '',
36764     /**
36765      * @cfg {String} bgimage
36766      */   
36767     bgimage : '',
36768     /**
36769      * @cfg {String} cls
36770      */   
36771     cls : '',
36772     /**
36773      * @cfg {String} href
36774      */   
36775     href : '',
36776     /**
36777      * @cfg {String} video
36778      */   
36779     video : '',
36780     /**
36781      * @cfg {Boolean} square
36782      */   
36783     square : true,
36784     
36785     getAutoCreate : function()
36786     {
36787         var cls = 'roo-brick';
36788         
36789         if(this.href.length){
36790             cls += ' roo-brick-link';
36791         }
36792         
36793         if(this.bgimage.length){
36794             cls += ' roo-brick-image';
36795         }
36796         
36797         if(!this.html.length && !this.bgimage.length){
36798             cls += ' roo-brick-center-title';
36799         }
36800         
36801         if(!this.html.length && this.bgimage.length){
36802             cls += ' roo-brick-bottom-title';
36803         }
36804         
36805         if(this.cls){
36806             cls += ' ' + this.cls;
36807         }
36808         
36809         var cfg = {
36810             tag: (this.href.length) ? 'a' : 'div',
36811             cls: cls,
36812             cn: [
36813                 {
36814                     tag: 'div',
36815                     cls: 'roo-brick-paragraph',
36816                     cn: []
36817                 }
36818             ]
36819         };
36820         
36821         if(this.href.length){
36822             cfg.href = this.href;
36823         }
36824         
36825         var cn = cfg.cn[0].cn;
36826         
36827         if(this.title.length){
36828             cn.push({
36829                 tag: 'h4',
36830                 cls: 'roo-brick-title',
36831                 html: this.title
36832             });
36833         }
36834         
36835         if(this.html.length){
36836             cn.push({
36837                 tag: 'p',
36838                 cls: 'roo-brick-text',
36839                 html: this.html
36840             });
36841         } else {
36842             cn.cls += ' hide';
36843         }
36844         
36845         if(this.bgimage.length){
36846             cfg.cn.push({
36847                 tag: 'img',
36848                 cls: 'roo-brick-image-view',
36849                 src: this.bgimage
36850             });
36851         }
36852         
36853         return cfg;
36854     },
36855     
36856     initEvents: function() 
36857     {
36858         if(this.title.length || this.html.length){
36859             this.el.on('mouseenter'  ,this.enter, this);
36860             this.el.on('mouseleave', this.leave, this);
36861         }
36862         
36863         Roo.EventManager.onWindowResize(this.resize, this); 
36864         
36865         if(this.bgimage.length){
36866             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36867             this.imageEl.on('load', this.onImageLoad, this);
36868             return;
36869         }
36870         
36871         this.resize();
36872     },
36873     
36874     onImageLoad : function()
36875     {
36876         this.resize();
36877     },
36878     
36879     resize : function()
36880     {
36881         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36882         
36883         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36884         
36885         if(this.bgimage.length){
36886             var image = this.el.select('.roo-brick-image-view', true).first();
36887             
36888             image.setWidth(paragraph.getWidth());
36889             
36890             if(this.square){
36891                 image.setHeight(paragraph.getWidth());
36892             }
36893             
36894             this.el.setHeight(image.getHeight());
36895             paragraph.setHeight(image.getHeight());
36896             
36897         }
36898         
36899     },
36900     
36901     enter: function(e, el)
36902     {
36903         e.preventDefault();
36904         
36905         if(this.bgimage.length){
36906             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36907             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36908         }
36909     },
36910     
36911     leave: function(e, el)
36912     {
36913         e.preventDefault();
36914         
36915         if(this.bgimage.length){
36916             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36917             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36918         }
36919     }
36920     
36921 });
36922
36923  
36924
36925  /*
36926  * - LGPL
36927  *
36928  * Number field 
36929  */
36930
36931 /**
36932  * @class Roo.bootstrap.NumberField
36933  * @extends Roo.bootstrap.Input
36934  * Bootstrap NumberField class
36935  * 
36936  * 
36937  * 
36938  * 
36939  * @constructor
36940  * Create a new NumberField
36941  * @param {Object} config The config object
36942  */
36943
36944 Roo.bootstrap.NumberField = function(config){
36945     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36946 };
36947
36948 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36949     
36950     /**
36951      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36952      */
36953     allowDecimals : true,
36954     /**
36955      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36956      */
36957     decimalSeparator : ".",
36958     /**
36959      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36960      */
36961     decimalPrecision : 2,
36962     /**
36963      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36964      */
36965     allowNegative : true,
36966     
36967     /**
36968      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36969      */
36970     allowZero: true,
36971     /**
36972      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36973      */
36974     minValue : Number.NEGATIVE_INFINITY,
36975     /**
36976      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36977      */
36978     maxValue : Number.MAX_VALUE,
36979     /**
36980      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36981      */
36982     minText : "The minimum value for this field is {0}",
36983     /**
36984      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36985      */
36986     maxText : "The maximum value for this field is {0}",
36987     /**
36988      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36989      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36990      */
36991     nanText : "{0} is not a valid number",
36992     /**
36993      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36994      */
36995     thousandsDelimiter : false,
36996     /**
36997      * @cfg {String} valueAlign alignment of value
36998      */
36999     valueAlign : "left",
37000
37001     getAutoCreate : function()
37002     {
37003         var hiddenInput = {
37004             tag: 'input',
37005             type: 'hidden',
37006             id: Roo.id(),
37007             cls: 'hidden-number-input'
37008         };
37009         
37010         if (this.name) {
37011             hiddenInput.name = this.name;
37012         }
37013         
37014         this.name = '';
37015         
37016         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37017         
37018         this.name = hiddenInput.name;
37019         
37020         if(cfg.cn.length > 0) {
37021             cfg.cn.push(hiddenInput);
37022         }
37023         
37024         return cfg;
37025     },
37026
37027     // private
37028     initEvents : function()
37029     {   
37030         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37031         
37032         var allowed = "0123456789";
37033         
37034         if(this.allowDecimals){
37035             allowed += this.decimalSeparator;
37036         }
37037         
37038         if(this.allowNegative){
37039             allowed += "-";
37040         }
37041         
37042         if(this.thousandsDelimiter) {
37043             allowed += ",";
37044         }
37045         
37046         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37047         
37048         var keyPress = function(e){
37049             
37050             var k = e.getKey();
37051             
37052             var c = e.getCharCode();
37053             
37054             if(
37055                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37056                     allowed.indexOf(String.fromCharCode(c)) === -1
37057             ){
37058                 e.stopEvent();
37059                 return;
37060             }
37061             
37062             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37063                 return;
37064             }
37065             
37066             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37067                 e.stopEvent();
37068             }
37069         };
37070         
37071         this.el.on("keypress", keyPress, this);
37072     },
37073     
37074     validateValue : function(value)
37075     {
37076         
37077         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37078             return false;
37079         }
37080         
37081         var num = this.parseValue(value);
37082         
37083         if(isNaN(num)){
37084             this.markInvalid(String.format(this.nanText, value));
37085             return false;
37086         }
37087         
37088         if(num < this.minValue){
37089             this.markInvalid(String.format(this.minText, this.minValue));
37090             return false;
37091         }
37092         
37093         if(num > this.maxValue){
37094             this.markInvalid(String.format(this.maxText, this.maxValue));
37095             return false;
37096         }
37097         
37098         return true;
37099     },
37100
37101     getValue : function()
37102     {
37103         var v = this.hiddenEl().getValue();
37104         
37105         return this.fixPrecision(this.parseValue(v));
37106     },
37107
37108     parseValue : function(value)
37109     {
37110         if(this.thousandsDelimiter) {
37111             value += "";
37112             r = new RegExp(",", "g");
37113             value = value.replace(r, "");
37114         }
37115         
37116         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37117         return isNaN(value) ? '' : value;
37118     },
37119
37120     fixPrecision : function(value)
37121     {
37122         if(this.thousandsDelimiter) {
37123             value += "";
37124             r = new RegExp(",", "g");
37125             value = value.replace(r, "");
37126         }
37127         
37128         var nan = isNaN(value);
37129         
37130         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37131             return nan ? '' : value;
37132         }
37133         return parseFloat(value).toFixed(this.decimalPrecision);
37134     },
37135
37136     setValue : function(v)
37137     {
37138         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37139         
37140         this.value = v;
37141         
37142         if(this.rendered){
37143             
37144             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37145             
37146             this.inputEl().dom.value = (v == '') ? '' :
37147                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37148             
37149             if(!this.allowZero && v === '0') {
37150                 this.hiddenEl().dom.value = '';
37151                 this.inputEl().dom.value = '';
37152             }
37153             
37154             this.validate();
37155         }
37156     },
37157
37158     decimalPrecisionFcn : function(v)
37159     {
37160         return Math.floor(v);
37161     },
37162
37163     beforeBlur : function()
37164     {
37165         var v = this.parseValue(this.getRawValue());
37166         
37167         if(v || v === 0 || v === ''){
37168             this.setValue(v);
37169         }
37170     },
37171     
37172     hiddenEl : function()
37173     {
37174         return this.el.select('input.hidden-number-input',true).first();
37175     }
37176     
37177 });
37178
37179  
37180
37181 /*
37182 * Licence: LGPL
37183 */
37184
37185 /**
37186  * @class Roo.bootstrap.DocumentSlider
37187  * @extends Roo.bootstrap.Component
37188  * Bootstrap DocumentSlider class
37189  * 
37190  * @constructor
37191  * Create a new DocumentViewer
37192  * @param {Object} config The config object
37193  */
37194
37195 Roo.bootstrap.DocumentSlider = function(config){
37196     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37197     
37198     this.files = [];
37199     
37200     this.addEvents({
37201         /**
37202          * @event initial
37203          * Fire after initEvent
37204          * @param {Roo.bootstrap.DocumentSlider} this
37205          */
37206         "initial" : true,
37207         /**
37208          * @event update
37209          * Fire after update
37210          * @param {Roo.bootstrap.DocumentSlider} this
37211          */
37212         "update" : true,
37213         /**
37214          * @event click
37215          * Fire after click
37216          * @param {Roo.bootstrap.DocumentSlider} this
37217          */
37218         "click" : true
37219     });
37220 };
37221
37222 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37223     
37224     files : false,
37225     
37226     indicator : 0,
37227     
37228     getAutoCreate : function()
37229     {
37230         var cfg = {
37231             tag : 'div',
37232             cls : 'roo-document-slider',
37233             cn : [
37234                 {
37235                     tag : 'div',
37236                     cls : 'roo-document-slider-header',
37237                     cn : [
37238                         {
37239                             tag : 'div',
37240                             cls : 'roo-document-slider-header-title'
37241                         }
37242                     ]
37243                 },
37244                 {
37245                     tag : 'div',
37246                     cls : 'roo-document-slider-body',
37247                     cn : [
37248                         {
37249                             tag : 'div',
37250                             cls : 'roo-document-slider-prev',
37251                             cn : [
37252                                 {
37253                                     tag : 'i',
37254                                     cls : 'fa fa-chevron-left'
37255                                 }
37256                             ]
37257                         },
37258                         {
37259                             tag : 'div',
37260                             cls : 'roo-document-slider-thumb',
37261                             cn : [
37262                                 {
37263                                     tag : 'img',
37264                                     cls : 'roo-document-slider-image'
37265                                 }
37266                             ]
37267                         },
37268                         {
37269                             tag : 'div',
37270                             cls : 'roo-document-slider-next',
37271                             cn : [
37272                                 {
37273                                     tag : 'i',
37274                                     cls : 'fa fa-chevron-right'
37275                                 }
37276                             ]
37277                         }
37278                     ]
37279                 }
37280             ]
37281         };
37282         
37283         return cfg;
37284     },
37285     
37286     initEvents : function()
37287     {
37288         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37289         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37290         
37291         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37292         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37293         
37294         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37295         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37296         
37297         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37298         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37299         
37300         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37301         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37302         
37303         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37304         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37305         
37306         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37307         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37308         
37309         this.thumbEl.on('click', this.onClick, this);
37310         
37311         this.prevIndicator.on('click', this.prev, this);
37312         
37313         this.nextIndicator.on('click', this.next, this);
37314         
37315     },
37316     
37317     initial : function()
37318     {
37319         if(this.files.length){
37320             this.indicator = 1;
37321             this.update()
37322         }
37323         
37324         this.fireEvent('initial', this);
37325     },
37326     
37327     update : function()
37328     {
37329         this.imageEl.attr('src', this.files[this.indicator - 1]);
37330         
37331         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37332         
37333         this.prevIndicator.show();
37334         
37335         if(this.indicator == 1){
37336             this.prevIndicator.hide();
37337         }
37338         
37339         this.nextIndicator.show();
37340         
37341         if(this.indicator == this.files.length){
37342             this.nextIndicator.hide();
37343         }
37344         
37345         this.thumbEl.scrollTo('top');
37346         
37347         this.fireEvent('update', this);
37348     },
37349     
37350     onClick : function(e)
37351     {
37352         e.preventDefault();
37353         
37354         this.fireEvent('click', this);
37355     },
37356     
37357     prev : function(e)
37358     {
37359         e.preventDefault();
37360         
37361         this.indicator = Math.max(1, this.indicator - 1);
37362         
37363         this.update();
37364     },
37365     
37366     next : function(e)
37367     {
37368         e.preventDefault();
37369         
37370         this.indicator = Math.min(this.files.length, this.indicator + 1);
37371         
37372         this.update();
37373     }
37374 });
37375 /*
37376  * - LGPL
37377  *
37378  * RadioSet
37379  *
37380  *
37381  */
37382
37383 /**
37384  * @class Roo.bootstrap.RadioSet
37385  * @extends Roo.bootstrap.Input
37386  * Bootstrap RadioSet class
37387  * @cfg {String} indicatorpos (left|right) default left
37388  * @cfg {Boolean} inline (true|false) inline the element (default true)
37389  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37390  * @constructor
37391  * Create a new RadioSet
37392  * @param {Object} config The config object
37393  */
37394
37395 Roo.bootstrap.RadioSet = function(config){
37396     
37397     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37398     
37399     this.radioes = [];
37400     
37401     Roo.bootstrap.RadioSet.register(this);
37402     
37403     this.addEvents({
37404         /**
37405         * @event check
37406         * Fires when the element is checked or unchecked.
37407         * @param {Roo.bootstrap.RadioSet} this This radio
37408         * @param {Roo.bootstrap.Radio} item The checked item
37409         */
37410        check : true,
37411        /**
37412         * @event click
37413         * Fires when the element is click.
37414         * @param {Roo.bootstrap.RadioSet} this This radio set
37415         * @param {Roo.bootstrap.Radio} item The checked item
37416         * @param {Roo.EventObject} e The event object
37417         */
37418        click : true
37419     });
37420     
37421 };
37422
37423 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37424
37425     radioes : false,
37426     
37427     inline : true,
37428     
37429     weight : '',
37430     
37431     indicatorpos : 'left',
37432     
37433     getAutoCreate : function()
37434     {
37435         var label = {
37436             tag : 'label',
37437             cls : 'roo-radio-set-label',
37438             cn : [
37439                 {
37440                     tag : 'span',
37441                     html : this.fieldLabel
37442                 }
37443             ]
37444         };
37445         if (Roo.bootstrap.version == 3) {
37446             
37447             
37448             if(this.indicatorpos == 'left'){
37449                 label.cn.unshift({
37450                     tag : 'i',
37451                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37452                     tooltip : 'This field is required'
37453                 });
37454             } else {
37455                 label.cn.push({
37456                     tag : 'i',
37457                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37458                     tooltip : 'This field is required'
37459                 });
37460             }
37461         }
37462         var items = {
37463             tag : 'div',
37464             cls : 'roo-radio-set-items'
37465         };
37466         
37467         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37468         
37469         if (align === 'left' && this.fieldLabel.length) {
37470             
37471             items = {
37472                 cls : "roo-radio-set-right", 
37473                 cn: [
37474                     items
37475                 ]
37476             };
37477             
37478             if(this.labelWidth > 12){
37479                 label.style = "width: " + this.labelWidth + 'px';
37480             }
37481             
37482             if(this.labelWidth < 13 && this.labelmd == 0){
37483                 this.labelmd = this.labelWidth;
37484             }
37485             
37486             if(this.labellg > 0){
37487                 label.cls += ' col-lg-' + this.labellg;
37488                 items.cls += ' col-lg-' + (12 - this.labellg);
37489             }
37490             
37491             if(this.labelmd > 0){
37492                 label.cls += ' col-md-' + this.labelmd;
37493                 items.cls += ' col-md-' + (12 - this.labelmd);
37494             }
37495             
37496             if(this.labelsm > 0){
37497                 label.cls += ' col-sm-' + this.labelsm;
37498                 items.cls += ' col-sm-' + (12 - this.labelsm);
37499             }
37500             
37501             if(this.labelxs > 0){
37502                 label.cls += ' col-xs-' + this.labelxs;
37503                 items.cls += ' col-xs-' + (12 - this.labelxs);
37504             }
37505         }
37506         
37507         var cfg = {
37508             tag : 'div',
37509             cls : 'roo-radio-set',
37510             cn : [
37511                 {
37512                     tag : 'input',
37513                     cls : 'roo-radio-set-input',
37514                     type : 'hidden',
37515                     name : this.name,
37516                     value : this.value ? this.value :  ''
37517                 },
37518                 label,
37519                 items
37520             ]
37521         };
37522         
37523         if(this.weight.length){
37524             cfg.cls += ' roo-radio-' + this.weight;
37525         }
37526         
37527         if(this.inline) {
37528             cfg.cls += ' roo-radio-set-inline';
37529         }
37530         
37531         var settings=this;
37532         ['xs','sm','md','lg'].map(function(size){
37533             if (settings[size]) {
37534                 cfg.cls += ' col-' + size + '-' + settings[size];
37535             }
37536         });
37537         
37538         return cfg;
37539         
37540     },
37541
37542     initEvents : function()
37543     {
37544         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37545         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37546         
37547         if(!this.fieldLabel.length){
37548             this.labelEl.hide();
37549         }
37550         
37551         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37552         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37553         
37554         this.indicator = this.indicatorEl();
37555         
37556         if(this.indicator){
37557             this.indicator.addClass('invisible');
37558         }
37559         
37560         this.originalValue = this.getValue();
37561         
37562     },
37563     
37564     inputEl: function ()
37565     {
37566         return this.el.select('.roo-radio-set-input', true).first();
37567     },
37568     
37569     getChildContainer : function()
37570     {
37571         return this.itemsEl;
37572     },
37573     
37574     register : function(item)
37575     {
37576         this.radioes.push(item);
37577         
37578     },
37579     
37580     validate : function()
37581     {   
37582         if(this.getVisibilityEl().hasClass('hidden')){
37583             return true;
37584         }
37585         
37586         var valid = false;
37587         
37588         Roo.each(this.radioes, function(i){
37589             if(!i.checked){
37590                 return;
37591             }
37592             
37593             valid = true;
37594             return false;
37595         });
37596         
37597         if(this.allowBlank) {
37598             return true;
37599         }
37600         
37601         if(this.disabled || valid){
37602             this.markValid();
37603             return true;
37604         }
37605         
37606         this.markInvalid();
37607         return false;
37608         
37609     },
37610     
37611     markValid : function()
37612     {
37613         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37614             this.indicatorEl().removeClass('visible');
37615             this.indicatorEl().addClass('invisible');
37616         }
37617         
37618         
37619         if (Roo.bootstrap.version == 3) {
37620             this.el.removeClass([this.invalidClass, this.validClass]);
37621             this.el.addClass(this.validClass);
37622         } else {
37623             this.el.removeClass(['is-invalid','is-valid']);
37624             this.el.addClass(['is-valid']);
37625         }
37626         this.fireEvent('valid', this);
37627     },
37628     
37629     markInvalid : function(msg)
37630     {
37631         if(this.allowBlank || this.disabled){
37632             return;
37633         }
37634         
37635         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37636             this.indicatorEl().removeClass('invisible');
37637             this.indicatorEl().addClass('visible');
37638         }
37639         if (Roo.bootstrap.version == 3) {
37640             this.el.removeClass([this.invalidClass, this.validClass]);
37641             this.el.addClass(this.invalidClass);
37642         } else {
37643             this.el.removeClass(['is-invalid','is-valid']);
37644             this.el.addClass(['is-invalid']);
37645         }
37646         
37647         this.fireEvent('invalid', this, msg);
37648         
37649     },
37650     
37651     setValue : function(v, suppressEvent)
37652     {   
37653         if(this.value === v){
37654             return;
37655         }
37656         
37657         this.value = v;
37658         
37659         if(this.rendered){
37660             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37661         }
37662         
37663         Roo.each(this.radioes, function(i){
37664             i.checked = false;
37665             i.el.removeClass('checked');
37666         });
37667         
37668         Roo.each(this.radioes, function(i){
37669             
37670             if(i.value === v || i.value.toString() === v.toString()){
37671                 i.checked = true;
37672                 i.el.addClass('checked');
37673                 
37674                 if(suppressEvent !== true){
37675                     this.fireEvent('check', this, i);
37676                 }
37677                 
37678                 return false;
37679             }
37680             
37681         }, this);
37682         
37683         this.validate();
37684     },
37685     
37686     clearInvalid : function(){
37687         
37688         if(!this.el || this.preventMark){
37689             return;
37690         }
37691         
37692         this.el.removeClass([this.invalidClass]);
37693         
37694         this.fireEvent('valid', this);
37695     }
37696     
37697 });
37698
37699 Roo.apply(Roo.bootstrap.RadioSet, {
37700     
37701     groups: {},
37702     
37703     register : function(set)
37704     {
37705         this.groups[set.name] = set;
37706     },
37707     
37708     get: function(name) 
37709     {
37710         if (typeof(this.groups[name]) == 'undefined') {
37711             return false;
37712         }
37713         
37714         return this.groups[name] ;
37715     }
37716     
37717 });
37718 /*
37719  * Based on:
37720  * Ext JS Library 1.1.1
37721  * Copyright(c) 2006-2007, Ext JS, LLC.
37722  *
37723  * Originally Released Under LGPL - original licence link has changed is not relivant.
37724  *
37725  * Fork - LGPL
37726  * <script type="text/javascript">
37727  */
37728
37729
37730 /**
37731  * @class Roo.bootstrap.SplitBar
37732  * @extends Roo.util.Observable
37733  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37734  * <br><br>
37735  * Usage:
37736  * <pre><code>
37737 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37738                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37739 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37740 split.minSize = 100;
37741 split.maxSize = 600;
37742 split.animate = true;
37743 split.on('moved', splitterMoved);
37744 </code></pre>
37745  * @constructor
37746  * Create a new SplitBar
37747  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37748  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37749  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37750  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37751                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37752                         position of the SplitBar).
37753  */
37754 Roo.bootstrap.SplitBar = function(cfg){
37755     
37756     /** @private */
37757     
37758     //{
37759     //  dragElement : elm
37760     //  resizingElement: el,
37761         // optional..
37762     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37763     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37764         // existingProxy ???
37765     //}
37766     
37767     this.el = Roo.get(cfg.dragElement, true);
37768     this.el.dom.unselectable = "on";
37769     /** @private */
37770     this.resizingEl = Roo.get(cfg.resizingElement, true);
37771
37772     /**
37773      * @private
37774      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37775      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37776      * @type Number
37777      */
37778     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37779     
37780     /**
37781      * The minimum size of the resizing element. (Defaults to 0)
37782      * @type Number
37783      */
37784     this.minSize = 0;
37785     
37786     /**
37787      * The maximum size of the resizing element. (Defaults to 2000)
37788      * @type Number
37789      */
37790     this.maxSize = 2000;
37791     
37792     /**
37793      * Whether to animate the transition to the new size
37794      * @type Boolean
37795      */
37796     this.animate = false;
37797     
37798     /**
37799      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37800      * @type Boolean
37801      */
37802     this.useShim = false;
37803     
37804     /** @private */
37805     this.shim = null;
37806     
37807     if(!cfg.existingProxy){
37808         /** @private */
37809         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37810     }else{
37811         this.proxy = Roo.get(cfg.existingProxy).dom;
37812     }
37813     /** @private */
37814     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37815     
37816     /** @private */
37817     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37818     
37819     /** @private */
37820     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37821     
37822     /** @private */
37823     this.dragSpecs = {};
37824     
37825     /**
37826      * @private The adapter to use to positon and resize elements
37827      */
37828     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37829     this.adapter.init(this);
37830     
37831     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37832         /** @private */
37833         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37834         this.el.addClass("roo-splitbar-h");
37835     }else{
37836         /** @private */
37837         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37838         this.el.addClass("roo-splitbar-v");
37839     }
37840     
37841     this.addEvents({
37842         /**
37843          * @event resize
37844          * Fires when the splitter is moved (alias for {@link #event-moved})
37845          * @param {Roo.bootstrap.SplitBar} this
37846          * @param {Number} newSize the new width or height
37847          */
37848         "resize" : true,
37849         /**
37850          * @event moved
37851          * Fires when the splitter is moved
37852          * @param {Roo.bootstrap.SplitBar} this
37853          * @param {Number} newSize the new width or height
37854          */
37855         "moved" : true,
37856         /**
37857          * @event beforeresize
37858          * Fires before the splitter is dragged
37859          * @param {Roo.bootstrap.SplitBar} this
37860          */
37861         "beforeresize" : true,
37862
37863         "beforeapply" : true
37864     });
37865
37866     Roo.util.Observable.call(this);
37867 };
37868
37869 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37870     onStartProxyDrag : function(x, y){
37871         this.fireEvent("beforeresize", this);
37872         if(!this.overlay){
37873             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37874             o.unselectable();
37875             o.enableDisplayMode("block");
37876             // all splitbars share the same overlay
37877             Roo.bootstrap.SplitBar.prototype.overlay = o;
37878         }
37879         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37880         this.overlay.show();
37881         Roo.get(this.proxy).setDisplayed("block");
37882         var size = this.adapter.getElementSize(this);
37883         this.activeMinSize = this.getMinimumSize();;
37884         this.activeMaxSize = this.getMaximumSize();;
37885         var c1 = size - this.activeMinSize;
37886         var c2 = Math.max(this.activeMaxSize - size, 0);
37887         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37888             this.dd.resetConstraints();
37889             this.dd.setXConstraint(
37890                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37891                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37892             );
37893             this.dd.setYConstraint(0, 0);
37894         }else{
37895             this.dd.resetConstraints();
37896             this.dd.setXConstraint(0, 0);
37897             this.dd.setYConstraint(
37898                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37899                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37900             );
37901          }
37902         this.dragSpecs.startSize = size;
37903         this.dragSpecs.startPoint = [x, y];
37904         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37905     },
37906     
37907     /** 
37908      * @private Called after the drag operation by the DDProxy
37909      */
37910     onEndProxyDrag : function(e){
37911         Roo.get(this.proxy).setDisplayed(false);
37912         var endPoint = Roo.lib.Event.getXY(e);
37913         if(this.overlay){
37914             this.overlay.hide();
37915         }
37916         var newSize;
37917         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37918             newSize = this.dragSpecs.startSize + 
37919                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37920                     endPoint[0] - this.dragSpecs.startPoint[0] :
37921                     this.dragSpecs.startPoint[0] - endPoint[0]
37922                 );
37923         }else{
37924             newSize = this.dragSpecs.startSize + 
37925                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37926                     endPoint[1] - this.dragSpecs.startPoint[1] :
37927                     this.dragSpecs.startPoint[1] - endPoint[1]
37928                 );
37929         }
37930         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37931         if(newSize != this.dragSpecs.startSize){
37932             if(this.fireEvent('beforeapply', this, newSize) !== false){
37933                 this.adapter.setElementSize(this, newSize);
37934                 this.fireEvent("moved", this, newSize);
37935                 this.fireEvent("resize", this, newSize);
37936             }
37937         }
37938     },
37939     
37940     /**
37941      * Get the adapter this SplitBar uses
37942      * @return The adapter object
37943      */
37944     getAdapter : function(){
37945         return this.adapter;
37946     },
37947     
37948     /**
37949      * Set the adapter this SplitBar uses
37950      * @param {Object} adapter A SplitBar adapter object
37951      */
37952     setAdapter : function(adapter){
37953         this.adapter = adapter;
37954         this.adapter.init(this);
37955     },
37956     
37957     /**
37958      * Gets the minimum size for the resizing element
37959      * @return {Number} The minimum size
37960      */
37961     getMinimumSize : function(){
37962         return this.minSize;
37963     },
37964     
37965     /**
37966      * Sets the minimum size for the resizing element
37967      * @param {Number} minSize The minimum size
37968      */
37969     setMinimumSize : function(minSize){
37970         this.minSize = minSize;
37971     },
37972     
37973     /**
37974      * Gets the maximum size for the resizing element
37975      * @return {Number} The maximum size
37976      */
37977     getMaximumSize : function(){
37978         return this.maxSize;
37979     },
37980     
37981     /**
37982      * Sets the maximum size for the resizing element
37983      * @param {Number} maxSize The maximum size
37984      */
37985     setMaximumSize : function(maxSize){
37986         this.maxSize = maxSize;
37987     },
37988     
37989     /**
37990      * Sets the initialize size for the resizing element
37991      * @param {Number} size The initial size
37992      */
37993     setCurrentSize : function(size){
37994         var oldAnimate = this.animate;
37995         this.animate = false;
37996         this.adapter.setElementSize(this, size);
37997         this.animate = oldAnimate;
37998     },
37999     
38000     /**
38001      * Destroy this splitbar. 
38002      * @param {Boolean} removeEl True to remove the element
38003      */
38004     destroy : function(removeEl){
38005         if(this.shim){
38006             this.shim.remove();
38007         }
38008         this.dd.unreg();
38009         this.proxy.parentNode.removeChild(this.proxy);
38010         if(removeEl){
38011             this.el.remove();
38012         }
38013     }
38014 });
38015
38016 /**
38017  * @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.
38018  */
38019 Roo.bootstrap.SplitBar.createProxy = function(dir){
38020     var proxy = new Roo.Element(document.createElement("div"));
38021     proxy.unselectable();
38022     var cls = 'roo-splitbar-proxy';
38023     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38024     document.body.appendChild(proxy.dom);
38025     return proxy.dom;
38026 };
38027
38028 /** 
38029  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38030  * Default Adapter. It assumes the splitter and resizing element are not positioned
38031  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38032  */
38033 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38034 };
38035
38036 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38037     // do nothing for now
38038     init : function(s){
38039     
38040     },
38041     /**
38042      * Called before drag operations to get the current size of the resizing element. 
38043      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38044      */
38045      getElementSize : function(s){
38046         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38047             return s.resizingEl.getWidth();
38048         }else{
38049             return s.resizingEl.getHeight();
38050         }
38051     },
38052     
38053     /**
38054      * Called after drag operations to set the size of the resizing element.
38055      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38056      * @param {Number} newSize The new size to set
38057      * @param {Function} onComplete A function to be invoked when resizing is complete
38058      */
38059     setElementSize : function(s, newSize, onComplete){
38060         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38061             if(!s.animate){
38062                 s.resizingEl.setWidth(newSize);
38063                 if(onComplete){
38064                     onComplete(s, newSize);
38065                 }
38066             }else{
38067                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38068             }
38069         }else{
38070             
38071             if(!s.animate){
38072                 s.resizingEl.setHeight(newSize);
38073                 if(onComplete){
38074                     onComplete(s, newSize);
38075                 }
38076             }else{
38077                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38078             }
38079         }
38080     }
38081 };
38082
38083 /** 
38084  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38085  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38086  * Adapter that  moves the splitter element to align with the resized sizing element. 
38087  * Used with an absolute positioned SplitBar.
38088  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38089  * document.body, make sure you assign an id to the body element.
38090  */
38091 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38092     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38093     this.container = Roo.get(container);
38094 };
38095
38096 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38097     init : function(s){
38098         this.basic.init(s);
38099     },
38100     
38101     getElementSize : function(s){
38102         return this.basic.getElementSize(s);
38103     },
38104     
38105     setElementSize : function(s, newSize, onComplete){
38106         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38107     },
38108     
38109     moveSplitter : function(s){
38110         var yes = Roo.bootstrap.SplitBar;
38111         switch(s.placement){
38112             case yes.LEFT:
38113                 s.el.setX(s.resizingEl.getRight());
38114                 break;
38115             case yes.RIGHT:
38116                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38117                 break;
38118             case yes.TOP:
38119                 s.el.setY(s.resizingEl.getBottom());
38120                 break;
38121             case yes.BOTTOM:
38122                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38123                 break;
38124         }
38125     }
38126 };
38127
38128 /**
38129  * Orientation constant - Create a vertical SplitBar
38130  * @static
38131  * @type Number
38132  */
38133 Roo.bootstrap.SplitBar.VERTICAL = 1;
38134
38135 /**
38136  * Orientation constant - Create a horizontal SplitBar
38137  * @static
38138  * @type Number
38139  */
38140 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38141
38142 /**
38143  * Placement constant - The resizing element is to the left of the splitter element
38144  * @static
38145  * @type Number
38146  */
38147 Roo.bootstrap.SplitBar.LEFT = 1;
38148
38149 /**
38150  * Placement constant - The resizing element is to the right of the splitter element
38151  * @static
38152  * @type Number
38153  */
38154 Roo.bootstrap.SplitBar.RIGHT = 2;
38155
38156 /**
38157  * Placement constant - The resizing element is positioned above the splitter element
38158  * @static
38159  * @type Number
38160  */
38161 Roo.bootstrap.SplitBar.TOP = 3;
38162
38163 /**
38164  * Placement constant - The resizing element is positioned under splitter element
38165  * @static
38166  * @type Number
38167  */
38168 Roo.bootstrap.SplitBar.BOTTOM = 4;
38169 Roo.namespace("Roo.bootstrap.layout");/*
38170  * Based on:
38171  * Ext JS Library 1.1.1
38172  * Copyright(c) 2006-2007, Ext JS, LLC.
38173  *
38174  * Originally Released Under LGPL - original licence link has changed is not relivant.
38175  *
38176  * Fork - LGPL
38177  * <script type="text/javascript">
38178  */
38179
38180 /**
38181  * @class Roo.bootstrap.layout.Manager
38182  * @extends Roo.bootstrap.Component
38183  * Base class for layout managers.
38184  */
38185 Roo.bootstrap.layout.Manager = function(config)
38186 {
38187     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38188
38189
38190
38191
38192
38193     /** false to disable window resize monitoring @type Boolean */
38194     this.monitorWindowResize = true;
38195     this.regions = {};
38196     this.addEvents({
38197         /**
38198          * @event layout
38199          * Fires when a layout is performed.
38200          * @param {Roo.LayoutManager} this
38201          */
38202         "layout" : true,
38203         /**
38204          * @event regionresized
38205          * Fires when the user resizes a region.
38206          * @param {Roo.LayoutRegion} region The resized region
38207          * @param {Number} newSize The new size (width for east/west, height for north/south)
38208          */
38209         "regionresized" : true,
38210         /**
38211          * @event regioncollapsed
38212          * Fires when a region is collapsed.
38213          * @param {Roo.LayoutRegion} region The collapsed region
38214          */
38215         "regioncollapsed" : true,
38216         /**
38217          * @event regionexpanded
38218          * Fires when a region is expanded.
38219          * @param {Roo.LayoutRegion} region The expanded region
38220          */
38221         "regionexpanded" : true
38222     });
38223     this.updating = false;
38224
38225     if (config.el) {
38226         this.el = Roo.get(config.el);
38227         this.initEvents();
38228     }
38229
38230 };
38231
38232 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38233
38234
38235     regions : null,
38236
38237     monitorWindowResize : true,
38238
38239
38240     updating : false,
38241
38242
38243     onRender : function(ct, position)
38244     {
38245         if(!this.el){
38246             this.el = Roo.get(ct);
38247             this.initEvents();
38248         }
38249         //this.fireEvent('render',this);
38250     },
38251
38252
38253     initEvents: function()
38254     {
38255
38256
38257         // ie scrollbar fix
38258         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38259             document.body.scroll = "no";
38260         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38261             this.el.position('relative');
38262         }
38263         this.id = this.el.id;
38264         this.el.addClass("roo-layout-container");
38265         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38266         if(this.el.dom != document.body ) {
38267             this.el.on('resize', this.layout,this);
38268             this.el.on('show', this.layout,this);
38269         }
38270
38271     },
38272
38273     /**
38274      * Returns true if this layout is currently being updated
38275      * @return {Boolean}
38276      */
38277     isUpdating : function(){
38278         return this.updating;
38279     },
38280
38281     /**
38282      * Suspend the LayoutManager from doing auto-layouts while
38283      * making multiple add or remove calls
38284      */
38285     beginUpdate : function(){
38286         this.updating = true;
38287     },
38288
38289     /**
38290      * Restore auto-layouts and optionally disable the manager from performing a layout
38291      * @param {Boolean} noLayout true to disable a layout update
38292      */
38293     endUpdate : function(noLayout){
38294         this.updating = false;
38295         if(!noLayout){
38296             this.layout();
38297         }
38298     },
38299
38300     layout: function(){
38301         // abstract...
38302     },
38303
38304     onRegionResized : function(region, newSize){
38305         this.fireEvent("regionresized", region, newSize);
38306         this.layout();
38307     },
38308
38309     onRegionCollapsed : function(region){
38310         this.fireEvent("regioncollapsed", region);
38311     },
38312
38313     onRegionExpanded : function(region){
38314         this.fireEvent("regionexpanded", region);
38315     },
38316
38317     /**
38318      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38319      * performs box-model adjustments.
38320      * @return {Object} The size as an object {width: (the width), height: (the height)}
38321      */
38322     getViewSize : function()
38323     {
38324         var size;
38325         if(this.el.dom != document.body){
38326             size = this.el.getSize();
38327         }else{
38328             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38329         }
38330         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38331         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38332         return size;
38333     },
38334
38335     /**
38336      * Returns the Element this layout is bound to.
38337      * @return {Roo.Element}
38338      */
38339     getEl : function(){
38340         return this.el;
38341     },
38342
38343     /**
38344      * Returns the specified region.
38345      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38346      * @return {Roo.LayoutRegion}
38347      */
38348     getRegion : function(target){
38349         return this.regions[target.toLowerCase()];
38350     },
38351
38352     onWindowResize : function(){
38353         if(this.monitorWindowResize){
38354             this.layout();
38355         }
38356     }
38357 });
38358 /*
38359  * Based on:
38360  * Ext JS Library 1.1.1
38361  * Copyright(c) 2006-2007, Ext JS, LLC.
38362  *
38363  * Originally Released Under LGPL - original licence link has changed is not relivant.
38364  *
38365  * Fork - LGPL
38366  * <script type="text/javascript">
38367  */
38368 /**
38369  * @class Roo.bootstrap.layout.Border
38370  * @extends Roo.bootstrap.layout.Manager
38371  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38372  * please see: examples/bootstrap/nested.html<br><br>
38373  
38374 <b>The container the layout is rendered into can be either the body element or any other element.
38375 If it is not the body element, the container needs to either be an absolute positioned element,
38376 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38377 the container size if it is not the body element.</b>
38378
38379 * @constructor
38380 * Create a new Border
38381 * @param {Object} config Configuration options
38382  */
38383 Roo.bootstrap.layout.Border = function(config){
38384     config = config || {};
38385     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38386     
38387     
38388     
38389     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38390         if(config[region]){
38391             config[region].region = region;
38392             this.addRegion(config[region]);
38393         }
38394     },this);
38395     
38396 };
38397
38398 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38399
38400 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38401     
38402     parent : false, // this might point to a 'nest' or a ???
38403     
38404     /**
38405      * Creates and adds a new region if it doesn't already exist.
38406      * @param {String} target The target region key (north, south, east, west or center).
38407      * @param {Object} config The regions config object
38408      * @return {BorderLayoutRegion} The new region
38409      */
38410     addRegion : function(config)
38411     {
38412         if(!this.regions[config.region]){
38413             var r = this.factory(config);
38414             this.bindRegion(r);
38415         }
38416         return this.regions[config.region];
38417     },
38418
38419     // private (kinda)
38420     bindRegion : function(r){
38421         this.regions[r.config.region] = r;
38422         
38423         r.on("visibilitychange",    this.layout, this);
38424         r.on("paneladded",          this.layout, this);
38425         r.on("panelremoved",        this.layout, this);
38426         r.on("invalidated",         this.layout, this);
38427         r.on("resized",             this.onRegionResized, this);
38428         r.on("collapsed",           this.onRegionCollapsed, this);
38429         r.on("expanded",            this.onRegionExpanded, this);
38430     },
38431
38432     /**
38433      * Performs a layout update.
38434      */
38435     layout : function()
38436     {
38437         if(this.updating) {
38438             return;
38439         }
38440         
38441         // render all the rebions if they have not been done alreayd?
38442         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38443             if(this.regions[region] && !this.regions[region].bodyEl){
38444                 this.regions[region].onRender(this.el)
38445             }
38446         },this);
38447         
38448         var size = this.getViewSize();
38449         var w = size.width;
38450         var h = size.height;
38451         var centerW = w;
38452         var centerH = h;
38453         var centerY = 0;
38454         var centerX = 0;
38455         //var x = 0, y = 0;
38456
38457         var rs = this.regions;
38458         var north = rs["north"];
38459         var south = rs["south"]; 
38460         var west = rs["west"];
38461         var east = rs["east"];
38462         var center = rs["center"];
38463         //if(this.hideOnLayout){ // not supported anymore
38464             //c.el.setStyle("display", "none");
38465         //}
38466         if(north && north.isVisible()){
38467             var b = north.getBox();
38468             var m = north.getMargins();
38469             b.width = w - (m.left+m.right);
38470             b.x = m.left;
38471             b.y = m.top;
38472             centerY = b.height + b.y + m.bottom;
38473             centerH -= centerY;
38474             north.updateBox(this.safeBox(b));
38475         }
38476         if(south && south.isVisible()){
38477             var b = south.getBox();
38478             var m = south.getMargins();
38479             b.width = w - (m.left+m.right);
38480             b.x = m.left;
38481             var totalHeight = (b.height + m.top + m.bottom);
38482             b.y = h - totalHeight + m.top;
38483             centerH -= totalHeight;
38484             south.updateBox(this.safeBox(b));
38485         }
38486         if(west && west.isVisible()){
38487             var b = west.getBox();
38488             var m = west.getMargins();
38489             b.height = centerH - (m.top+m.bottom);
38490             b.x = m.left;
38491             b.y = centerY + m.top;
38492             var totalWidth = (b.width + m.left + m.right);
38493             centerX += totalWidth;
38494             centerW -= totalWidth;
38495             west.updateBox(this.safeBox(b));
38496         }
38497         if(east && east.isVisible()){
38498             var b = east.getBox();
38499             var m = east.getMargins();
38500             b.height = centerH - (m.top+m.bottom);
38501             var totalWidth = (b.width + m.left + m.right);
38502             b.x = w - totalWidth + m.left;
38503             b.y = centerY + m.top;
38504             centerW -= totalWidth;
38505             east.updateBox(this.safeBox(b));
38506         }
38507         if(center){
38508             var m = center.getMargins();
38509             var centerBox = {
38510                 x: centerX + m.left,
38511                 y: centerY + m.top,
38512                 width: centerW - (m.left+m.right),
38513                 height: centerH - (m.top+m.bottom)
38514             };
38515             //if(this.hideOnLayout){
38516                 //center.el.setStyle("display", "block");
38517             //}
38518             center.updateBox(this.safeBox(centerBox));
38519         }
38520         this.el.repaint();
38521         this.fireEvent("layout", this);
38522     },
38523
38524     // private
38525     safeBox : function(box){
38526         box.width = Math.max(0, box.width);
38527         box.height = Math.max(0, box.height);
38528         return box;
38529     },
38530
38531     /**
38532      * Adds a ContentPanel (or subclass) to this layout.
38533      * @param {String} target The target region key (north, south, east, west or center).
38534      * @param {Roo.ContentPanel} panel The panel to add
38535      * @return {Roo.ContentPanel} The added panel
38536      */
38537     add : function(target, panel){
38538          
38539         target = target.toLowerCase();
38540         return this.regions[target].add(panel);
38541     },
38542
38543     /**
38544      * Remove a ContentPanel (or subclass) to this layout.
38545      * @param {String} target The target region key (north, south, east, west or center).
38546      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38547      * @return {Roo.ContentPanel} The removed panel
38548      */
38549     remove : function(target, panel){
38550         target = target.toLowerCase();
38551         return this.regions[target].remove(panel);
38552     },
38553
38554     /**
38555      * Searches all regions for a panel with the specified id
38556      * @param {String} panelId
38557      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38558      */
38559     findPanel : function(panelId){
38560         var rs = this.regions;
38561         for(var target in rs){
38562             if(typeof rs[target] != "function"){
38563                 var p = rs[target].getPanel(panelId);
38564                 if(p){
38565                     return p;
38566                 }
38567             }
38568         }
38569         return null;
38570     },
38571
38572     /**
38573      * Searches all regions for a panel with the specified id and activates (shows) it.
38574      * @param {String/ContentPanel} panelId The panels id or the panel itself
38575      * @return {Roo.ContentPanel} The shown panel or null
38576      */
38577     showPanel : function(panelId) {
38578       var rs = this.regions;
38579       for(var target in rs){
38580          var r = rs[target];
38581          if(typeof r != "function"){
38582             if(r.hasPanel(panelId)){
38583                return r.showPanel(panelId);
38584             }
38585          }
38586       }
38587       return null;
38588    },
38589
38590    /**
38591      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38592      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38593      */
38594    /*
38595     restoreState : function(provider){
38596         if(!provider){
38597             provider = Roo.state.Manager;
38598         }
38599         var sm = new Roo.LayoutStateManager();
38600         sm.init(this, provider);
38601     },
38602 */
38603  
38604  
38605     /**
38606      * Adds a xtype elements to the layout.
38607      * <pre><code>
38608
38609 layout.addxtype({
38610        xtype : 'ContentPanel',
38611        region: 'west',
38612        items: [ .... ]
38613    }
38614 );
38615
38616 layout.addxtype({
38617         xtype : 'NestedLayoutPanel',
38618         region: 'west',
38619         layout: {
38620            center: { },
38621            west: { }   
38622         },
38623         items : [ ... list of content panels or nested layout panels.. ]
38624    }
38625 );
38626 </code></pre>
38627      * @param {Object} cfg Xtype definition of item to add.
38628      */
38629     addxtype : function(cfg)
38630     {
38631         // basically accepts a pannel...
38632         // can accept a layout region..!?!?
38633         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38634         
38635         
38636         // theory?  children can only be panels??
38637         
38638         //if (!cfg.xtype.match(/Panel$/)) {
38639         //    return false;
38640         //}
38641         var ret = false;
38642         
38643         if (typeof(cfg.region) == 'undefined') {
38644             Roo.log("Failed to add Panel, region was not set");
38645             Roo.log(cfg);
38646             return false;
38647         }
38648         var region = cfg.region;
38649         delete cfg.region;
38650         
38651           
38652         var xitems = [];
38653         if (cfg.items) {
38654             xitems = cfg.items;
38655             delete cfg.items;
38656         }
38657         var nb = false;
38658         
38659         if ( region == 'center') {
38660             Roo.log("Center: " + cfg.title);
38661         }
38662         
38663         
38664         switch(cfg.xtype) 
38665         {
38666             case 'Content':  // ContentPanel (el, cfg)
38667             case 'Scroll':  // ContentPanel (el, cfg)
38668             case 'View': 
38669                 cfg.autoCreate = cfg.autoCreate || true;
38670                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38671                 //} else {
38672                 //    var el = this.el.createChild();
38673                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38674                 //}
38675                 
38676                 this.add(region, ret);
38677                 break;
38678             
38679             /*
38680             case 'TreePanel': // our new panel!
38681                 cfg.el = this.el.createChild();
38682                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38683                 this.add(region, ret);
38684                 break;
38685             */
38686             
38687             case 'Nest': 
38688                 // create a new Layout (which is  a Border Layout...
38689                 
38690                 var clayout = cfg.layout;
38691                 clayout.el  = this.el.createChild();
38692                 clayout.items   = clayout.items  || [];
38693                 
38694                 delete cfg.layout;
38695                 
38696                 // replace this exitems with the clayout ones..
38697                 xitems = clayout.items;
38698                  
38699                 // force background off if it's in center...
38700                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38701                     cfg.background = false;
38702                 }
38703                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38704                 
38705                 
38706                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38707                 //console.log('adding nested layout panel '  + cfg.toSource());
38708                 this.add(region, ret);
38709                 nb = {}; /// find first...
38710                 break;
38711             
38712             case 'Grid':
38713                 
38714                 // needs grid and region
38715                 
38716                 //var el = this.getRegion(region).el.createChild();
38717                 /*
38718                  *var el = this.el.createChild();
38719                 // create the grid first...
38720                 cfg.grid.container = el;
38721                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38722                 */
38723                 
38724                 if (region == 'center' && this.active ) {
38725                     cfg.background = false;
38726                 }
38727                 
38728                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38729                 
38730                 this.add(region, ret);
38731                 /*
38732                 if (cfg.background) {
38733                     // render grid on panel activation (if panel background)
38734                     ret.on('activate', function(gp) {
38735                         if (!gp.grid.rendered) {
38736                     //        gp.grid.render(el);
38737                         }
38738                     });
38739                 } else {
38740                   //  cfg.grid.render(el);
38741                 }
38742                 */
38743                 break;
38744            
38745            
38746             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38747                 // it was the old xcomponent building that caused this before.
38748                 // espeically if border is the top element in the tree.
38749                 ret = this;
38750                 break; 
38751                 
38752                     
38753                 
38754                 
38755                 
38756             default:
38757                 /*
38758                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38759                     
38760                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38761                     this.add(region, ret);
38762                 } else {
38763                 */
38764                     Roo.log(cfg);
38765                     throw "Can not add '" + cfg.xtype + "' to Border";
38766                     return null;
38767              
38768                                 
38769              
38770         }
38771         this.beginUpdate();
38772         // add children..
38773         var region = '';
38774         var abn = {};
38775         Roo.each(xitems, function(i)  {
38776             region = nb && i.region ? i.region : false;
38777             
38778             var add = ret.addxtype(i);
38779            
38780             if (region) {
38781                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38782                 if (!i.background) {
38783                     abn[region] = nb[region] ;
38784                 }
38785             }
38786             
38787         });
38788         this.endUpdate();
38789
38790         // make the last non-background panel active..
38791         //if (nb) { Roo.log(abn); }
38792         if (nb) {
38793             
38794             for(var r in abn) {
38795                 region = this.getRegion(r);
38796                 if (region) {
38797                     // tried using nb[r], but it does not work..
38798                      
38799                     region.showPanel(abn[r]);
38800                    
38801                 }
38802             }
38803         }
38804         return ret;
38805         
38806     },
38807     
38808     
38809 // private
38810     factory : function(cfg)
38811     {
38812         
38813         var validRegions = Roo.bootstrap.layout.Border.regions;
38814
38815         var target = cfg.region;
38816         cfg.mgr = this;
38817         
38818         var r = Roo.bootstrap.layout;
38819         Roo.log(target);
38820         switch(target){
38821             case "north":
38822                 return new r.North(cfg);
38823             case "south":
38824                 return new r.South(cfg);
38825             case "east":
38826                 return new r.East(cfg);
38827             case "west":
38828                 return new r.West(cfg);
38829             case "center":
38830                 return new r.Center(cfg);
38831         }
38832         throw 'Layout region "'+target+'" not supported.';
38833     }
38834     
38835     
38836 });
38837  /*
38838  * Based on:
38839  * Ext JS Library 1.1.1
38840  * Copyright(c) 2006-2007, Ext JS, LLC.
38841  *
38842  * Originally Released Under LGPL - original licence link has changed is not relivant.
38843  *
38844  * Fork - LGPL
38845  * <script type="text/javascript">
38846  */
38847  
38848 /**
38849  * @class Roo.bootstrap.layout.Basic
38850  * @extends Roo.util.Observable
38851  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38852  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38853  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38854  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38855  * @cfg {string}   region  the region that it inhabits..
38856  * @cfg {bool}   skipConfig skip config?
38857  * 
38858
38859  */
38860 Roo.bootstrap.layout.Basic = function(config){
38861     
38862     this.mgr = config.mgr;
38863     
38864     this.position = config.region;
38865     
38866     var skipConfig = config.skipConfig;
38867     
38868     this.events = {
38869         /**
38870          * @scope Roo.BasicLayoutRegion
38871          */
38872         
38873         /**
38874          * @event beforeremove
38875          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38876          * @param {Roo.LayoutRegion} this
38877          * @param {Roo.ContentPanel} panel The panel
38878          * @param {Object} e The cancel event object
38879          */
38880         "beforeremove" : true,
38881         /**
38882          * @event invalidated
38883          * Fires when the layout for this region is changed.
38884          * @param {Roo.LayoutRegion} this
38885          */
38886         "invalidated" : true,
38887         /**
38888          * @event visibilitychange
38889          * Fires when this region is shown or hidden 
38890          * @param {Roo.LayoutRegion} this
38891          * @param {Boolean} visibility true or false
38892          */
38893         "visibilitychange" : true,
38894         /**
38895          * @event paneladded
38896          * Fires when a panel is added. 
38897          * @param {Roo.LayoutRegion} this
38898          * @param {Roo.ContentPanel} panel The panel
38899          */
38900         "paneladded" : true,
38901         /**
38902          * @event panelremoved
38903          * Fires when a panel is removed. 
38904          * @param {Roo.LayoutRegion} this
38905          * @param {Roo.ContentPanel} panel The panel
38906          */
38907         "panelremoved" : true,
38908         /**
38909          * @event beforecollapse
38910          * Fires when this region before collapse.
38911          * @param {Roo.LayoutRegion} this
38912          */
38913         "beforecollapse" : true,
38914         /**
38915          * @event collapsed
38916          * Fires when this region is collapsed.
38917          * @param {Roo.LayoutRegion} this
38918          */
38919         "collapsed" : true,
38920         /**
38921          * @event expanded
38922          * Fires when this region is expanded.
38923          * @param {Roo.LayoutRegion} this
38924          */
38925         "expanded" : true,
38926         /**
38927          * @event slideshow
38928          * Fires when this region is slid into view.
38929          * @param {Roo.LayoutRegion} this
38930          */
38931         "slideshow" : true,
38932         /**
38933          * @event slidehide
38934          * Fires when this region slides out of view. 
38935          * @param {Roo.LayoutRegion} this
38936          */
38937         "slidehide" : true,
38938         /**
38939          * @event panelactivated
38940          * Fires when a panel is activated. 
38941          * @param {Roo.LayoutRegion} this
38942          * @param {Roo.ContentPanel} panel The activated panel
38943          */
38944         "panelactivated" : true,
38945         /**
38946          * @event resized
38947          * Fires when the user resizes this region. 
38948          * @param {Roo.LayoutRegion} this
38949          * @param {Number} newSize The new size (width for east/west, height for north/south)
38950          */
38951         "resized" : true
38952     };
38953     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38954     this.panels = new Roo.util.MixedCollection();
38955     this.panels.getKey = this.getPanelId.createDelegate(this);
38956     this.box = null;
38957     this.activePanel = null;
38958     // ensure listeners are added...
38959     
38960     if (config.listeners || config.events) {
38961         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38962             listeners : config.listeners || {},
38963             events : config.events || {}
38964         });
38965     }
38966     
38967     if(skipConfig !== true){
38968         this.applyConfig(config);
38969     }
38970 };
38971
38972 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38973 {
38974     getPanelId : function(p){
38975         return p.getId();
38976     },
38977     
38978     applyConfig : function(config){
38979         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38980         this.config = config;
38981         
38982     },
38983     
38984     /**
38985      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38986      * the width, for horizontal (north, south) the height.
38987      * @param {Number} newSize The new width or height
38988      */
38989     resizeTo : function(newSize){
38990         var el = this.el ? this.el :
38991                  (this.activePanel ? this.activePanel.getEl() : null);
38992         if(el){
38993             switch(this.position){
38994                 case "east":
38995                 case "west":
38996                     el.setWidth(newSize);
38997                     this.fireEvent("resized", this, newSize);
38998                 break;
38999                 case "north":
39000                 case "south":
39001                     el.setHeight(newSize);
39002                     this.fireEvent("resized", this, newSize);
39003                 break;                
39004             }
39005         }
39006     },
39007     
39008     getBox : function(){
39009         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39010     },
39011     
39012     getMargins : function(){
39013         return this.margins;
39014     },
39015     
39016     updateBox : function(box){
39017         this.box = box;
39018         var el = this.activePanel.getEl();
39019         el.dom.style.left = box.x + "px";
39020         el.dom.style.top = box.y + "px";
39021         this.activePanel.setSize(box.width, box.height);
39022     },
39023     
39024     /**
39025      * Returns the container element for this region.
39026      * @return {Roo.Element}
39027      */
39028     getEl : function(){
39029         return this.activePanel;
39030     },
39031     
39032     /**
39033      * Returns true if this region is currently visible.
39034      * @return {Boolean}
39035      */
39036     isVisible : function(){
39037         return this.activePanel ? true : false;
39038     },
39039     
39040     setActivePanel : function(panel){
39041         panel = this.getPanel(panel);
39042         if(this.activePanel && this.activePanel != panel){
39043             this.activePanel.setActiveState(false);
39044             this.activePanel.getEl().setLeftTop(-10000,-10000);
39045         }
39046         this.activePanel = panel;
39047         panel.setActiveState(true);
39048         if(this.box){
39049             panel.setSize(this.box.width, this.box.height);
39050         }
39051         this.fireEvent("panelactivated", this, panel);
39052         this.fireEvent("invalidated");
39053     },
39054     
39055     /**
39056      * Show the specified panel.
39057      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39058      * @return {Roo.ContentPanel} The shown panel or null
39059      */
39060     showPanel : function(panel){
39061         panel = this.getPanel(panel);
39062         if(panel){
39063             this.setActivePanel(panel);
39064         }
39065         return panel;
39066     },
39067     
39068     /**
39069      * Get the active panel for this region.
39070      * @return {Roo.ContentPanel} The active panel or null
39071      */
39072     getActivePanel : function(){
39073         return this.activePanel;
39074     },
39075     
39076     /**
39077      * Add the passed ContentPanel(s)
39078      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39079      * @return {Roo.ContentPanel} The panel added (if only one was added)
39080      */
39081     add : function(panel){
39082         if(arguments.length > 1){
39083             for(var i = 0, len = arguments.length; i < len; i++) {
39084                 this.add(arguments[i]);
39085             }
39086             return null;
39087         }
39088         if(this.hasPanel(panel)){
39089             this.showPanel(panel);
39090             return panel;
39091         }
39092         var el = panel.getEl();
39093         if(el.dom.parentNode != this.mgr.el.dom){
39094             this.mgr.el.dom.appendChild(el.dom);
39095         }
39096         if(panel.setRegion){
39097             panel.setRegion(this);
39098         }
39099         this.panels.add(panel);
39100         el.setStyle("position", "absolute");
39101         if(!panel.background){
39102             this.setActivePanel(panel);
39103             if(this.config.initialSize && this.panels.getCount()==1){
39104                 this.resizeTo(this.config.initialSize);
39105             }
39106         }
39107         this.fireEvent("paneladded", this, panel);
39108         return panel;
39109     },
39110     
39111     /**
39112      * Returns true if the panel is in this region.
39113      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39114      * @return {Boolean}
39115      */
39116     hasPanel : function(panel){
39117         if(typeof panel == "object"){ // must be panel obj
39118             panel = panel.getId();
39119         }
39120         return this.getPanel(panel) ? true : false;
39121     },
39122     
39123     /**
39124      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39125      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39126      * @param {Boolean} preservePanel Overrides the config preservePanel option
39127      * @return {Roo.ContentPanel} The panel that was removed
39128      */
39129     remove : function(panel, preservePanel){
39130         panel = this.getPanel(panel);
39131         if(!panel){
39132             return null;
39133         }
39134         var e = {};
39135         this.fireEvent("beforeremove", this, panel, e);
39136         if(e.cancel === true){
39137             return null;
39138         }
39139         var panelId = panel.getId();
39140         this.panels.removeKey(panelId);
39141         return panel;
39142     },
39143     
39144     /**
39145      * Returns the panel specified or null if it's not in this region.
39146      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39147      * @return {Roo.ContentPanel}
39148      */
39149     getPanel : function(id){
39150         if(typeof id == "object"){ // must be panel obj
39151             return id;
39152         }
39153         return this.panels.get(id);
39154     },
39155     
39156     /**
39157      * Returns this regions position (north/south/east/west/center).
39158      * @return {String} 
39159      */
39160     getPosition: function(){
39161         return this.position;    
39162     }
39163 });/*
39164  * Based on:
39165  * Ext JS Library 1.1.1
39166  * Copyright(c) 2006-2007, Ext JS, LLC.
39167  *
39168  * Originally Released Under LGPL - original licence link has changed is not relivant.
39169  *
39170  * Fork - LGPL
39171  * <script type="text/javascript">
39172  */
39173  
39174 /**
39175  * @class Roo.bootstrap.layout.Region
39176  * @extends Roo.bootstrap.layout.Basic
39177  * This class represents a region in a layout manager.
39178  
39179  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39180  * @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})
39181  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39182  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39183  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39184  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39185  * @cfg {String}    title           The title for the region (overrides panel titles)
39186  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39187  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39188  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39189  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39190  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39191  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39192  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39193  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39194  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39195  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39196
39197  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39198  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39199  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39200  * @cfg {Number}    width           For East/West panels
39201  * @cfg {Number}    height          For North/South panels
39202  * @cfg {Boolean}   split           To show the splitter
39203  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39204  * 
39205  * @cfg {string}   cls             Extra CSS classes to add to region
39206  * 
39207  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39208  * @cfg {string}   region  the region that it inhabits..
39209  *
39210
39211  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39212  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39213
39214  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39215  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39216  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39217  */
39218 Roo.bootstrap.layout.Region = function(config)
39219 {
39220     this.applyConfig(config);
39221
39222     var mgr = config.mgr;
39223     var pos = config.region;
39224     config.skipConfig = true;
39225     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39226     
39227     if (mgr.el) {
39228         this.onRender(mgr.el);   
39229     }
39230      
39231     this.visible = true;
39232     this.collapsed = false;
39233     this.unrendered_panels = [];
39234 };
39235
39236 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39237
39238     position: '', // set by wrapper (eg. north/south etc..)
39239     unrendered_panels : null,  // unrendered panels.
39240     
39241     tabPosition : false,
39242     
39243     mgr: false, // points to 'Border'
39244     
39245     
39246     createBody : function(){
39247         /** This region's body element 
39248         * @type Roo.Element */
39249         this.bodyEl = this.el.createChild({
39250                 tag: "div",
39251                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39252         });
39253     },
39254
39255     onRender: function(ctr, pos)
39256     {
39257         var dh = Roo.DomHelper;
39258         /** This region's container element 
39259         * @type Roo.Element */
39260         this.el = dh.append(ctr.dom, {
39261                 tag: "div",
39262                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39263             }, true);
39264         /** This region's title element 
39265         * @type Roo.Element */
39266     
39267         this.titleEl = dh.append(this.el.dom,  {
39268                 tag: "div",
39269                 unselectable: "on",
39270                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39271                 children:[
39272                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39273                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39274                 ]
39275             }, true);
39276         
39277         this.titleEl.enableDisplayMode();
39278         /** This region's title text element 
39279         * @type HTMLElement */
39280         this.titleTextEl = this.titleEl.dom.firstChild;
39281         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39282         /*
39283         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39284         this.closeBtn.enableDisplayMode();
39285         this.closeBtn.on("click", this.closeClicked, this);
39286         this.closeBtn.hide();
39287     */
39288         this.createBody(this.config);
39289         if(this.config.hideWhenEmpty){
39290             this.hide();
39291             this.on("paneladded", this.validateVisibility, this);
39292             this.on("panelremoved", this.validateVisibility, this);
39293         }
39294         if(this.autoScroll){
39295             this.bodyEl.setStyle("overflow", "auto");
39296         }else{
39297             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39298         }
39299         //if(c.titlebar !== false){
39300             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39301                 this.titleEl.hide();
39302             }else{
39303                 this.titleEl.show();
39304                 if(this.config.title){
39305                     this.titleTextEl.innerHTML = this.config.title;
39306                 }
39307             }
39308         //}
39309         if(this.config.collapsed){
39310             this.collapse(true);
39311         }
39312         if(this.config.hidden){
39313             this.hide();
39314         }
39315         
39316         if (this.unrendered_panels && this.unrendered_panels.length) {
39317             for (var i =0;i< this.unrendered_panels.length; i++) {
39318                 this.add(this.unrendered_panels[i]);
39319             }
39320             this.unrendered_panels = null;
39321             
39322         }
39323         
39324     },
39325     
39326     applyConfig : function(c)
39327     {
39328         /*
39329          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39330             var dh = Roo.DomHelper;
39331             if(c.titlebar !== false){
39332                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39333                 this.collapseBtn.on("click", this.collapse, this);
39334                 this.collapseBtn.enableDisplayMode();
39335                 /*
39336                 if(c.showPin === true || this.showPin){
39337                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39338                     this.stickBtn.enableDisplayMode();
39339                     this.stickBtn.on("click", this.expand, this);
39340                     this.stickBtn.hide();
39341                 }
39342                 
39343             }
39344             */
39345             /** This region's collapsed element
39346             * @type Roo.Element */
39347             /*
39348              *
39349             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39350                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39351             ]}, true);
39352             
39353             if(c.floatable !== false){
39354                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39355                this.collapsedEl.on("click", this.collapseClick, this);
39356             }
39357
39358             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39359                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39360                    id: "message", unselectable: "on", style:{"float":"left"}});
39361                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39362              }
39363             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39364             this.expandBtn.on("click", this.expand, this);
39365             
39366         }
39367         
39368         if(this.collapseBtn){
39369             this.collapseBtn.setVisible(c.collapsible == true);
39370         }
39371         
39372         this.cmargins = c.cmargins || this.cmargins ||
39373                          (this.position == "west" || this.position == "east" ?
39374                              {top: 0, left: 2, right:2, bottom: 0} :
39375                              {top: 2, left: 0, right:0, bottom: 2});
39376         */
39377         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39378         
39379         
39380         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39381         
39382         this.autoScroll = c.autoScroll || false;
39383         
39384         
39385        
39386         
39387         this.duration = c.duration || .30;
39388         this.slideDuration = c.slideDuration || .45;
39389         this.config = c;
39390        
39391     },
39392     /**
39393      * Returns true if this region is currently visible.
39394      * @return {Boolean}
39395      */
39396     isVisible : function(){
39397         return this.visible;
39398     },
39399
39400     /**
39401      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39402      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39403      */
39404     //setCollapsedTitle : function(title){
39405     //    title = title || "&#160;";
39406      //   if(this.collapsedTitleTextEl){
39407       //      this.collapsedTitleTextEl.innerHTML = title;
39408        // }
39409     //},
39410
39411     getBox : function(){
39412         var b;
39413       //  if(!this.collapsed){
39414             b = this.el.getBox(false, true);
39415        // }else{
39416           //  b = this.collapsedEl.getBox(false, true);
39417         //}
39418         return b;
39419     },
39420
39421     getMargins : function(){
39422         return this.margins;
39423         //return this.collapsed ? this.cmargins : this.margins;
39424     },
39425 /*
39426     highlight : function(){
39427         this.el.addClass("x-layout-panel-dragover");
39428     },
39429
39430     unhighlight : function(){
39431         this.el.removeClass("x-layout-panel-dragover");
39432     },
39433 */
39434     updateBox : function(box)
39435     {
39436         if (!this.bodyEl) {
39437             return; // not rendered yet..
39438         }
39439         
39440         this.box = box;
39441         if(!this.collapsed){
39442             this.el.dom.style.left = box.x + "px";
39443             this.el.dom.style.top = box.y + "px";
39444             this.updateBody(box.width, box.height);
39445         }else{
39446             this.collapsedEl.dom.style.left = box.x + "px";
39447             this.collapsedEl.dom.style.top = box.y + "px";
39448             this.collapsedEl.setSize(box.width, box.height);
39449         }
39450         if(this.tabs){
39451             this.tabs.autoSizeTabs();
39452         }
39453     },
39454
39455     updateBody : function(w, h)
39456     {
39457         if(w !== null){
39458             this.el.setWidth(w);
39459             w -= this.el.getBorderWidth("rl");
39460             if(this.config.adjustments){
39461                 w += this.config.adjustments[0];
39462             }
39463         }
39464         if(h !== null && h > 0){
39465             this.el.setHeight(h);
39466             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39467             h -= this.el.getBorderWidth("tb");
39468             if(this.config.adjustments){
39469                 h += this.config.adjustments[1];
39470             }
39471             this.bodyEl.setHeight(h);
39472             if(this.tabs){
39473                 h = this.tabs.syncHeight(h);
39474             }
39475         }
39476         if(this.panelSize){
39477             w = w !== null ? w : this.panelSize.width;
39478             h = h !== null ? h : this.panelSize.height;
39479         }
39480         if(this.activePanel){
39481             var el = this.activePanel.getEl();
39482             w = w !== null ? w : el.getWidth();
39483             h = h !== null ? h : el.getHeight();
39484             this.panelSize = {width: w, height: h};
39485             this.activePanel.setSize(w, h);
39486         }
39487         if(Roo.isIE && this.tabs){
39488             this.tabs.el.repaint();
39489         }
39490     },
39491
39492     /**
39493      * Returns the container element for this region.
39494      * @return {Roo.Element}
39495      */
39496     getEl : function(){
39497         return this.el;
39498     },
39499
39500     /**
39501      * Hides this region.
39502      */
39503     hide : function(){
39504         //if(!this.collapsed){
39505             this.el.dom.style.left = "-2000px";
39506             this.el.hide();
39507         //}else{
39508          //   this.collapsedEl.dom.style.left = "-2000px";
39509          //   this.collapsedEl.hide();
39510        // }
39511         this.visible = false;
39512         this.fireEvent("visibilitychange", this, false);
39513     },
39514
39515     /**
39516      * Shows this region if it was previously hidden.
39517      */
39518     show : function(){
39519         //if(!this.collapsed){
39520             this.el.show();
39521         //}else{
39522         //    this.collapsedEl.show();
39523        // }
39524         this.visible = true;
39525         this.fireEvent("visibilitychange", this, true);
39526     },
39527 /*
39528     closeClicked : function(){
39529         if(this.activePanel){
39530             this.remove(this.activePanel);
39531         }
39532     },
39533
39534     collapseClick : function(e){
39535         if(this.isSlid){
39536            e.stopPropagation();
39537            this.slideIn();
39538         }else{
39539            e.stopPropagation();
39540            this.slideOut();
39541         }
39542     },
39543 */
39544     /**
39545      * Collapses this region.
39546      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39547      */
39548     /*
39549     collapse : function(skipAnim, skipCheck = false){
39550         if(this.collapsed) {
39551             return;
39552         }
39553         
39554         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39555             
39556             this.collapsed = true;
39557             if(this.split){
39558                 this.split.el.hide();
39559             }
39560             if(this.config.animate && skipAnim !== true){
39561                 this.fireEvent("invalidated", this);
39562                 this.animateCollapse();
39563             }else{
39564                 this.el.setLocation(-20000,-20000);
39565                 this.el.hide();
39566                 this.collapsedEl.show();
39567                 this.fireEvent("collapsed", this);
39568                 this.fireEvent("invalidated", this);
39569             }
39570         }
39571         
39572     },
39573 */
39574     animateCollapse : function(){
39575         // overridden
39576     },
39577
39578     /**
39579      * Expands this region if it was previously collapsed.
39580      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39581      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39582      */
39583     /*
39584     expand : function(e, skipAnim){
39585         if(e) {
39586             e.stopPropagation();
39587         }
39588         if(!this.collapsed || this.el.hasActiveFx()) {
39589             return;
39590         }
39591         if(this.isSlid){
39592             this.afterSlideIn();
39593             skipAnim = true;
39594         }
39595         this.collapsed = false;
39596         if(this.config.animate && skipAnim !== true){
39597             this.animateExpand();
39598         }else{
39599             this.el.show();
39600             if(this.split){
39601                 this.split.el.show();
39602             }
39603             this.collapsedEl.setLocation(-2000,-2000);
39604             this.collapsedEl.hide();
39605             this.fireEvent("invalidated", this);
39606             this.fireEvent("expanded", this);
39607         }
39608     },
39609 */
39610     animateExpand : function(){
39611         // overridden
39612     },
39613
39614     initTabs : function()
39615     {
39616         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39617         
39618         var ts = new Roo.bootstrap.panel.Tabs({
39619             el: this.bodyEl.dom,
39620             region : this,
39621             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39622             disableTooltips: this.config.disableTabTips,
39623             toolbar : this.config.toolbar
39624         });
39625         
39626         if(this.config.hideTabs){
39627             ts.stripWrap.setDisplayed(false);
39628         }
39629         this.tabs = ts;
39630         ts.resizeTabs = this.config.resizeTabs === true;
39631         ts.minTabWidth = this.config.minTabWidth || 40;
39632         ts.maxTabWidth = this.config.maxTabWidth || 250;
39633         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39634         ts.monitorResize = false;
39635         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39636         ts.bodyEl.addClass('roo-layout-tabs-body');
39637         this.panels.each(this.initPanelAsTab, this);
39638     },
39639
39640     initPanelAsTab : function(panel){
39641         var ti = this.tabs.addTab(
39642             panel.getEl().id,
39643             panel.getTitle(),
39644             null,
39645             this.config.closeOnTab && panel.isClosable(),
39646             panel.tpl
39647         );
39648         if(panel.tabTip !== undefined){
39649             ti.setTooltip(panel.tabTip);
39650         }
39651         ti.on("activate", function(){
39652               this.setActivePanel(panel);
39653         }, this);
39654         
39655         if(this.config.closeOnTab){
39656             ti.on("beforeclose", function(t, e){
39657                 e.cancel = true;
39658                 this.remove(panel);
39659             }, this);
39660         }
39661         
39662         panel.tabItem = ti;
39663         
39664         return ti;
39665     },
39666
39667     updatePanelTitle : function(panel, title)
39668     {
39669         if(this.activePanel == panel){
39670             this.updateTitle(title);
39671         }
39672         if(this.tabs){
39673             var ti = this.tabs.getTab(panel.getEl().id);
39674             ti.setText(title);
39675             if(panel.tabTip !== undefined){
39676                 ti.setTooltip(panel.tabTip);
39677             }
39678         }
39679     },
39680
39681     updateTitle : function(title){
39682         if(this.titleTextEl && !this.config.title){
39683             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39684         }
39685     },
39686
39687     setActivePanel : function(panel)
39688     {
39689         panel = this.getPanel(panel);
39690         if(this.activePanel && this.activePanel != panel){
39691             if(this.activePanel.setActiveState(false) === false){
39692                 return;
39693             }
39694         }
39695         this.activePanel = panel;
39696         panel.setActiveState(true);
39697         if(this.panelSize){
39698             panel.setSize(this.panelSize.width, this.panelSize.height);
39699         }
39700         if(this.closeBtn){
39701             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39702         }
39703         this.updateTitle(panel.getTitle());
39704         if(this.tabs){
39705             this.fireEvent("invalidated", this);
39706         }
39707         this.fireEvent("panelactivated", this, panel);
39708     },
39709
39710     /**
39711      * Shows the specified panel.
39712      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39713      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39714      */
39715     showPanel : function(panel)
39716     {
39717         panel = this.getPanel(panel);
39718         if(panel){
39719             if(this.tabs){
39720                 var tab = this.tabs.getTab(panel.getEl().id);
39721                 if(tab.isHidden()){
39722                     this.tabs.unhideTab(tab.id);
39723                 }
39724                 tab.activate();
39725             }else{
39726                 this.setActivePanel(panel);
39727             }
39728         }
39729         return panel;
39730     },
39731
39732     /**
39733      * Get the active panel for this region.
39734      * @return {Roo.ContentPanel} The active panel or null
39735      */
39736     getActivePanel : function(){
39737         return this.activePanel;
39738     },
39739
39740     validateVisibility : function(){
39741         if(this.panels.getCount() < 1){
39742             this.updateTitle("&#160;");
39743             this.closeBtn.hide();
39744             this.hide();
39745         }else{
39746             if(!this.isVisible()){
39747                 this.show();
39748             }
39749         }
39750     },
39751
39752     /**
39753      * Adds the passed ContentPanel(s) to this region.
39754      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39755      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39756      */
39757     add : function(panel)
39758     {
39759         if(arguments.length > 1){
39760             for(var i = 0, len = arguments.length; i < len; i++) {
39761                 this.add(arguments[i]);
39762             }
39763             return null;
39764         }
39765         
39766         // if we have not been rendered yet, then we can not really do much of this..
39767         if (!this.bodyEl) {
39768             this.unrendered_panels.push(panel);
39769             return panel;
39770         }
39771         
39772         
39773         
39774         
39775         if(this.hasPanel(panel)){
39776             this.showPanel(panel);
39777             return panel;
39778         }
39779         panel.setRegion(this);
39780         this.panels.add(panel);
39781        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39782             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39783             // and hide them... ???
39784             this.bodyEl.dom.appendChild(panel.getEl().dom);
39785             if(panel.background !== true){
39786                 this.setActivePanel(panel);
39787             }
39788             this.fireEvent("paneladded", this, panel);
39789             return panel;
39790         }
39791         */
39792         if(!this.tabs){
39793             this.initTabs();
39794         }else{
39795             this.initPanelAsTab(panel);
39796         }
39797         
39798         
39799         if(panel.background !== true){
39800             this.tabs.activate(panel.getEl().id);
39801         }
39802         this.fireEvent("paneladded", this, panel);
39803         return panel;
39804     },
39805
39806     /**
39807      * Hides the tab for the specified panel.
39808      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39809      */
39810     hidePanel : function(panel){
39811         if(this.tabs && (panel = this.getPanel(panel))){
39812             this.tabs.hideTab(panel.getEl().id);
39813         }
39814     },
39815
39816     /**
39817      * Unhides the tab for a previously hidden panel.
39818      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39819      */
39820     unhidePanel : function(panel){
39821         if(this.tabs && (panel = this.getPanel(panel))){
39822             this.tabs.unhideTab(panel.getEl().id);
39823         }
39824     },
39825
39826     clearPanels : function(){
39827         while(this.panels.getCount() > 0){
39828              this.remove(this.panels.first());
39829         }
39830     },
39831
39832     /**
39833      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39834      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39835      * @param {Boolean} preservePanel Overrides the config preservePanel option
39836      * @return {Roo.ContentPanel} The panel that was removed
39837      */
39838     remove : function(panel, preservePanel)
39839     {
39840         panel = this.getPanel(panel);
39841         if(!panel){
39842             return null;
39843         }
39844         var e = {};
39845         this.fireEvent("beforeremove", this, panel, e);
39846         if(e.cancel === true){
39847             return null;
39848         }
39849         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39850         var panelId = panel.getId();
39851         this.panels.removeKey(panelId);
39852         if(preservePanel){
39853             document.body.appendChild(panel.getEl().dom);
39854         }
39855         if(this.tabs){
39856             this.tabs.removeTab(panel.getEl().id);
39857         }else if (!preservePanel){
39858             this.bodyEl.dom.removeChild(panel.getEl().dom);
39859         }
39860         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39861             var p = this.panels.first();
39862             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39863             tempEl.appendChild(p.getEl().dom);
39864             this.bodyEl.update("");
39865             this.bodyEl.dom.appendChild(p.getEl().dom);
39866             tempEl = null;
39867             this.updateTitle(p.getTitle());
39868             this.tabs = null;
39869             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39870             this.setActivePanel(p);
39871         }
39872         panel.setRegion(null);
39873         if(this.activePanel == panel){
39874             this.activePanel = null;
39875         }
39876         if(this.config.autoDestroy !== false && preservePanel !== true){
39877             try{panel.destroy();}catch(e){}
39878         }
39879         this.fireEvent("panelremoved", this, panel);
39880         return panel;
39881     },
39882
39883     /**
39884      * Returns the TabPanel component used by this region
39885      * @return {Roo.TabPanel}
39886      */
39887     getTabs : function(){
39888         return this.tabs;
39889     },
39890
39891     createTool : function(parentEl, className){
39892         var btn = Roo.DomHelper.append(parentEl, {
39893             tag: "div",
39894             cls: "x-layout-tools-button",
39895             children: [ {
39896                 tag: "div",
39897                 cls: "roo-layout-tools-button-inner " + className,
39898                 html: "&#160;"
39899             }]
39900         }, true);
39901         btn.addClassOnOver("roo-layout-tools-button-over");
39902         return btn;
39903     }
39904 });/*
39905  * Based on:
39906  * Ext JS Library 1.1.1
39907  * Copyright(c) 2006-2007, Ext JS, LLC.
39908  *
39909  * Originally Released Under LGPL - original licence link has changed is not relivant.
39910  *
39911  * Fork - LGPL
39912  * <script type="text/javascript">
39913  */
39914  
39915
39916
39917 /**
39918  * @class Roo.SplitLayoutRegion
39919  * @extends Roo.LayoutRegion
39920  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39921  */
39922 Roo.bootstrap.layout.Split = function(config){
39923     this.cursor = config.cursor;
39924     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39925 };
39926
39927 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39928 {
39929     splitTip : "Drag to resize.",
39930     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39931     useSplitTips : false,
39932
39933     applyConfig : function(config){
39934         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39935     },
39936     
39937     onRender : function(ctr,pos) {
39938         
39939         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39940         if(!this.config.split){
39941             return;
39942         }
39943         if(!this.split){
39944             
39945             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39946                             tag: "div",
39947                             id: this.el.id + "-split",
39948                             cls: "roo-layout-split roo-layout-split-"+this.position,
39949                             html: "&#160;"
39950             });
39951             /** The SplitBar for this region 
39952             * @type Roo.SplitBar */
39953             // does not exist yet...
39954             Roo.log([this.position, this.orientation]);
39955             
39956             this.split = new Roo.bootstrap.SplitBar({
39957                 dragElement : splitEl,
39958                 resizingElement: this.el,
39959                 orientation : this.orientation
39960             });
39961             
39962             this.split.on("moved", this.onSplitMove, this);
39963             this.split.useShim = this.config.useShim === true;
39964             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39965             if(this.useSplitTips){
39966                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39967             }
39968             //if(config.collapsible){
39969             //    this.split.el.on("dblclick", this.collapse,  this);
39970             //}
39971         }
39972         if(typeof this.config.minSize != "undefined"){
39973             this.split.minSize = this.config.minSize;
39974         }
39975         if(typeof this.config.maxSize != "undefined"){
39976             this.split.maxSize = this.config.maxSize;
39977         }
39978         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39979             this.hideSplitter();
39980         }
39981         
39982     },
39983
39984     getHMaxSize : function(){
39985          var cmax = this.config.maxSize || 10000;
39986          var center = this.mgr.getRegion("center");
39987          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39988     },
39989
39990     getVMaxSize : function(){
39991          var cmax = this.config.maxSize || 10000;
39992          var center = this.mgr.getRegion("center");
39993          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39994     },
39995
39996     onSplitMove : function(split, newSize){
39997         this.fireEvent("resized", this, newSize);
39998     },
39999     
40000     /** 
40001      * Returns the {@link Roo.SplitBar} for this region.
40002      * @return {Roo.SplitBar}
40003      */
40004     getSplitBar : function(){
40005         return this.split;
40006     },
40007     
40008     hide : function(){
40009         this.hideSplitter();
40010         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40011     },
40012
40013     hideSplitter : function(){
40014         if(this.split){
40015             this.split.el.setLocation(-2000,-2000);
40016             this.split.el.hide();
40017         }
40018     },
40019
40020     show : function(){
40021         if(this.split){
40022             this.split.el.show();
40023         }
40024         Roo.bootstrap.layout.Split.superclass.show.call(this);
40025     },
40026     
40027     beforeSlide: function(){
40028         if(Roo.isGecko){// firefox overflow auto bug workaround
40029             this.bodyEl.clip();
40030             if(this.tabs) {
40031                 this.tabs.bodyEl.clip();
40032             }
40033             if(this.activePanel){
40034                 this.activePanel.getEl().clip();
40035                 
40036                 if(this.activePanel.beforeSlide){
40037                     this.activePanel.beforeSlide();
40038                 }
40039             }
40040         }
40041     },
40042     
40043     afterSlide : function(){
40044         if(Roo.isGecko){// firefox overflow auto bug workaround
40045             this.bodyEl.unclip();
40046             if(this.tabs) {
40047                 this.tabs.bodyEl.unclip();
40048             }
40049             if(this.activePanel){
40050                 this.activePanel.getEl().unclip();
40051                 if(this.activePanel.afterSlide){
40052                     this.activePanel.afterSlide();
40053                 }
40054             }
40055         }
40056     },
40057
40058     initAutoHide : function(){
40059         if(this.autoHide !== false){
40060             if(!this.autoHideHd){
40061                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40062                 this.autoHideHd = {
40063                     "mouseout": function(e){
40064                         if(!e.within(this.el, true)){
40065                             st.delay(500);
40066                         }
40067                     },
40068                     "mouseover" : function(e){
40069                         st.cancel();
40070                     },
40071                     scope : this
40072                 };
40073             }
40074             this.el.on(this.autoHideHd);
40075         }
40076     },
40077
40078     clearAutoHide : function(){
40079         if(this.autoHide !== false){
40080             this.el.un("mouseout", this.autoHideHd.mouseout);
40081             this.el.un("mouseover", this.autoHideHd.mouseover);
40082         }
40083     },
40084
40085     clearMonitor : function(){
40086         Roo.get(document).un("click", this.slideInIf, this);
40087     },
40088
40089     // these names are backwards but not changed for compat
40090     slideOut : function(){
40091         if(this.isSlid || this.el.hasActiveFx()){
40092             return;
40093         }
40094         this.isSlid = true;
40095         if(this.collapseBtn){
40096             this.collapseBtn.hide();
40097         }
40098         this.closeBtnState = this.closeBtn.getStyle('display');
40099         this.closeBtn.hide();
40100         if(this.stickBtn){
40101             this.stickBtn.show();
40102         }
40103         this.el.show();
40104         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40105         this.beforeSlide();
40106         this.el.setStyle("z-index", 10001);
40107         this.el.slideIn(this.getSlideAnchor(), {
40108             callback: function(){
40109                 this.afterSlide();
40110                 this.initAutoHide();
40111                 Roo.get(document).on("click", this.slideInIf, this);
40112                 this.fireEvent("slideshow", this);
40113             },
40114             scope: this,
40115             block: true
40116         });
40117     },
40118
40119     afterSlideIn : function(){
40120         this.clearAutoHide();
40121         this.isSlid = false;
40122         this.clearMonitor();
40123         this.el.setStyle("z-index", "");
40124         if(this.collapseBtn){
40125             this.collapseBtn.show();
40126         }
40127         this.closeBtn.setStyle('display', this.closeBtnState);
40128         if(this.stickBtn){
40129             this.stickBtn.hide();
40130         }
40131         this.fireEvent("slidehide", this);
40132     },
40133
40134     slideIn : function(cb){
40135         if(!this.isSlid || this.el.hasActiveFx()){
40136             Roo.callback(cb);
40137             return;
40138         }
40139         this.isSlid = false;
40140         this.beforeSlide();
40141         this.el.slideOut(this.getSlideAnchor(), {
40142             callback: function(){
40143                 this.el.setLeftTop(-10000, -10000);
40144                 this.afterSlide();
40145                 this.afterSlideIn();
40146                 Roo.callback(cb);
40147             },
40148             scope: this,
40149             block: true
40150         });
40151     },
40152     
40153     slideInIf : function(e){
40154         if(!e.within(this.el)){
40155             this.slideIn();
40156         }
40157     },
40158
40159     animateCollapse : function(){
40160         this.beforeSlide();
40161         this.el.setStyle("z-index", 20000);
40162         var anchor = this.getSlideAnchor();
40163         this.el.slideOut(anchor, {
40164             callback : function(){
40165                 this.el.setStyle("z-index", "");
40166                 this.collapsedEl.slideIn(anchor, {duration:.3});
40167                 this.afterSlide();
40168                 this.el.setLocation(-10000,-10000);
40169                 this.el.hide();
40170                 this.fireEvent("collapsed", this);
40171             },
40172             scope: this,
40173             block: true
40174         });
40175     },
40176
40177     animateExpand : function(){
40178         this.beforeSlide();
40179         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40180         this.el.setStyle("z-index", 20000);
40181         this.collapsedEl.hide({
40182             duration:.1
40183         });
40184         this.el.slideIn(this.getSlideAnchor(), {
40185             callback : function(){
40186                 this.el.setStyle("z-index", "");
40187                 this.afterSlide();
40188                 if(this.split){
40189                     this.split.el.show();
40190                 }
40191                 this.fireEvent("invalidated", this);
40192                 this.fireEvent("expanded", this);
40193             },
40194             scope: this,
40195             block: true
40196         });
40197     },
40198
40199     anchors : {
40200         "west" : "left",
40201         "east" : "right",
40202         "north" : "top",
40203         "south" : "bottom"
40204     },
40205
40206     sanchors : {
40207         "west" : "l",
40208         "east" : "r",
40209         "north" : "t",
40210         "south" : "b"
40211     },
40212
40213     canchors : {
40214         "west" : "tl-tr",
40215         "east" : "tr-tl",
40216         "north" : "tl-bl",
40217         "south" : "bl-tl"
40218     },
40219
40220     getAnchor : function(){
40221         return this.anchors[this.position];
40222     },
40223
40224     getCollapseAnchor : function(){
40225         return this.canchors[this.position];
40226     },
40227
40228     getSlideAnchor : function(){
40229         return this.sanchors[this.position];
40230     },
40231
40232     getAlignAdj : function(){
40233         var cm = this.cmargins;
40234         switch(this.position){
40235             case "west":
40236                 return [0, 0];
40237             break;
40238             case "east":
40239                 return [0, 0];
40240             break;
40241             case "north":
40242                 return [0, 0];
40243             break;
40244             case "south":
40245                 return [0, 0];
40246             break;
40247         }
40248     },
40249
40250     getExpandAdj : function(){
40251         var c = this.collapsedEl, cm = this.cmargins;
40252         switch(this.position){
40253             case "west":
40254                 return [-(cm.right+c.getWidth()+cm.left), 0];
40255             break;
40256             case "east":
40257                 return [cm.right+c.getWidth()+cm.left, 0];
40258             break;
40259             case "north":
40260                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40261             break;
40262             case "south":
40263                 return [0, cm.top+cm.bottom+c.getHeight()];
40264             break;
40265         }
40266     }
40267 });/*
40268  * Based on:
40269  * Ext JS Library 1.1.1
40270  * Copyright(c) 2006-2007, Ext JS, LLC.
40271  *
40272  * Originally Released Under LGPL - original licence link has changed is not relivant.
40273  *
40274  * Fork - LGPL
40275  * <script type="text/javascript">
40276  */
40277 /*
40278  * These classes are private internal classes
40279  */
40280 Roo.bootstrap.layout.Center = function(config){
40281     config.region = "center";
40282     Roo.bootstrap.layout.Region.call(this, config);
40283     this.visible = true;
40284     this.minWidth = config.minWidth || 20;
40285     this.minHeight = config.minHeight || 20;
40286 };
40287
40288 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40289     hide : function(){
40290         // center panel can't be hidden
40291     },
40292     
40293     show : function(){
40294         // center panel can't be hidden
40295     },
40296     
40297     getMinWidth: function(){
40298         return this.minWidth;
40299     },
40300     
40301     getMinHeight: function(){
40302         return this.minHeight;
40303     }
40304 });
40305
40306
40307
40308
40309  
40310
40311
40312
40313
40314
40315
40316 Roo.bootstrap.layout.North = function(config)
40317 {
40318     config.region = 'north';
40319     config.cursor = 'n-resize';
40320     
40321     Roo.bootstrap.layout.Split.call(this, config);
40322     
40323     
40324     if(this.split){
40325         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40326         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40327         this.split.el.addClass("roo-layout-split-v");
40328     }
40329     //var size = config.initialSize || config.height;
40330     //if(this.el && typeof size != "undefined"){
40331     //    this.el.setHeight(size);
40332     //}
40333 };
40334 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40335 {
40336     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40337      
40338      
40339     onRender : function(ctr, pos)
40340     {
40341         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40342         var size = this.config.initialSize || this.config.height;
40343         if(this.el && typeof size != "undefined"){
40344             this.el.setHeight(size);
40345         }
40346     
40347     },
40348     
40349     getBox : function(){
40350         if(this.collapsed){
40351             return this.collapsedEl.getBox();
40352         }
40353         var box = this.el.getBox();
40354         if(this.split){
40355             box.height += this.split.el.getHeight();
40356         }
40357         return box;
40358     },
40359     
40360     updateBox : function(box){
40361         if(this.split && !this.collapsed){
40362             box.height -= this.split.el.getHeight();
40363             this.split.el.setLeft(box.x);
40364             this.split.el.setTop(box.y+box.height);
40365             this.split.el.setWidth(box.width);
40366         }
40367         if(this.collapsed){
40368             this.updateBody(box.width, null);
40369         }
40370         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40371     }
40372 });
40373
40374
40375
40376
40377
40378 Roo.bootstrap.layout.South = function(config){
40379     config.region = 'south';
40380     config.cursor = 's-resize';
40381     Roo.bootstrap.layout.Split.call(this, config);
40382     if(this.split){
40383         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40384         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40385         this.split.el.addClass("roo-layout-split-v");
40386     }
40387     
40388 };
40389
40390 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40391     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40392     
40393     onRender : function(ctr, pos)
40394     {
40395         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40396         var size = this.config.initialSize || this.config.height;
40397         if(this.el && typeof size != "undefined"){
40398             this.el.setHeight(size);
40399         }
40400     
40401     },
40402     
40403     getBox : function(){
40404         if(this.collapsed){
40405             return this.collapsedEl.getBox();
40406         }
40407         var box = this.el.getBox();
40408         if(this.split){
40409             var sh = this.split.el.getHeight();
40410             box.height += sh;
40411             box.y -= sh;
40412         }
40413         return box;
40414     },
40415     
40416     updateBox : function(box){
40417         if(this.split && !this.collapsed){
40418             var sh = this.split.el.getHeight();
40419             box.height -= sh;
40420             box.y += sh;
40421             this.split.el.setLeft(box.x);
40422             this.split.el.setTop(box.y-sh);
40423             this.split.el.setWidth(box.width);
40424         }
40425         if(this.collapsed){
40426             this.updateBody(box.width, null);
40427         }
40428         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40429     }
40430 });
40431
40432 Roo.bootstrap.layout.East = function(config){
40433     config.region = "east";
40434     config.cursor = "e-resize";
40435     Roo.bootstrap.layout.Split.call(this, config);
40436     if(this.split){
40437         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40438         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40439         this.split.el.addClass("roo-layout-split-h");
40440     }
40441     
40442 };
40443 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40444     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40445     
40446     onRender : function(ctr, pos)
40447     {
40448         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40449         var size = this.config.initialSize || this.config.width;
40450         if(this.el && typeof size != "undefined"){
40451             this.el.setWidth(size);
40452         }
40453     
40454     },
40455     
40456     getBox : function(){
40457         if(this.collapsed){
40458             return this.collapsedEl.getBox();
40459         }
40460         var box = this.el.getBox();
40461         if(this.split){
40462             var sw = this.split.el.getWidth();
40463             box.width += sw;
40464             box.x -= sw;
40465         }
40466         return box;
40467     },
40468
40469     updateBox : function(box){
40470         if(this.split && !this.collapsed){
40471             var sw = this.split.el.getWidth();
40472             box.width -= sw;
40473             this.split.el.setLeft(box.x);
40474             this.split.el.setTop(box.y);
40475             this.split.el.setHeight(box.height);
40476             box.x += sw;
40477         }
40478         if(this.collapsed){
40479             this.updateBody(null, box.height);
40480         }
40481         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40482     }
40483 });
40484
40485 Roo.bootstrap.layout.West = function(config){
40486     config.region = "west";
40487     config.cursor = "w-resize";
40488     
40489     Roo.bootstrap.layout.Split.call(this, config);
40490     if(this.split){
40491         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40492         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40493         this.split.el.addClass("roo-layout-split-h");
40494     }
40495     
40496 };
40497 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40498     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40499     
40500     onRender: function(ctr, pos)
40501     {
40502         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40503         var size = this.config.initialSize || this.config.width;
40504         if(typeof size != "undefined"){
40505             this.el.setWidth(size);
40506         }
40507     },
40508     
40509     getBox : function(){
40510         if(this.collapsed){
40511             return this.collapsedEl.getBox();
40512         }
40513         var box = this.el.getBox();
40514         if (box.width == 0) {
40515             box.width = this.config.width; // kludge?
40516         }
40517         if(this.split){
40518             box.width += this.split.el.getWidth();
40519         }
40520         return box;
40521     },
40522     
40523     updateBox : function(box){
40524         if(this.split && !this.collapsed){
40525             var sw = this.split.el.getWidth();
40526             box.width -= sw;
40527             this.split.el.setLeft(box.x+box.width);
40528             this.split.el.setTop(box.y);
40529             this.split.el.setHeight(box.height);
40530         }
40531         if(this.collapsed){
40532             this.updateBody(null, box.height);
40533         }
40534         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40535     }
40536 });Roo.namespace("Roo.bootstrap.panel");/*
40537  * Based on:
40538  * Ext JS Library 1.1.1
40539  * Copyright(c) 2006-2007, Ext JS, LLC.
40540  *
40541  * Originally Released Under LGPL - original licence link has changed is not relivant.
40542  *
40543  * Fork - LGPL
40544  * <script type="text/javascript">
40545  */
40546 /**
40547  * @class Roo.ContentPanel
40548  * @extends Roo.util.Observable
40549  * A basic ContentPanel element.
40550  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40551  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40552  * @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
40553  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40554  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40555  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40556  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40557  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40558  * @cfg {String} title          The title for this panel
40559  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40560  * @cfg {String} url            Calls {@link #setUrl} with this value
40561  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40562  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40563  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40564  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40565  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40566  * @cfg {Boolean} badges render the badges
40567  * @cfg {String} cls  extra classes to use  
40568  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40569
40570  * @constructor
40571  * Create a new ContentPanel.
40572  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40573  * @param {String/Object} config A string to set only the title or a config object
40574  * @param {String} content (optional) Set the HTML content for this panel
40575  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40576  */
40577 Roo.bootstrap.panel.Content = function( config){
40578     
40579     this.tpl = config.tpl || false;
40580     
40581     var el = config.el;
40582     var content = config.content;
40583
40584     if(config.autoCreate){ // xtype is available if this is called from factory
40585         el = Roo.id();
40586     }
40587     this.el = Roo.get(el);
40588     if(!this.el && config && config.autoCreate){
40589         if(typeof config.autoCreate == "object"){
40590             if(!config.autoCreate.id){
40591                 config.autoCreate.id = config.id||el;
40592             }
40593             this.el = Roo.DomHelper.append(document.body,
40594                         config.autoCreate, true);
40595         }else{
40596             var elcfg =  {
40597                 tag: "div",
40598                 cls: (config.cls || '') +
40599                     (config.background ? ' bg-' + config.background : '') +
40600                     " roo-layout-inactive-content",
40601                 id: config.id||el
40602             };
40603             if (config.iframe) {
40604                 elcfg.cn = [
40605                     {
40606                         tag : 'iframe',
40607                         style : 'border: 0px',
40608                         src : 'about:blank'
40609                     }
40610                 ];
40611             }
40612               
40613             if (config.html) {
40614                 elcfg.html = config.html;
40615                 
40616             }
40617                         
40618             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40619             if (config.iframe) {
40620                 this.iframeEl = this.el.select('iframe',true).first();
40621             }
40622             
40623         }
40624     } 
40625     this.closable = false;
40626     this.loaded = false;
40627     this.active = false;
40628    
40629       
40630     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40631         
40632         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40633         
40634         this.wrapEl = this.el; //this.el.wrap();
40635         var ti = [];
40636         if (config.toolbar.items) {
40637             ti = config.toolbar.items ;
40638             delete config.toolbar.items ;
40639         }
40640         
40641         var nitems = [];
40642         this.toolbar.render(this.wrapEl, 'before');
40643         for(var i =0;i < ti.length;i++) {
40644           //  Roo.log(['add child', items[i]]);
40645             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40646         }
40647         this.toolbar.items = nitems;
40648         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40649         delete config.toolbar;
40650         
40651     }
40652     /*
40653     // xtype created footer. - not sure if will work as we normally have to render first..
40654     if (this.footer && !this.footer.el && this.footer.xtype) {
40655         if (!this.wrapEl) {
40656             this.wrapEl = this.el.wrap();
40657         }
40658     
40659         this.footer.container = this.wrapEl.createChild();
40660          
40661         this.footer = Roo.factory(this.footer, Roo);
40662         
40663     }
40664     */
40665     
40666      if(typeof config == "string"){
40667         this.title = config;
40668     }else{
40669         Roo.apply(this, config);
40670     }
40671     
40672     if(this.resizeEl){
40673         this.resizeEl = Roo.get(this.resizeEl, true);
40674     }else{
40675         this.resizeEl = this.el;
40676     }
40677     // handle view.xtype
40678     
40679  
40680     
40681     
40682     this.addEvents({
40683         /**
40684          * @event activate
40685          * Fires when this panel is activated. 
40686          * @param {Roo.ContentPanel} this
40687          */
40688         "activate" : true,
40689         /**
40690          * @event deactivate
40691          * Fires when this panel is activated. 
40692          * @param {Roo.ContentPanel} this
40693          */
40694         "deactivate" : true,
40695
40696         /**
40697          * @event resize
40698          * Fires when this panel is resized if fitToFrame is true.
40699          * @param {Roo.ContentPanel} this
40700          * @param {Number} width The width after any component adjustments
40701          * @param {Number} height The height after any component adjustments
40702          */
40703         "resize" : true,
40704         
40705          /**
40706          * @event render
40707          * Fires when this tab is created
40708          * @param {Roo.ContentPanel} this
40709          */
40710         "render" : true,
40711         
40712           /**
40713          * @event scroll
40714          * Fires when this content is scrolled
40715          * @param {Roo.ContentPanel} this
40716          * @param {Event} scrollEvent
40717          */
40718         "scroll" : true
40719         
40720         
40721         
40722     });
40723     
40724
40725     
40726     
40727     if(this.autoScroll && !this.iframe){
40728         this.resizeEl.setStyle("overflow", "auto");
40729         this.resizeEl.on('scroll', this.onScroll, this);
40730     } else {
40731         // fix randome scrolling
40732         //this.el.on('scroll', function() {
40733         //    Roo.log('fix random scolling');
40734         //    this.scrollTo('top',0); 
40735         //});
40736     }
40737     content = content || this.content;
40738     if(content){
40739         this.setContent(content);
40740     }
40741     if(config && config.url){
40742         this.setUrl(this.url, this.params, this.loadOnce);
40743     }
40744     
40745     
40746     
40747     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40748     
40749     if (this.view && typeof(this.view.xtype) != 'undefined') {
40750         this.view.el = this.el.appendChild(document.createElement("div"));
40751         this.view = Roo.factory(this.view); 
40752         this.view.render  &&  this.view.render(false, '');  
40753     }
40754     
40755     
40756     this.fireEvent('render', this);
40757 };
40758
40759 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40760     
40761     cls : '',
40762     background : '',
40763     
40764     tabTip : '',
40765     
40766     iframe : false,
40767     iframeEl : false,
40768     
40769     /* Resize Element - use this to work out scroll etc. */
40770     resizeEl : false,
40771     
40772     setRegion : function(region){
40773         this.region = region;
40774         this.setActiveClass(region && !this.background);
40775     },
40776     
40777     
40778     setActiveClass: function(state)
40779     {
40780         if(state){
40781            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40782            this.el.setStyle('position','relative');
40783         }else{
40784            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40785            this.el.setStyle('position', 'absolute');
40786         } 
40787     },
40788     
40789     /**
40790      * Returns the toolbar for this Panel if one was configured. 
40791      * @return {Roo.Toolbar} 
40792      */
40793     getToolbar : function(){
40794         return this.toolbar;
40795     },
40796     
40797     setActiveState : function(active)
40798     {
40799         this.active = active;
40800         this.setActiveClass(active);
40801         if(!active){
40802             if(this.fireEvent("deactivate", this) === false){
40803                 return false;
40804             }
40805             return true;
40806         }
40807         this.fireEvent("activate", this);
40808         return true;
40809     },
40810     /**
40811      * Updates this panel's element (not for iframe)
40812      * @param {String} content The new content
40813      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40814     */
40815     setContent : function(content, loadScripts){
40816         if (this.iframe) {
40817             return;
40818         }
40819         
40820         this.el.update(content, loadScripts);
40821     },
40822
40823     ignoreResize : function(w, h){
40824         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40825             return true;
40826         }else{
40827             this.lastSize = {width: w, height: h};
40828             return false;
40829         }
40830     },
40831     /**
40832      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40833      * @return {Roo.UpdateManager} The UpdateManager
40834      */
40835     getUpdateManager : function(){
40836         if (this.iframe) {
40837             return false;
40838         }
40839         return this.el.getUpdateManager();
40840     },
40841      /**
40842      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40843      * Does not work with IFRAME contents
40844      * @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:
40845 <pre><code>
40846 panel.load({
40847     url: "your-url.php",
40848     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40849     callback: yourFunction,
40850     scope: yourObject, //(optional scope)
40851     discardUrl: false,
40852     nocache: false,
40853     text: "Loading...",
40854     timeout: 30,
40855     scripts: false
40856 });
40857 </code></pre>
40858      
40859      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40860      * 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.
40861      * @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}
40862      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40863      * @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.
40864      * @return {Roo.ContentPanel} this
40865      */
40866     load : function(){
40867         
40868         if (this.iframe) {
40869             return this;
40870         }
40871         
40872         var um = this.el.getUpdateManager();
40873         um.update.apply(um, arguments);
40874         return this;
40875     },
40876
40877
40878     /**
40879      * 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.
40880      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40881      * @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)
40882      * @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)
40883      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40884      */
40885     setUrl : function(url, params, loadOnce){
40886         if (this.iframe) {
40887             this.iframeEl.dom.src = url;
40888             return false;
40889         }
40890         
40891         if(this.refreshDelegate){
40892             this.removeListener("activate", this.refreshDelegate);
40893         }
40894         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40895         this.on("activate", this.refreshDelegate);
40896         return this.el.getUpdateManager();
40897     },
40898     
40899     _handleRefresh : function(url, params, loadOnce){
40900         if(!loadOnce || !this.loaded){
40901             var updater = this.el.getUpdateManager();
40902             updater.update(url, params, this._setLoaded.createDelegate(this));
40903         }
40904     },
40905     
40906     _setLoaded : function(){
40907         this.loaded = true;
40908     }, 
40909     
40910     /**
40911      * Returns this panel's id
40912      * @return {String} 
40913      */
40914     getId : function(){
40915         return this.el.id;
40916     },
40917     
40918     /** 
40919      * Returns this panel's element - used by regiosn to add.
40920      * @return {Roo.Element} 
40921      */
40922     getEl : function(){
40923         return this.wrapEl || this.el;
40924     },
40925     
40926    
40927     
40928     adjustForComponents : function(width, height)
40929     {
40930         //Roo.log('adjustForComponents ');
40931         if(this.resizeEl != this.el){
40932             width -= this.el.getFrameWidth('lr');
40933             height -= this.el.getFrameWidth('tb');
40934         }
40935         if(this.toolbar){
40936             var te = this.toolbar.getEl();
40937             te.setWidth(width);
40938             height -= te.getHeight();
40939         }
40940         if(this.footer){
40941             var te = this.footer.getEl();
40942             te.setWidth(width);
40943             height -= te.getHeight();
40944         }
40945         
40946         
40947         if(this.adjustments){
40948             width += this.adjustments[0];
40949             height += this.adjustments[1];
40950         }
40951         return {"width": width, "height": height};
40952     },
40953     
40954     setSize : function(width, height){
40955         if(this.fitToFrame && !this.ignoreResize(width, height)){
40956             if(this.fitContainer && this.resizeEl != this.el){
40957                 this.el.setSize(width, height);
40958             }
40959             var size = this.adjustForComponents(width, height);
40960             if (this.iframe) {
40961                 this.iframeEl.setSize(width,height);
40962             }
40963             
40964             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40965             this.fireEvent('resize', this, size.width, size.height);
40966             
40967             
40968         }
40969     },
40970     
40971     /**
40972      * Returns this panel's title
40973      * @return {String} 
40974      */
40975     getTitle : function(){
40976         
40977         if (typeof(this.title) != 'object') {
40978             return this.title;
40979         }
40980         
40981         var t = '';
40982         for (var k in this.title) {
40983             if (!this.title.hasOwnProperty(k)) {
40984                 continue;
40985             }
40986             
40987             if (k.indexOf('-') >= 0) {
40988                 var s = k.split('-');
40989                 for (var i = 0; i<s.length; i++) {
40990                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40991                 }
40992             } else {
40993                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40994             }
40995         }
40996         return t;
40997     },
40998     
40999     /**
41000      * Set this panel's title
41001      * @param {String} title
41002      */
41003     setTitle : function(title){
41004         this.title = title;
41005         if(this.region){
41006             this.region.updatePanelTitle(this, title);
41007         }
41008     },
41009     
41010     /**
41011      * Returns true is this panel was configured to be closable
41012      * @return {Boolean} 
41013      */
41014     isClosable : function(){
41015         return this.closable;
41016     },
41017     
41018     beforeSlide : function(){
41019         this.el.clip();
41020         this.resizeEl.clip();
41021     },
41022     
41023     afterSlide : function(){
41024         this.el.unclip();
41025         this.resizeEl.unclip();
41026     },
41027     
41028     /**
41029      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41030      *   Will fail silently if the {@link #setUrl} method has not been called.
41031      *   This does not activate the panel, just updates its content.
41032      */
41033     refresh : function(){
41034         if(this.refreshDelegate){
41035            this.loaded = false;
41036            this.refreshDelegate();
41037         }
41038     },
41039     
41040     /**
41041      * Destroys this panel
41042      */
41043     destroy : function(){
41044         this.el.removeAllListeners();
41045         var tempEl = document.createElement("span");
41046         tempEl.appendChild(this.el.dom);
41047         tempEl.innerHTML = "";
41048         this.el.remove();
41049         this.el = null;
41050     },
41051     
41052     /**
41053      * form - if the content panel contains a form - this is a reference to it.
41054      * @type {Roo.form.Form}
41055      */
41056     form : false,
41057     /**
41058      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41059      *    This contains a reference to it.
41060      * @type {Roo.View}
41061      */
41062     view : false,
41063     
41064       /**
41065      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41066      * <pre><code>
41067
41068 layout.addxtype({
41069        xtype : 'Form',
41070        items: [ .... ]
41071    }
41072 );
41073
41074 </code></pre>
41075      * @param {Object} cfg Xtype definition of item to add.
41076      */
41077     
41078     
41079     getChildContainer: function () {
41080         return this.getEl();
41081     },
41082     
41083     
41084     onScroll : function(e)
41085     {
41086         this.fireEvent('scroll', this, e);
41087     }
41088     
41089     
41090     /*
41091         var  ret = new Roo.factory(cfg);
41092         return ret;
41093         
41094         
41095         // add form..
41096         if (cfg.xtype.match(/^Form$/)) {
41097             
41098             var el;
41099             //if (this.footer) {
41100             //    el = this.footer.container.insertSibling(false, 'before');
41101             //} else {
41102                 el = this.el.createChild();
41103             //}
41104
41105             this.form = new  Roo.form.Form(cfg);
41106             
41107             
41108             if ( this.form.allItems.length) {
41109                 this.form.render(el.dom);
41110             }
41111             return this.form;
41112         }
41113         // should only have one of theses..
41114         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41115             // views.. should not be just added - used named prop 'view''
41116             
41117             cfg.el = this.el.appendChild(document.createElement("div"));
41118             // factory?
41119             
41120             var ret = new Roo.factory(cfg);
41121              
41122              ret.render && ret.render(false, ''); // render blank..
41123             this.view = ret;
41124             return ret;
41125         }
41126         return false;
41127     }
41128     \*/
41129 });
41130  
41131 /**
41132  * @class Roo.bootstrap.panel.Grid
41133  * @extends Roo.bootstrap.panel.Content
41134  * @constructor
41135  * Create a new GridPanel.
41136  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41137  * @param {Object} config A the config object
41138   
41139  */
41140
41141
41142
41143 Roo.bootstrap.panel.Grid = function(config)
41144 {
41145     
41146       
41147     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41148         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41149
41150     config.el = this.wrapper;
41151     //this.el = this.wrapper;
41152     
41153       if (config.container) {
41154         // ctor'ed from a Border/panel.grid
41155         
41156         
41157         this.wrapper.setStyle("overflow", "hidden");
41158         this.wrapper.addClass('roo-grid-container');
41159
41160     }
41161     
41162     
41163     if(config.toolbar){
41164         var tool_el = this.wrapper.createChild();    
41165         this.toolbar = Roo.factory(config.toolbar);
41166         var ti = [];
41167         if (config.toolbar.items) {
41168             ti = config.toolbar.items ;
41169             delete config.toolbar.items ;
41170         }
41171         
41172         var nitems = [];
41173         this.toolbar.render(tool_el);
41174         for(var i =0;i < ti.length;i++) {
41175           //  Roo.log(['add child', items[i]]);
41176             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41177         }
41178         this.toolbar.items = nitems;
41179         
41180         delete config.toolbar;
41181     }
41182     
41183     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41184     config.grid.scrollBody = true;;
41185     config.grid.monitorWindowResize = false; // turn off autosizing
41186     config.grid.autoHeight = false;
41187     config.grid.autoWidth = false;
41188     
41189     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41190     
41191     if (config.background) {
41192         // render grid on panel activation (if panel background)
41193         this.on('activate', function(gp) {
41194             if (!gp.grid.rendered) {
41195                 gp.grid.render(this.wrapper);
41196                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41197             }
41198         });
41199             
41200     } else {
41201         this.grid.render(this.wrapper);
41202         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41203
41204     }
41205     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41206     // ??? needed ??? config.el = this.wrapper;
41207     
41208     
41209     
41210   
41211     // xtype created footer. - not sure if will work as we normally have to render first..
41212     if (this.footer && !this.footer.el && this.footer.xtype) {
41213         
41214         var ctr = this.grid.getView().getFooterPanel(true);
41215         this.footer.dataSource = this.grid.dataSource;
41216         this.footer = Roo.factory(this.footer, Roo);
41217         this.footer.render(ctr);
41218         
41219     }
41220     
41221     
41222     
41223     
41224      
41225 };
41226
41227 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41228     getId : function(){
41229         return this.grid.id;
41230     },
41231     
41232     /**
41233      * Returns the grid for this panel
41234      * @return {Roo.bootstrap.Table} 
41235      */
41236     getGrid : function(){
41237         return this.grid;    
41238     },
41239     
41240     setSize : function(width, height){
41241         if(!this.ignoreResize(width, height)){
41242             var grid = this.grid;
41243             var size = this.adjustForComponents(width, height);
41244             // tfoot is not a footer?
41245           
41246             
41247             var gridel = grid.getGridEl();
41248             gridel.setSize(size.width, size.height);
41249             
41250             var tbd = grid.getGridEl().select('tbody', true).first();
41251             var thd = grid.getGridEl().select('thead',true).first();
41252             var tbf= grid.getGridEl().select('tfoot', true).first();
41253
41254             if (tbf) {
41255                 size.height -= tbf.getHeight();
41256             }
41257             if (thd) {
41258                 size.height -= thd.getHeight();
41259             }
41260             
41261             tbd.setSize(size.width, size.height );
41262             // this is for the account management tab -seems to work there.
41263             var thd = grid.getGridEl().select('thead',true).first();
41264             //if (tbd) {
41265             //    tbd.setSize(size.width, size.height - thd.getHeight());
41266             //}
41267              
41268             grid.autoSize();
41269         }
41270     },
41271      
41272     
41273     
41274     beforeSlide : function(){
41275         this.grid.getView().scroller.clip();
41276     },
41277     
41278     afterSlide : function(){
41279         this.grid.getView().scroller.unclip();
41280     },
41281     
41282     destroy : function(){
41283         this.grid.destroy();
41284         delete this.grid;
41285         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41286     }
41287 });
41288
41289 /**
41290  * @class Roo.bootstrap.panel.Nest
41291  * @extends Roo.bootstrap.panel.Content
41292  * @constructor
41293  * Create a new Panel, that can contain a layout.Border.
41294  * 
41295  * 
41296  * @param {Roo.BorderLayout} layout The layout for this panel
41297  * @param {String/Object} config A string to set only the title or a config object
41298  */
41299 Roo.bootstrap.panel.Nest = function(config)
41300 {
41301     // construct with only one argument..
41302     /* FIXME - implement nicer consturctors
41303     if (layout.layout) {
41304         config = layout;
41305         layout = config.layout;
41306         delete config.layout;
41307     }
41308     if (layout.xtype && !layout.getEl) {
41309         // then layout needs constructing..
41310         layout = Roo.factory(layout, Roo);
41311     }
41312     */
41313     
41314     config.el =  config.layout.getEl();
41315     
41316     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41317     
41318     config.layout.monitorWindowResize = false; // turn off autosizing
41319     this.layout = config.layout;
41320     this.layout.getEl().addClass("roo-layout-nested-layout");
41321     this.layout.parent = this;
41322     
41323     
41324     
41325     
41326 };
41327
41328 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41329
41330     setSize : function(width, height){
41331         if(!this.ignoreResize(width, height)){
41332             var size = this.adjustForComponents(width, height);
41333             var el = this.layout.getEl();
41334             if (size.height < 1) {
41335                 el.setWidth(size.width);   
41336             } else {
41337                 el.setSize(size.width, size.height);
41338             }
41339             var touch = el.dom.offsetWidth;
41340             this.layout.layout();
41341             // ie requires a double layout on the first pass
41342             if(Roo.isIE && !this.initialized){
41343                 this.initialized = true;
41344                 this.layout.layout();
41345             }
41346         }
41347     },
41348     
41349     // activate all subpanels if not currently active..
41350     
41351     setActiveState : function(active){
41352         this.active = active;
41353         this.setActiveClass(active);
41354         
41355         if(!active){
41356             this.fireEvent("deactivate", this);
41357             return;
41358         }
41359         
41360         this.fireEvent("activate", this);
41361         // not sure if this should happen before or after..
41362         if (!this.layout) {
41363             return; // should not happen..
41364         }
41365         var reg = false;
41366         for (var r in this.layout.regions) {
41367             reg = this.layout.getRegion(r);
41368             if (reg.getActivePanel()) {
41369                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41370                 reg.setActivePanel(reg.getActivePanel());
41371                 continue;
41372             }
41373             if (!reg.panels.length) {
41374                 continue;
41375             }
41376             reg.showPanel(reg.getPanel(0));
41377         }
41378         
41379         
41380         
41381         
41382     },
41383     
41384     /**
41385      * Returns the nested BorderLayout for this panel
41386      * @return {Roo.BorderLayout} 
41387      */
41388     getLayout : function(){
41389         return this.layout;
41390     },
41391     
41392      /**
41393      * Adds a xtype elements to the layout of the nested panel
41394      * <pre><code>
41395
41396 panel.addxtype({
41397        xtype : 'ContentPanel',
41398        region: 'west',
41399        items: [ .... ]
41400    }
41401 );
41402
41403 panel.addxtype({
41404         xtype : 'NestedLayoutPanel',
41405         region: 'west',
41406         layout: {
41407            center: { },
41408            west: { }   
41409         },
41410         items : [ ... list of content panels or nested layout panels.. ]
41411    }
41412 );
41413 </code></pre>
41414      * @param {Object} cfg Xtype definition of item to add.
41415      */
41416     addxtype : function(cfg) {
41417         return this.layout.addxtype(cfg);
41418     
41419     }
41420 });/*
41421  * Based on:
41422  * Ext JS Library 1.1.1
41423  * Copyright(c) 2006-2007, Ext JS, LLC.
41424  *
41425  * Originally Released Under LGPL - original licence link has changed is not relivant.
41426  *
41427  * Fork - LGPL
41428  * <script type="text/javascript">
41429  */
41430 /**
41431  * @class Roo.TabPanel
41432  * @extends Roo.util.Observable
41433  * A lightweight tab container.
41434  * <br><br>
41435  * Usage:
41436  * <pre><code>
41437 // basic tabs 1, built from existing content
41438 var tabs = new Roo.TabPanel("tabs1");
41439 tabs.addTab("script", "View Script");
41440 tabs.addTab("markup", "View Markup");
41441 tabs.activate("script");
41442
41443 // more advanced tabs, built from javascript
41444 var jtabs = new Roo.TabPanel("jtabs");
41445 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41446
41447 // set up the UpdateManager
41448 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41449 var updater = tab2.getUpdateManager();
41450 updater.setDefaultUrl("ajax1.htm");
41451 tab2.on('activate', updater.refresh, updater, true);
41452
41453 // Use setUrl for Ajax loading
41454 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41455 tab3.setUrl("ajax2.htm", null, true);
41456
41457 // Disabled tab
41458 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41459 tab4.disable();
41460
41461 jtabs.activate("jtabs-1");
41462  * </code></pre>
41463  * @constructor
41464  * Create a new TabPanel.
41465  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41466  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41467  */
41468 Roo.bootstrap.panel.Tabs = function(config){
41469     /**
41470     * The container element for this TabPanel.
41471     * @type Roo.Element
41472     */
41473     this.el = Roo.get(config.el);
41474     delete config.el;
41475     if(config){
41476         if(typeof config == "boolean"){
41477             this.tabPosition = config ? "bottom" : "top";
41478         }else{
41479             Roo.apply(this, config);
41480         }
41481     }
41482     
41483     if(this.tabPosition == "bottom"){
41484         // if tabs are at the bottom = create the body first.
41485         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41486         this.el.addClass("roo-tabs-bottom");
41487     }
41488     // next create the tabs holders
41489     
41490     if (this.tabPosition == "west"){
41491         
41492         var reg = this.region; // fake it..
41493         while (reg) {
41494             if (!reg.mgr.parent) {
41495                 break;
41496             }
41497             reg = reg.mgr.parent.region;
41498         }
41499         Roo.log("got nest?");
41500         Roo.log(reg);
41501         if (reg.mgr.getRegion('west')) {
41502             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41503             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41504             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41505             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41506             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41507         
41508             
41509         }
41510         
41511         
41512     } else {
41513      
41514         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41515         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41516         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41517         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41518     }
41519     
41520     
41521     if(Roo.isIE){
41522         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41523     }
41524     
41525     // finally - if tabs are at the top, then create the body last..
41526     if(this.tabPosition != "bottom"){
41527         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41528          * @type Roo.Element
41529          */
41530         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41531         this.el.addClass("roo-tabs-top");
41532     }
41533     this.items = [];
41534
41535     this.bodyEl.setStyle("position", "relative");
41536
41537     this.active = null;
41538     this.activateDelegate = this.activate.createDelegate(this);
41539
41540     this.addEvents({
41541         /**
41542          * @event tabchange
41543          * Fires when the active tab changes
41544          * @param {Roo.TabPanel} this
41545          * @param {Roo.TabPanelItem} activePanel The new active tab
41546          */
41547         "tabchange": true,
41548         /**
41549          * @event beforetabchange
41550          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41551          * @param {Roo.TabPanel} this
41552          * @param {Object} e Set cancel to true on this object to cancel the tab change
41553          * @param {Roo.TabPanelItem} tab The tab being changed to
41554          */
41555         "beforetabchange" : true
41556     });
41557
41558     Roo.EventManager.onWindowResize(this.onResize, this);
41559     this.cpad = this.el.getPadding("lr");
41560     this.hiddenCount = 0;
41561
41562
41563     // toolbar on the tabbar support...
41564     if (this.toolbar) {
41565         alert("no toolbar support yet");
41566         this.toolbar  = false;
41567         /*
41568         var tcfg = this.toolbar;
41569         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41570         this.toolbar = new Roo.Toolbar(tcfg);
41571         if (Roo.isSafari) {
41572             var tbl = tcfg.container.child('table', true);
41573             tbl.setAttribute('width', '100%');
41574         }
41575         */
41576         
41577     }
41578    
41579
41580
41581     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41582 };
41583
41584 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41585     /*
41586      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41587      */
41588     tabPosition : "top",
41589     /*
41590      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41591      */
41592     currentTabWidth : 0,
41593     /*
41594      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41595      */
41596     minTabWidth : 40,
41597     /*
41598      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41599      */
41600     maxTabWidth : 250,
41601     /*
41602      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41603      */
41604     preferredTabWidth : 175,
41605     /*
41606      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41607      */
41608     resizeTabs : false,
41609     /*
41610      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41611      */
41612     monitorResize : true,
41613     /*
41614      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41615      */
41616     toolbar : false,  // set by caller..
41617     
41618     region : false, /// set by caller
41619     
41620     disableTooltips : true, // not used yet...
41621
41622     /**
41623      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41624      * @param {String} id The id of the div to use <b>or create</b>
41625      * @param {String} text The text for the tab
41626      * @param {String} content (optional) Content to put in the TabPanelItem body
41627      * @param {Boolean} closable (optional) True to create a close icon on the tab
41628      * @return {Roo.TabPanelItem} The created TabPanelItem
41629      */
41630     addTab : function(id, text, content, closable, tpl)
41631     {
41632         var item = new Roo.bootstrap.panel.TabItem({
41633             panel: this,
41634             id : id,
41635             text : text,
41636             closable : closable,
41637             tpl : tpl
41638         });
41639         this.addTabItem(item);
41640         if(content){
41641             item.setContent(content);
41642         }
41643         return item;
41644     },
41645
41646     /**
41647      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41648      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41649      * @return {Roo.TabPanelItem}
41650      */
41651     getTab : function(id){
41652         return this.items[id];
41653     },
41654
41655     /**
41656      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41657      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41658      */
41659     hideTab : function(id){
41660         var t = this.items[id];
41661         if(!t.isHidden()){
41662            t.setHidden(true);
41663            this.hiddenCount++;
41664            this.autoSizeTabs();
41665         }
41666     },
41667
41668     /**
41669      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41670      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41671      */
41672     unhideTab : function(id){
41673         var t = this.items[id];
41674         if(t.isHidden()){
41675            t.setHidden(false);
41676            this.hiddenCount--;
41677            this.autoSizeTabs();
41678         }
41679     },
41680
41681     /**
41682      * Adds an existing {@link Roo.TabPanelItem}.
41683      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41684      */
41685     addTabItem : function(item)
41686     {
41687         this.items[item.id] = item;
41688         this.items.push(item);
41689         this.autoSizeTabs();
41690       //  if(this.resizeTabs){
41691     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41692   //         this.autoSizeTabs();
41693 //        }else{
41694 //            item.autoSize();
41695        // }
41696     },
41697
41698     /**
41699      * Removes a {@link Roo.TabPanelItem}.
41700      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41701      */
41702     removeTab : function(id){
41703         var items = this.items;
41704         var tab = items[id];
41705         if(!tab) { return; }
41706         var index = items.indexOf(tab);
41707         if(this.active == tab && items.length > 1){
41708             var newTab = this.getNextAvailable(index);
41709             if(newTab) {
41710                 newTab.activate();
41711             }
41712         }
41713         this.stripEl.dom.removeChild(tab.pnode.dom);
41714         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41715             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41716         }
41717         items.splice(index, 1);
41718         delete this.items[tab.id];
41719         tab.fireEvent("close", tab);
41720         tab.purgeListeners();
41721         this.autoSizeTabs();
41722     },
41723
41724     getNextAvailable : function(start){
41725         var items = this.items;
41726         var index = start;
41727         // look for a next tab that will slide over to
41728         // replace the one being removed
41729         while(index < items.length){
41730             var item = items[++index];
41731             if(item && !item.isHidden()){
41732                 return item;
41733             }
41734         }
41735         // if one isn't found select the previous tab (on the left)
41736         index = start;
41737         while(index >= 0){
41738             var item = items[--index];
41739             if(item && !item.isHidden()){
41740                 return item;
41741             }
41742         }
41743         return null;
41744     },
41745
41746     /**
41747      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41748      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41749      */
41750     disableTab : function(id){
41751         var tab = this.items[id];
41752         if(tab && this.active != tab){
41753             tab.disable();
41754         }
41755     },
41756
41757     /**
41758      * Enables a {@link Roo.TabPanelItem} that is disabled.
41759      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41760      */
41761     enableTab : function(id){
41762         var tab = this.items[id];
41763         tab.enable();
41764     },
41765
41766     /**
41767      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41768      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41769      * @return {Roo.TabPanelItem} The TabPanelItem.
41770      */
41771     activate : function(id)
41772     {
41773         //Roo.log('activite:'  + id);
41774         
41775         var tab = this.items[id];
41776         if(!tab){
41777             return null;
41778         }
41779         if(tab == this.active || tab.disabled){
41780             return tab;
41781         }
41782         var e = {};
41783         this.fireEvent("beforetabchange", this, e, tab);
41784         if(e.cancel !== true && !tab.disabled){
41785             if(this.active){
41786                 this.active.hide();
41787             }
41788             this.active = this.items[id];
41789             this.active.show();
41790             this.fireEvent("tabchange", this, this.active);
41791         }
41792         return tab;
41793     },
41794
41795     /**
41796      * Gets the active {@link Roo.TabPanelItem}.
41797      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41798      */
41799     getActiveTab : function(){
41800         return this.active;
41801     },
41802
41803     /**
41804      * Updates the tab body element to fit the height of the container element
41805      * for overflow scrolling
41806      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41807      */
41808     syncHeight : function(targetHeight){
41809         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41810         var bm = this.bodyEl.getMargins();
41811         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41812         this.bodyEl.setHeight(newHeight);
41813         return newHeight;
41814     },
41815
41816     onResize : function(){
41817         if(this.monitorResize){
41818             this.autoSizeTabs();
41819         }
41820     },
41821
41822     /**
41823      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41824      */
41825     beginUpdate : function(){
41826         this.updating = true;
41827     },
41828
41829     /**
41830      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41831      */
41832     endUpdate : function(){
41833         this.updating = false;
41834         this.autoSizeTabs();
41835     },
41836
41837     /**
41838      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41839      */
41840     autoSizeTabs : function()
41841     {
41842         var count = this.items.length;
41843         var vcount = count - this.hiddenCount;
41844         
41845         if (vcount < 2) {
41846             this.stripEl.hide();
41847         } else {
41848             this.stripEl.show();
41849         }
41850         
41851         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41852             return;
41853         }
41854         
41855         
41856         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41857         var availWidth = Math.floor(w / vcount);
41858         var b = this.stripBody;
41859         if(b.getWidth() > w){
41860             var tabs = this.items;
41861             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41862             if(availWidth < this.minTabWidth){
41863                 /*if(!this.sleft){    // incomplete scrolling code
41864                     this.createScrollButtons();
41865                 }
41866                 this.showScroll();
41867                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41868             }
41869         }else{
41870             if(this.currentTabWidth < this.preferredTabWidth){
41871                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41872             }
41873         }
41874     },
41875
41876     /**
41877      * Returns the number of tabs in this TabPanel.
41878      * @return {Number}
41879      */
41880      getCount : function(){
41881          return this.items.length;
41882      },
41883
41884     /**
41885      * Resizes all the tabs to the passed width
41886      * @param {Number} The new width
41887      */
41888     setTabWidth : function(width){
41889         this.currentTabWidth = width;
41890         for(var i = 0, len = this.items.length; i < len; i++) {
41891                 if(!this.items[i].isHidden()) {
41892                 this.items[i].setWidth(width);
41893             }
41894         }
41895     },
41896
41897     /**
41898      * Destroys this TabPanel
41899      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41900      */
41901     destroy : function(removeEl){
41902         Roo.EventManager.removeResizeListener(this.onResize, this);
41903         for(var i = 0, len = this.items.length; i < len; i++){
41904             this.items[i].purgeListeners();
41905         }
41906         if(removeEl === true){
41907             this.el.update("");
41908             this.el.remove();
41909         }
41910     },
41911     
41912     createStrip : function(container)
41913     {
41914         var strip = document.createElement("nav");
41915         strip.className = Roo.bootstrap.version == 4 ?
41916             "navbar-light bg-light" : 
41917             "navbar navbar-default"; //"x-tabs-wrap";
41918         container.appendChild(strip);
41919         return strip;
41920     },
41921     
41922     createStripList : function(strip)
41923     {
41924         // div wrapper for retard IE
41925         // returns the "tr" element.
41926         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41927         //'<div class="x-tabs-strip-wrap">'+
41928           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41929           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41930         return strip.firstChild; //.firstChild.firstChild.firstChild;
41931     },
41932     createBody : function(container)
41933     {
41934         var body = document.createElement("div");
41935         Roo.id(body, "tab-body");
41936         //Roo.fly(body).addClass("x-tabs-body");
41937         Roo.fly(body).addClass("tab-content");
41938         container.appendChild(body);
41939         return body;
41940     },
41941     createItemBody :function(bodyEl, id){
41942         var body = Roo.getDom(id);
41943         if(!body){
41944             body = document.createElement("div");
41945             body.id = id;
41946         }
41947         //Roo.fly(body).addClass("x-tabs-item-body");
41948         Roo.fly(body).addClass("tab-pane");
41949          bodyEl.insertBefore(body, bodyEl.firstChild);
41950         return body;
41951     },
41952     /** @private */
41953     createStripElements :  function(stripEl, text, closable, tpl)
41954     {
41955         var td = document.createElement("li"); // was td..
41956         td.className = 'nav-item';
41957         
41958         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41959         
41960         
41961         stripEl.appendChild(td);
41962         /*if(closable){
41963             td.className = "x-tabs-closable";
41964             if(!this.closeTpl){
41965                 this.closeTpl = new Roo.Template(
41966                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41967                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41968                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41969                 );
41970             }
41971             var el = this.closeTpl.overwrite(td, {"text": text});
41972             var close = el.getElementsByTagName("div")[0];
41973             var inner = el.getElementsByTagName("em")[0];
41974             return {"el": el, "close": close, "inner": inner};
41975         } else {
41976         */
41977         // not sure what this is..
41978 //            if(!this.tabTpl){
41979                 //this.tabTpl = new Roo.Template(
41980                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41981                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41982                 //);
41983 //                this.tabTpl = new Roo.Template(
41984 //                   '<a href="#">' +
41985 //                   '<span unselectable="on"' +
41986 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41987 //                            ' >{text}</span></a>'
41988 //                );
41989 //                
41990 //            }
41991
41992
41993             var template = tpl || this.tabTpl || false;
41994             
41995             if(!template){
41996                 template =  new Roo.Template(
41997                         Roo.bootstrap.version == 4 ? 
41998                             (
41999                                 '<a class="nav-link" href="#" unselectable="on"' +
42000                                      (this.disableTooltips ? '' : ' title="{text}"') +
42001                                      ' >{text}</a>'
42002                             ) : (
42003                                 '<a class="nav-link" href="#">' +
42004                                 '<span unselectable="on"' +
42005                                          (this.disableTooltips ? '' : ' title="{text}"') +
42006                                     ' >{text}</span></a>'
42007                             )
42008                 );
42009             }
42010             
42011             switch (typeof(template)) {
42012                 case 'object' :
42013                     break;
42014                 case 'string' :
42015                     template = new Roo.Template(template);
42016                     break;
42017                 default :
42018                     break;
42019             }
42020             
42021             var el = template.overwrite(td, {"text": text});
42022             
42023             var inner = el.getElementsByTagName("span")[0];
42024             
42025             return {"el": el, "inner": inner};
42026             
42027     }
42028         
42029     
42030 });
42031
42032 /**
42033  * @class Roo.TabPanelItem
42034  * @extends Roo.util.Observable
42035  * Represents an individual item (tab plus body) in a TabPanel.
42036  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42037  * @param {String} id The id of this TabPanelItem
42038  * @param {String} text The text for the tab of this TabPanelItem
42039  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42040  */
42041 Roo.bootstrap.panel.TabItem = function(config){
42042     /**
42043      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42044      * @type Roo.TabPanel
42045      */
42046     this.tabPanel = config.panel;
42047     /**
42048      * The id for this TabPanelItem
42049      * @type String
42050      */
42051     this.id = config.id;
42052     /** @private */
42053     this.disabled = false;
42054     /** @private */
42055     this.text = config.text;
42056     /** @private */
42057     this.loaded = false;
42058     this.closable = config.closable;
42059
42060     /**
42061      * The body element for this TabPanelItem.
42062      * @type Roo.Element
42063      */
42064     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42065     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42066     this.bodyEl.setStyle("display", "block");
42067     this.bodyEl.setStyle("zoom", "1");
42068     //this.hideAction();
42069
42070     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42071     /** @private */
42072     this.el = Roo.get(els.el);
42073     this.inner = Roo.get(els.inner, true);
42074      this.textEl = Roo.bootstrap.version == 4 ?
42075         this.el : Roo.get(this.el.dom.firstChild, true);
42076
42077     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42078     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42079
42080     
42081 //    this.el.on("mousedown", this.onTabMouseDown, this);
42082     this.el.on("click", this.onTabClick, this);
42083     /** @private */
42084     if(config.closable){
42085         var c = Roo.get(els.close, true);
42086         c.dom.title = this.closeText;
42087         c.addClassOnOver("close-over");
42088         c.on("click", this.closeClick, this);
42089      }
42090
42091     this.addEvents({
42092          /**
42093          * @event activate
42094          * Fires when this tab becomes the active tab.
42095          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42096          * @param {Roo.TabPanelItem} this
42097          */
42098         "activate": true,
42099         /**
42100          * @event beforeclose
42101          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42102          * @param {Roo.TabPanelItem} this
42103          * @param {Object} e Set cancel to true on this object to cancel the close.
42104          */
42105         "beforeclose": true,
42106         /**
42107          * @event close
42108          * Fires when this tab is closed.
42109          * @param {Roo.TabPanelItem} this
42110          */
42111          "close": true,
42112         /**
42113          * @event deactivate
42114          * Fires when this tab is no longer the active tab.
42115          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42116          * @param {Roo.TabPanelItem} this
42117          */
42118          "deactivate" : true
42119     });
42120     this.hidden = false;
42121
42122     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42123 };
42124
42125 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42126            {
42127     purgeListeners : function(){
42128        Roo.util.Observable.prototype.purgeListeners.call(this);
42129        this.el.removeAllListeners();
42130     },
42131     /**
42132      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42133      */
42134     show : function(){
42135         this.status_node.addClass("active");
42136         this.showAction();
42137         if(Roo.isOpera){
42138             this.tabPanel.stripWrap.repaint();
42139         }
42140         this.fireEvent("activate", this.tabPanel, this);
42141     },
42142
42143     /**
42144      * Returns true if this tab is the active tab.
42145      * @return {Boolean}
42146      */
42147     isActive : function(){
42148         return this.tabPanel.getActiveTab() == this;
42149     },
42150
42151     /**
42152      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42153      */
42154     hide : function(){
42155         this.status_node.removeClass("active");
42156         this.hideAction();
42157         this.fireEvent("deactivate", this.tabPanel, this);
42158     },
42159
42160     hideAction : function(){
42161         this.bodyEl.hide();
42162         this.bodyEl.setStyle("position", "absolute");
42163         this.bodyEl.setLeft("-20000px");
42164         this.bodyEl.setTop("-20000px");
42165     },
42166
42167     showAction : function(){
42168         this.bodyEl.setStyle("position", "relative");
42169         this.bodyEl.setTop("");
42170         this.bodyEl.setLeft("");
42171         this.bodyEl.show();
42172     },
42173
42174     /**
42175      * Set the tooltip for the tab.
42176      * @param {String} tooltip The tab's tooltip
42177      */
42178     setTooltip : function(text){
42179         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42180             this.textEl.dom.qtip = text;
42181             this.textEl.dom.removeAttribute('title');
42182         }else{
42183             this.textEl.dom.title = text;
42184         }
42185     },
42186
42187     onTabClick : function(e){
42188         e.preventDefault();
42189         this.tabPanel.activate(this.id);
42190     },
42191
42192     onTabMouseDown : function(e){
42193         e.preventDefault();
42194         this.tabPanel.activate(this.id);
42195     },
42196 /*
42197     getWidth : function(){
42198         return this.inner.getWidth();
42199     },
42200
42201     setWidth : function(width){
42202         var iwidth = width - this.linode.getPadding("lr");
42203         this.inner.setWidth(iwidth);
42204         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42205         this.linode.setWidth(width);
42206     },
42207 */
42208     /**
42209      * Show or hide the tab
42210      * @param {Boolean} hidden True to hide or false to show.
42211      */
42212     setHidden : function(hidden){
42213         this.hidden = hidden;
42214         this.linode.setStyle("display", hidden ? "none" : "");
42215     },
42216
42217     /**
42218      * Returns true if this tab is "hidden"
42219      * @return {Boolean}
42220      */
42221     isHidden : function(){
42222         return this.hidden;
42223     },
42224
42225     /**
42226      * Returns the text for this tab
42227      * @return {String}
42228      */
42229     getText : function(){
42230         return this.text;
42231     },
42232     /*
42233     autoSize : function(){
42234         //this.el.beginMeasure();
42235         this.textEl.setWidth(1);
42236         /*
42237          *  #2804 [new] Tabs in Roojs
42238          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42239          */
42240         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42241         //this.el.endMeasure();
42242     //},
42243
42244     /**
42245      * Sets the text for the tab (Note: this also sets the tooltip text)
42246      * @param {String} text The tab's text and tooltip
42247      */
42248     setText : function(text){
42249         this.text = text;
42250         this.textEl.update(text);
42251         this.setTooltip(text);
42252         //if(!this.tabPanel.resizeTabs){
42253         //    this.autoSize();
42254         //}
42255     },
42256     /**
42257      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42258      */
42259     activate : function(){
42260         this.tabPanel.activate(this.id);
42261     },
42262
42263     /**
42264      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42265      */
42266     disable : function(){
42267         if(this.tabPanel.active != this){
42268             this.disabled = true;
42269             this.status_node.addClass("disabled");
42270         }
42271     },
42272
42273     /**
42274      * Enables this TabPanelItem if it was previously disabled.
42275      */
42276     enable : function(){
42277         this.disabled = false;
42278         this.status_node.removeClass("disabled");
42279     },
42280
42281     /**
42282      * Sets the content for this TabPanelItem.
42283      * @param {String} content The content
42284      * @param {Boolean} loadScripts true to look for and load scripts
42285      */
42286     setContent : function(content, loadScripts){
42287         this.bodyEl.update(content, loadScripts);
42288     },
42289
42290     /**
42291      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42292      * @return {Roo.UpdateManager} The UpdateManager
42293      */
42294     getUpdateManager : function(){
42295         return this.bodyEl.getUpdateManager();
42296     },
42297
42298     /**
42299      * Set a URL to be used to load the content for this TabPanelItem.
42300      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42301      * @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)
42302      * @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)
42303      * @return {Roo.UpdateManager} The UpdateManager
42304      */
42305     setUrl : function(url, params, loadOnce){
42306         if(this.refreshDelegate){
42307             this.un('activate', this.refreshDelegate);
42308         }
42309         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42310         this.on("activate", this.refreshDelegate);
42311         return this.bodyEl.getUpdateManager();
42312     },
42313
42314     /** @private */
42315     _handleRefresh : function(url, params, loadOnce){
42316         if(!loadOnce || !this.loaded){
42317             var updater = this.bodyEl.getUpdateManager();
42318             updater.update(url, params, this._setLoaded.createDelegate(this));
42319         }
42320     },
42321
42322     /**
42323      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42324      *   Will fail silently if the setUrl method has not been called.
42325      *   This does not activate the panel, just updates its content.
42326      */
42327     refresh : function(){
42328         if(this.refreshDelegate){
42329            this.loaded = false;
42330            this.refreshDelegate();
42331         }
42332     },
42333
42334     /** @private */
42335     _setLoaded : function(){
42336         this.loaded = true;
42337     },
42338
42339     /** @private */
42340     closeClick : function(e){
42341         var o = {};
42342         e.stopEvent();
42343         this.fireEvent("beforeclose", this, o);
42344         if(o.cancel !== true){
42345             this.tabPanel.removeTab(this.id);
42346         }
42347     },
42348     /**
42349      * The text displayed in the tooltip for the close icon.
42350      * @type String
42351      */
42352     closeText : "Close this tab"
42353 });
42354 /**
42355 *    This script refer to:
42356 *    Title: International Telephone Input
42357 *    Author: Jack O'Connor
42358 *    Code version:  v12.1.12
42359 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42360 **/
42361
42362 Roo.bootstrap.PhoneInputData = function() {
42363     var d = [
42364       [
42365         "Afghanistan (‫افغانستان‬‎)",
42366         "af",
42367         "93"
42368       ],
42369       [
42370         "Albania (Shqipëri)",
42371         "al",
42372         "355"
42373       ],
42374       [
42375         "Algeria (‫الجزائر‬‎)",
42376         "dz",
42377         "213"
42378       ],
42379       [
42380         "American Samoa",
42381         "as",
42382         "1684"
42383       ],
42384       [
42385         "Andorra",
42386         "ad",
42387         "376"
42388       ],
42389       [
42390         "Angola",
42391         "ao",
42392         "244"
42393       ],
42394       [
42395         "Anguilla",
42396         "ai",
42397         "1264"
42398       ],
42399       [
42400         "Antigua and Barbuda",
42401         "ag",
42402         "1268"
42403       ],
42404       [
42405         "Argentina",
42406         "ar",
42407         "54"
42408       ],
42409       [
42410         "Armenia (Հայաստան)",
42411         "am",
42412         "374"
42413       ],
42414       [
42415         "Aruba",
42416         "aw",
42417         "297"
42418       ],
42419       [
42420         "Australia",
42421         "au",
42422         "61",
42423         0
42424       ],
42425       [
42426         "Austria (Österreich)",
42427         "at",
42428         "43"
42429       ],
42430       [
42431         "Azerbaijan (Azərbaycan)",
42432         "az",
42433         "994"
42434       ],
42435       [
42436         "Bahamas",
42437         "bs",
42438         "1242"
42439       ],
42440       [
42441         "Bahrain (‫البحرين‬‎)",
42442         "bh",
42443         "973"
42444       ],
42445       [
42446         "Bangladesh (বাংলাদেশ)",
42447         "bd",
42448         "880"
42449       ],
42450       [
42451         "Barbados",
42452         "bb",
42453         "1246"
42454       ],
42455       [
42456         "Belarus (Беларусь)",
42457         "by",
42458         "375"
42459       ],
42460       [
42461         "Belgium (België)",
42462         "be",
42463         "32"
42464       ],
42465       [
42466         "Belize",
42467         "bz",
42468         "501"
42469       ],
42470       [
42471         "Benin (Bénin)",
42472         "bj",
42473         "229"
42474       ],
42475       [
42476         "Bermuda",
42477         "bm",
42478         "1441"
42479       ],
42480       [
42481         "Bhutan (འབྲུག)",
42482         "bt",
42483         "975"
42484       ],
42485       [
42486         "Bolivia",
42487         "bo",
42488         "591"
42489       ],
42490       [
42491         "Bosnia and Herzegovina (Босна и Херцеговина)",
42492         "ba",
42493         "387"
42494       ],
42495       [
42496         "Botswana",
42497         "bw",
42498         "267"
42499       ],
42500       [
42501         "Brazil (Brasil)",
42502         "br",
42503         "55"
42504       ],
42505       [
42506         "British Indian Ocean Territory",
42507         "io",
42508         "246"
42509       ],
42510       [
42511         "British Virgin Islands",
42512         "vg",
42513         "1284"
42514       ],
42515       [
42516         "Brunei",
42517         "bn",
42518         "673"
42519       ],
42520       [
42521         "Bulgaria (България)",
42522         "bg",
42523         "359"
42524       ],
42525       [
42526         "Burkina Faso",
42527         "bf",
42528         "226"
42529       ],
42530       [
42531         "Burundi (Uburundi)",
42532         "bi",
42533         "257"
42534       ],
42535       [
42536         "Cambodia (កម្ពុជា)",
42537         "kh",
42538         "855"
42539       ],
42540       [
42541         "Cameroon (Cameroun)",
42542         "cm",
42543         "237"
42544       ],
42545       [
42546         "Canada",
42547         "ca",
42548         "1",
42549         1,
42550         ["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"]
42551       ],
42552       [
42553         "Cape Verde (Kabu Verdi)",
42554         "cv",
42555         "238"
42556       ],
42557       [
42558         "Caribbean Netherlands",
42559         "bq",
42560         "599",
42561         1
42562       ],
42563       [
42564         "Cayman Islands",
42565         "ky",
42566         "1345"
42567       ],
42568       [
42569         "Central African Republic (République centrafricaine)",
42570         "cf",
42571         "236"
42572       ],
42573       [
42574         "Chad (Tchad)",
42575         "td",
42576         "235"
42577       ],
42578       [
42579         "Chile",
42580         "cl",
42581         "56"
42582       ],
42583       [
42584         "China (中国)",
42585         "cn",
42586         "86"
42587       ],
42588       [
42589         "Christmas Island",
42590         "cx",
42591         "61",
42592         2
42593       ],
42594       [
42595         "Cocos (Keeling) Islands",
42596         "cc",
42597         "61",
42598         1
42599       ],
42600       [
42601         "Colombia",
42602         "co",
42603         "57"
42604       ],
42605       [
42606         "Comoros (‫جزر القمر‬‎)",
42607         "km",
42608         "269"
42609       ],
42610       [
42611         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42612         "cd",
42613         "243"
42614       ],
42615       [
42616         "Congo (Republic) (Congo-Brazzaville)",
42617         "cg",
42618         "242"
42619       ],
42620       [
42621         "Cook Islands",
42622         "ck",
42623         "682"
42624       ],
42625       [
42626         "Costa Rica",
42627         "cr",
42628         "506"
42629       ],
42630       [
42631         "Côte d’Ivoire",
42632         "ci",
42633         "225"
42634       ],
42635       [
42636         "Croatia (Hrvatska)",
42637         "hr",
42638         "385"
42639       ],
42640       [
42641         "Cuba",
42642         "cu",
42643         "53"
42644       ],
42645       [
42646         "Curaçao",
42647         "cw",
42648         "599",
42649         0
42650       ],
42651       [
42652         "Cyprus (Κύπρος)",
42653         "cy",
42654         "357"
42655       ],
42656       [
42657         "Czech Republic (Česká republika)",
42658         "cz",
42659         "420"
42660       ],
42661       [
42662         "Denmark (Danmark)",
42663         "dk",
42664         "45"
42665       ],
42666       [
42667         "Djibouti",
42668         "dj",
42669         "253"
42670       ],
42671       [
42672         "Dominica",
42673         "dm",
42674         "1767"
42675       ],
42676       [
42677         "Dominican Republic (República Dominicana)",
42678         "do",
42679         "1",
42680         2,
42681         ["809", "829", "849"]
42682       ],
42683       [
42684         "Ecuador",
42685         "ec",
42686         "593"
42687       ],
42688       [
42689         "Egypt (‫مصر‬‎)",
42690         "eg",
42691         "20"
42692       ],
42693       [
42694         "El Salvador",
42695         "sv",
42696         "503"
42697       ],
42698       [
42699         "Equatorial Guinea (Guinea Ecuatorial)",
42700         "gq",
42701         "240"
42702       ],
42703       [
42704         "Eritrea",
42705         "er",
42706         "291"
42707       ],
42708       [
42709         "Estonia (Eesti)",
42710         "ee",
42711         "372"
42712       ],
42713       [
42714         "Ethiopia",
42715         "et",
42716         "251"
42717       ],
42718       [
42719         "Falkland Islands (Islas Malvinas)",
42720         "fk",
42721         "500"
42722       ],
42723       [
42724         "Faroe Islands (Føroyar)",
42725         "fo",
42726         "298"
42727       ],
42728       [
42729         "Fiji",
42730         "fj",
42731         "679"
42732       ],
42733       [
42734         "Finland (Suomi)",
42735         "fi",
42736         "358",
42737         0
42738       ],
42739       [
42740         "France",
42741         "fr",
42742         "33"
42743       ],
42744       [
42745         "French Guiana (Guyane française)",
42746         "gf",
42747         "594"
42748       ],
42749       [
42750         "French Polynesia (Polynésie française)",
42751         "pf",
42752         "689"
42753       ],
42754       [
42755         "Gabon",
42756         "ga",
42757         "241"
42758       ],
42759       [
42760         "Gambia",
42761         "gm",
42762         "220"
42763       ],
42764       [
42765         "Georgia (საქართველო)",
42766         "ge",
42767         "995"
42768       ],
42769       [
42770         "Germany (Deutschland)",
42771         "de",
42772         "49"
42773       ],
42774       [
42775         "Ghana (Gaana)",
42776         "gh",
42777         "233"
42778       ],
42779       [
42780         "Gibraltar",
42781         "gi",
42782         "350"
42783       ],
42784       [
42785         "Greece (Ελλάδα)",
42786         "gr",
42787         "30"
42788       ],
42789       [
42790         "Greenland (Kalaallit Nunaat)",
42791         "gl",
42792         "299"
42793       ],
42794       [
42795         "Grenada",
42796         "gd",
42797         "1473"
42798       ],
42799       [
42800         "Guadeloupe",
42801         "gp",
42802         "590",
42803         0
42804       ],
42805       [
42806         "Guam",
42807         "gu",
42808         "1671"
42809       ],
42810       [
42811         "Guatemala",
42812         "gt",
42813         "502"
42814       ],
42815       [
42816         "Guernsey",
42817         "gg",
42818         "44",
42819         1
42820       ],
42821       [
42822         "Guinea (Guinée)",
42823         "gn",
42824         "224"
42825       ],
42826       [
42827         "Guinea-Bissau (Guiné Bissau)",
42828         "gw",
42829         "245"
42830       ],
42831       [
42832         "Guyana",
42833         "gy",
42834         "592"
42835       ],
42836       [
42837         "Haiti",
42838         "ht",
42839         "509"
42840       ],
42841       [
42842         "Honduras",
42843         "hn",
42844         "504"
42845       ],
42846       [
42847         "Hong Kong (香港)",
42848         "hk",
42849         "852"
42850       ],
42851       [
42852         "Hungary (Magyarország)",
42853         "hu",
42854         "36"
42855       ],
42856       [
42857         "Iceland (Ísland)",
42858         "is",
42859         "354"
42860       ],
42861       [
42862         "India (भारत)",
42863         "in",
42864         "91"
42865       ],
42866       [
42867         "Indonesia",
42868         "id",
42869         "62"
42870       ],
42871       [
42872         "Iran (‫ایران‬‎)",
42873         "ir",
42874         "98"
42875       ],
42876       [
42877         "Iraq (‫العراق‬‎)",
42878         "iq",
42879         "964"
42880       ],
42881       [
42882         "Ireland",
42883         "ie",
42884         "353"
42885       ],
42886       [
42887         "Isle of Man",
42888         "im",
42889         "44",
42890         2
42891       ],
42892       [
42893         "Israel (‫ישראל‬‎)",
42894         "il",
42895         "972"
42896       ],
42897       [
42898         "Italy (Italia)",
42899         "it",
42900         "39",
42901         0
42902       ],
42903       [
42904         "Jamaica",
42905         "jm",
42906         "1876"
42907       ],
42908       [
42909         "Japan (日本)",
42910         "jp",
42911         "81"
42912       ],
42913       [
42914         "Jersey",
42915         "je",
42916         "44",
42917         3
42918       ],
42919       [
42920         "Jordan (‫الأردن‬‎)",
42921         "jo",
42922         "962"
42923       ],
42924       [
42925         "Kazakhstan (Казахстан)",
42926         "kz",
42927         "7",
42928         1
42929       ],
42930       [
42931         "Kenya",
42932         "ke",
42933         "254"
42934       ],
42935       [
42936         "Kiribati",
42937         "ki",
42938         "686"
42939       ],
42940       [
42941         "Kosovo",
42942         "xk",
42943         "383"
42944       ],
42945       [
42946         "Kuwait (‫الكويت‬‎)",
42947         "kw",
42948         "965"
42949       ],
42950       [
42951         "Kyrgyzstan (Кыргызстан)",
42952         "kg",
42953         "996"
42954       ],
42955       [
42956         "Laos (ລາວ)",
42957         "la",
42958         "856"
42959       ],
42960       [
42961         "Latvia (Latvija)",
42962         "lv",
42963         "371"
42964       ],
42965       [
42966         "Lebanon (‫لبنان‬‎)",
42967         "lb",
42968         "961"
42969       ],
42970       [
42971         "Lesotho",
42972         "ls",
42973         "266"
42974       ],
42975       [
42976         "Liberia",
42977         "lr",
42978         "231"
42979       ],
42980       [
42981         "Libya (‫ليبيا‬‎)",
42982         "ly",
42983         "218"
42984       ],
42985       [
42986         "Liechtenstein",
42987         "li",
42988         "423"
42989       ],
42990       [
42991         "Lithuania (Lietuva)",
42992         "lt",
42993         "370"
42994       ],
42995       [
42996         "Luxembourg",
42997         "lu",
42998         "352"
42999       ],
43000       [
43001         "Macau (澳門)",
43002         "mo",
43003         "853"
43004       ],
43005       [
43006         "Macedonia (FYROM) (Македонија)",
43007         "mk",
43008         "389"
43009       ],
43010       [
43011         "Madagascar (Madagasikara)",
43012         "mg",
43013         "261"
43014       ],
43015       [
43016         "Malawi",
43017         "mw",
43018         "265"
43019       ],
43020       [
43021         "Malaysia",
43022         "my",
43023         "60"
43024       ],
43025       [
43026         "Maldives",
43027         "mv",
43028         "960"
43029       ],
43030       [
43031         "Mali",
43032         "ml",
43033         "223"
43034       ],
43035       [
43036         "Malta",
43037         "mt",
43038         "356"
43039       ],
43040       [
43041         "Marshall Islands",
43042         "mh",
43043         "692"
43044       ],
43045       [
43046         "Martinique",
43047         "mq",
43048         "596"
43049       ],
43050       [
43051         "Mauritania (‫موريتانيا‬‎)",
43052         "mr",
43053         "222"
43054       ],
43055       [
43056         "Mauritius (Moris)",
43057         "mu",
43058         "230"
43059       ],
43060       [
43061         "Mayotte",
43062         "yt",
43063         "262",
43064         1
43065       ],
43066       [
43067         "Mexico (México)",
43068         "mx",
43069         "52"
43070       ],
43071       [
43072         "Micronesia",
43073         "fm",
43074         "691"
43075       ],
43076       [
43077         "Moldova (Republica Moldova)",
43078         "md",
43079         "373"
43080       ],
43081       [
43082         "Monaco",
43083         "mc",
43084         "377"
43085       ],
43086       [
43087         "Mongolia (Монгол)",
43088         "mn",
43089         "976"
43090       ],
43091       [
43092         "Montenegro (Crna Gora)",
43093         "me",
43094         "382"
43095       ],
43096       [
43097         "Montserrat",
43098         "ms",
43099         "1664"
43100       ],
43101       [
43102         "Morocco (‫المغرب‬‎)",
43103         "ma",
43104         "212",
43105         0
43106       ],
43107       [
43108         "Mozambique (Moçambique)",
43109         "mz",
43110         "258"
43111       ],
43112       [
43113         "Myanmar (Burma) (မြန်မာ)",
43114         "mm",
43115         "95"
43116       ],
43117       [
43118         "Namibia (Namibië)",
43119         "na",
43120         "264"
43121       ],
43122       [
43123         "Nauru",
43124         "nr",
43125         "674"
43126       ],
43127       [
43128         "Nepal (नेपाल)",
43129         "np",
43130         "977"
43131       ],
43132       [
43133         "Netherlands (Nederland)",
43134         "nl",
43135         "31"
43136       ],
43137       [
43138         "New Caledonia (Nouvelle-Calédonie)",
43139         "nc",
43140         "687"
43141       ],
43142       [
43143         "New Zealand",
43144         "nz",
43145         "64"
43146       ],
43147       [
43148         "Nicaragua",
43149         "ni",
43150         "505"
43151       ],
43152       [
43153         "Niger (Nijar)",
43154         "ne",
43155         "227"
43156       ],
43157       [
43158         "Nigeria",
43159         "ng",
43160         "234"
43161       ],
43162       [
43163         "Niue",
43164         "nu",
43165         "683"
43166       ],
43167       [
43168         "Norfolk Island",
43169         "nf",
43170         "672"
43171       ],
43172       [
43173         "North Korea (조선 민주주의 인민 공화국)",
43174         "kp",
43175         "850"
43176       ],
43177       [
43178         "Northern Mariana Islands",
43179         "mp",
43180         "1670"
43181       ],
43182       [
43183         "Norway (Norge)",
43184         "no",
43185         "47",
43186         0
43187       ],
43188       [
43189         "Oman (‫عُمان‬‎)",
43190         "om",
43191         "968"
43192       ],
43193       [
43194         "Pakistan (‫پاکستان‬‎)",
43195         "pk",
43196         "92"
43197       ],
43198       [
43199         "Palau",
43200         "pw",
43201         "680"
43202       ],
43203       [
43204         "Palestine (‫فلسطين‬‎)",
43205         "ps",
43206         "970"
43207       ],
43208       [
43209         "Panama (Panamá)",
43210         "pa",
43211         "507"
43212       ],
43213       [
43214         "Papua New Guinea",
43215         "pg",
43216         "675"
43217       ],
43218       [
43219         "Paraguay",
43220         "py",
43221         "595"
43222       ],
43223       [
43224         "Peru (Perú)",
43225         "pe",
43226         "51"
43227       ],
43228       [
43229         "Philippines",
43230         "ph",
43231         "63"
43232       ],
43233       [
43234         "Poland (Polska)",
43235         "pl",
43236         "48"
43237       ],
43238       [
43239         "Portugal",
43240         "pt",
43241         "351"
43242       ],
43243       [
43244         "Puerto Rico",
43245         "pr",
43246         "1",
43247         3,
43248         ["787", "939"]
43249       ],
43250       [
43251         "Qatar (‫قطر‬‎)",
43252         "qa",
43253         "974"
43254       ],
43255       [
43256         "Réunion (La Réunion)",
43257         "re",
43258         "262",
43259         0
43260       ],
43261       [
43262         "Romania (România)",
43263         "ro",
43264         "40"
43265       ],
43266       [
43267         "Russia (Россия)",
43268         "ru",
43269         "7",
43270         0
43271       ],
43272       [
43273         "Rwanda",
43274         "rw",
43275         "250"
43276       ],
43277       [
43278         "Saint Barthélemy",
43279         "bl",
43280         "590",
43281         1
43282       ],
43283       [
43284         "Saint Helena",
43285         "sh",
43286         "290"
43287       ],
43288       [
43289         "Saint Kitts and Nevis",
43290         "kn",
43291         "1869"
43292       ],
43293       [
43294         "Saint Lucia",
43295         "lc",
43296         "1758"
43297       ],
43298       [
43299         "Saint Martin (Saint-Martin (partie française))",
43300         "mf",
43301         "590",
43302         2
43303       ],
43304       [
43305         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43306         "pm",
43307         "508"
43308       ],
43309       [
43310         "Saint Vincent and the Grenadines",
43311         "vc",
43312         "1784"
43313       ],
43314       [
43315         "Samoa",
43316         "ws",
43317         "685"
43318       ],
43319       [
43320         "San Marino",
43321         "sm",
43322         "378"
43323       ],
43324       [
43325         "São Tomé and Príncipe (São Tomé e Príncipe)",
43326         "st",
43327         "239"
43328       ],
43329       [
43330         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43331         "sa",
43332         "966"
43333       ],
43334       [
43335         "Senegal (Sénégal)",
43336         "sn",
43337         "221"
43338       ],
43339       [
43340         "Serbia (Србија)",
43341         "rs",
43342         "381"
43343       ],
43344       [
43345         "Seychelles",
43346         "sc",
43347         "248"
43348       ],
43349       [
43350         "Sierra Leone",
43351         "sl",
43352         "232"
43353       ],
43354       [
43355         "Singapore",
43356         "sg",
43357         "65"
43358       ],
43359       [
43360         "Sint Maarten",
43361         "sx",
43362         "1721"
43363       ],
43364       [
43365         "Slovakia (Slovensko)",
43366         "sk",
43367         "421"
43368       ],
43369       [
43370         "Slovenia (Slovenija)",
43371         "si",
43372         "386"
43373       ],
43374       [
43375         "Solomon Islands",
43376         "sb",
43377         "677"
43378       ],
43379       [
43380         "Somalia (Soomaaliya)",
43381         "so",
43382         "252"
43383       ],
43384       [
43385         "South Africa",
43386         "za",
43387         "27"
43388       ],
43389       [
43390         "South Korea (대한민국)",
43391         "kr",
43392         "82"
43393       ],
43394       [
43395         "South Sudan (‫جنوب السودان‬‎)",
43396         "ss",
43397         "211"
43398       ],
43399       [
43400         "Spain (España)",
43401         "es",
43402         "34"
43403       ],
43404       [
43405         "Sri Lanka (ශ්‍රී ලංකාව)",
43406         "lk",
43407         "94"
43408       ],
43409       [
43410         "Sudan (‫السودان‬‎)",
43411         "sd",
43412         "249"
43413       ],
43414       [
43415         "Suriname",
43416         "sr",
43417         "597"
43418       ],
43419       [
43420         "Svalbard and Jan Mayen",
43421         "sj",
43422         "47",
43423         1
43424       ],
43425       [
43426         "Swaziland",
43427         "sz",
43428         "268"
43429       ],
43430       [
43431         "Sweden (Sverige)",
43432         "se",
43433         "46"
43434       ],
43435       [
43436         "Switzerland (Schweiz)",
43437         "ch",
43438         "41"
43439       ],
43440       [
43441         "Syria (‫سوريا‬‎)",
43442         "sy",
43443         "963"
43444       ],
43445       [
43446         "Taiwan (台灣)",
43447         "tw",
43448         "886"
43449       ],
43450       [
43451         "Tajikistan",
43452         "tj",
43453         "992"
43454       ],
43455       [
43456         "Tanzania",
43457         "tz",
43458         "255"
43459       ],
43460       [
43461         "Thailand (ไทย)",
43462         "th",
43463         "66"
43464       ],
43465       [
43466         "Timor-Leste",
43467         "tl",
43468         "670"
43469       ],
43470       [
43471         "Togo",
43472         "tg",
43473         "228"
43474       ],
43475       [
43476         "Tokelau",
43477         "tk",
43478         "690"
43479       ],
43480       [
43481         "Tonga",
43482         "to",
43483         "676"
43484       ],
43485       [
43486         "Trinidad and Tobago",
43487         "tt",
43488         "1868"
43489       ],
43490       [
43491         "Tunisia (‫تونس‬‎)",
43492         "tn",
43493         "216"
43494       ],
43495       [
43496         "Turkey (Türkiye)",
43497         "tr",
43498         "90"
43499       ],
43500       [
43501         "Turkmenistan",
43502         "tm",
43503         "993"
43504       ],
43505       [
43506         "Turks and Caicos Islands",
43507         "tc",
43508         "1649"
43509       ],
43510       [
43511         "Tuvalu",
43512         "tv",
43513         "688"
43514       ],
43515       [
43516         "U.S. Virgin Islands",
43517         "vi",
43518         "1340"
43519       ],
43520       [
43521         "Uganda",
43522         "ug",
43523         "256"
43524       ],
43525       [
43526         "Ukraine (Україна)",
43527         "ua",
43528         "380"
43529       ],
43530       [
43531         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43532         "ae",
43533         "971"
43534       ],
43535       [
43536         "United Kingdom",
43537         "gb",
43538         "44",
43539         0
43540       ],
43541       [
43542         "United States",
43543         "us",
43544         "1",
43545         0
43546       ],
43547       [
43548         "Uruguay",
43549         "uy",
43550         "598"
43551       ],
43552       [
43553         "Uzbekistan (Oʻzbekiston)",
43554         "uz",
43555         "998"
43556       ],
43557       [
43558         "Vanuatu",
43559         "vu",
43560         "678"
43561       ],
43562       [
43563         "Vatican City (Città del Vaticano)",
43564         "va",
43565         "39",
43566         1
43567       ],
43568       [
43569         "Venezuela",
43570         "ve",
43571         "58"
43572       ],
43573       [
43574         "Vietnam (Việt Nam)",
43575         "vn",
43576         "84"
43577       ],
43578       [
43579         "Wallis and Futuna (Wallis-et-Futuna)",
43580         "wf",
43581         "681"
43582       ],
43583       [
43584         "Western Sahara (‫الصحراء الغربية‬‎)",
43585         "eh",
43586         "212",
43587         1
43588       ],
43589       [
43590         "Yemen (‫اليمن‬‎)",
43591         "ye",
43592         "967"
43593       ],
43594       [
43595         "Zambia",
43596         "zm",
43597         "260"
43598       ],
43599       [
43600         "Zimbabwe",
43601         "zw",
43602         "263"
43603       ],
43604       [
43605         "Åland Islands",
43606         "ax",
43607         "358",
43608         1
43609       ]
43610   ];
43611   
43612   return d;
43613 }/**
43614 *    This script refer to:
43615 *    Title: International Telephone Input
43616 *    Author: Jack O'Connor
43617 *    Code version:  v12.1.12
43618 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43619 **/
43620
43621 /**
43622  * @class Roo.bootstrap.PhoneInput
43623  * @extends Roo.bootstrap.TriggerField
43624  * An input with International dial-code selection
43625  
43626  * @cfg {String} defaultDialCode default '+852'
43627  * @cfg {Array} preferedCountries default []
43628   
43629  * @constructor
43630  * Create a new PhoneInput.
43631  * @param {Object} config Configuration options
43632  */
43633
43634 Roo.bootstrap.PhoneInput = function(config) {
43635     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43636 };
43637
43638 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43639         /**
43640         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43641         */
43642         listWidth: undefined,
43643         
43644         selectedClass: 'active',
43645         
43646         invalidClass : "has-warning",
43647         
43648         validClass: 'has-success',
43649         
43650         allowed: '0123456789',
43651         
43652         max_length: 15,
43653         
43654         /**
43655          * @cfg {String} defaultDialCode The default dial code when initializing the input
43656          */
43657         defaultDialCode: '+852',
43658         
43659         /**
43660          * @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
43661          */
43662         preferedCountries: false,
43663         
43664         getAutoCreate : function()
43665         {
43666             var data = Roo.bootstrap.PhoneInputData();
43667             var align = this.labelAlign || this.parentLabelAlign();
43668             var id = Roo.id();
43669             
43670             this.allCountries = [];
43671             this.dialCodeMapping = [];
43672             
43673             for (var i = 0; i < data.length; i++) {
43674               var c = data[i];
43675               this.allCountries[i] = {
43676                 name: c[0],
43677                 iso2: c[1],
43678                 dialCode: c[2],
43679                 priority: c[3] || 0,
43680                 areaCodes: c[4] || null
43681               };
43682               this.dialCodeMapping[c[2]] = {
43683                   name: c[0],
43684                   iso2: c[1],
43685                   priority: c[3] || 0,
43686                   areaCodes: c[4] || null
43687               };
43688             }
43689             
43690             var cfg = {
43691                 cls: 'form-group',
43692                 cn: []
43693             };
43694             
43695             var input =  {
43696                 tag: 'input',
43697                 id : id,
43698                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43699                 maxlength: this.max_length,
43700                 cls : 'form-control tel-input',
43701                 autocomplete: 'new-password'
43702             };
43703             
43704             var hiddenInput = {
43705                 tag: 'input',
43706                 type: 'hidden',
43707                 cls: 'hidden-tel-input'
43708             };
43709             
43710             if (this.name) {
43711                 hiddenInput.name = this.name;
43712             }
43713             
43714             if (this.disabled) {
43715                 input.disabled = true;
43716             }
43717             
43718             var flag_container = {
43719                 tag: 'div',
43720                 cls: 'flag-box',
43721                 cn: [
43722                     {
43723                         tag: 'div',
43724                         cls: 'flag'
43725                     },
43726                     {
43727                         tag: 'div',
43728                         cls: 'caret'
43729                     }
43730                 ]
43731             };
43732             
43733             var box = {
43734                 tag: 'div',
43735                 cls: this.hasFeedback ? 'has-feedback' : '',
43736                 cn: [
43737                     hiddenInput,
43738                     input,
43739                     {
43740                         tag: 'input',
43741                         cls: 'dial-code-holder',
43742                         disabled: true
43743                     }
43744                 ]
43745             };
43746             
43747             var container = {
43748                 cls: 'roo-select2-container input-group',
43749                 cn: [
43750                     flag_container,
43751                     box
43752                 ]
43753             };
43754             
43755             if (this.fieldLabel.length) {
43756                 var indicator = {
43757                     tag: 'i',
43758                     tooltip: 'This field is required'
43759                 };
43760                 
43761                 var label = {
43762                     tag: 'label',
43763                     'for':  id,
43764                     cls: 'control-label',
43765                     cn: []
43766                 };
43767                 
43768                 var label_text = {
43769                     tag: 'span',
43770                     html: this.fieldLabel
43771                 };
43772                 
43773                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43774                 label.cn = [
43775                     indicator,
43776                     label_text
43777                 ];
43778                 
43779                 if(this.indicatorpos == 'right') {
43780                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43781                     label.cn = [
43782                         label_text,
43783                         indicator
43784                     ];
43785                 }
43786                 
43787                 if(align == 'left') {
43788                     container = {
43789                         tag: 'div',
43790                         cn: [
43791                             container
43792                         ]
43793                     };
43794                     
43795                     if(this.labelWidth > 12){
43796                         label.style = "width: " + this.labelWidth + 'px';
43797                     }
43798                     if(this.labelWidth < 13 && this.labelmd == 0){
43799                         this.labelmd = this.labelWidth;
43800                     }
43801                     if(this.labellg > 0){
43802                         label.cls += ' col-lg-' + this.labellg;
43803                         input.cls += ' col-lg-' + (12 - this.labellg);
43804                     }
43805                     if(this.labelmd > 0){
43806                         label.cls += ' col-md-' + this.labelmd;
43807                         container.cls += ' col-md-' + (12 - this.labelmd);
43808                     }
43809                     if(this.labelsm > 0){
43810                         label.cls += ' col-sm-' + this.labelsm;
43811                         container.cls += ' col-sm-' + (12 - this.labelsm);
43812                     }
43813                     if(this.labelxs > 0){
43814                         label.cls += ' col-xs-' + this.labelxs;
43815                         container.cls += ' col-xs-' + (12 - this.labelxs);
43816                     }
43817                 }
43818             }
43819             
43820             cfg.cn = [
43821                 label,
43822                 container
43823             ];
43824             
43825             var settings = this;
43826             
43827             ['xs','sm','md','lg'].map(function(size){
43828                 if (settings[size]) {
43829                     cfg.cls += ' col-' + size + '-' + settings[size];
43830                 }
43831             });
43832             
43833             this.store = new Roo.data.Store({
43834                 proxy : new Roo.data.MemoryProxy({}),
43835                 reader : new Roo.data.JsonReader({
43836                     fields : [
43837                         {
43838                             'name' : 'name',
43839                             'type' : 'string'
43840                         },
43841                         {
43842                             'name' : 'iso2',
43843                             'type' : 'string'
43844                         },
43845                         {
43846                             'name' : 'dialCode',
43847                             'type' : 'string'
43848                         },
43849                         {
43850                             'name' : 'priority',
43851                             'type' : 'string'
43852                         },
43853                         {
43854                             'name' : 'areaCodes',
43855                             'type' : 'string'
43856                         }
43857                     ]
43858                 })
43859             });
43860             
43861             if(!this.preferedCountries) {
43862                 this.preferedCountries = [
43863                     'hk',
43864                     'gb',
43865                     'us'
43866                 ];
43867             }
43868             
43869             var p = this.preferedCountries.reverse();
43870             
43871             if(p) {
43872                 for (var i = 0; i < p.length; i++) {
43873                     for (var j = 0; j < this.allCountries.length; j++) {
43874                         if(this.allCountries[j].iso2 == p[i]) {
43875                             var t = this.allCountries[j];
43876                             this.allCountries.splice(j,1);
43877                             this.allCountries.unshift(t);
43878                         }
43879                     } 
43880                 }
43881             }
43882             
43883             this.store.proxy.data = {
43884                 success: true,
43885                 data: this.allCountries
43886             };
43887             
43888             return cfg;
43889         },
43890         
43891         initEvents : function()
43892         {
43893             this.createList();
43894             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43895             
43896             this.indicator = this.indicatorEl();
43897             this.flag = this.flagEl();
43898             this.dialCodeHolder = this.dialCodeHolderEl();
43899             
43900             this.trigger = this.el.select('div.flag-box',true).first();
43901             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43902             
43903             var _this = this;
43904             
43905             (function(){
43906                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43907                 _this.list.setWidth(lw);
43908             }).defer(100);
43909             
43910             this.list.on('mouseover', this.onViewOver, this);
43911             this.list.on('mousemove', this.onViewMove, this);
43912             this.inputEl().on("keyup", this.onKeyUp, this);
43913             this.inputEl().on("keypress", this.onKeyPress, this);
43914             
43915             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43916
43917             this.view = new Roo.View(this.list, this.tpl, {
43918                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43919             });
43920             
43921             this.view.on('click', this.onViewClick, this);
43922             this.setValue(this.defaultDialCode);
43923         },
43924         
43925         onTriggerClick : function(e)
43926         {
43927             Roo.log('trigger click');
43928             if(this.disabled){
43929                 return;
43930             }
43931             
43932             if(this.isExpanded()){
43933                 this.collapse();
43934                 this.hasFocus = false;
43935             }else {
43936                 this.store.load({});
43937                 this.hasFocus = true;
43938                 this.expand();
43939             }
43940         },
43941         
43942         isExpanded : function()
43943         {
43944             return this.list.isVisible();
43945         },
43946         
43947         collapse : function()
43948         {
43949             if(!this.isExpanded()){
43950                 return;
43951             }
43952             this.list.hide();
43953             Roo.get(document).un('mousedown', this.collapseIf, this);
43954             Roo.get(document).un('mousewheel', this.collapseIf, this);
43955             this.fireEvent('collapse', this);
43956             this.validate();
43957         },
43958         
43959         expand : function()
43960         {
43961             Roo.log('expand');
43962
43963             if(this.isExpanded() || !this.hasFocus){
43964                 return;
43965             }
43966             
43967             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43968             this.list.setWidth(lw);
43969             
43970             this.list.show();
43971             this.restrictHeight();
43972             
43973             Roo.get(document).on('mousedown', this.collapseIf, this);
43974             Roo.get(document).on('mousewheel', this.collapseIf, this);
43975             
43976             this.fireEvent('expand', this);
43977         },
43978         
43979         restrictHeight : function()
43980         {
43981             this.list.alignTo(this.inputEl(), this.listAlign);
43982             this.list.alignTo(this.inputEl(), this.listAlign);
43983         },
43984         
43985         onViewOver : function(e, t)
43986         {
43987             if(this.inKeyMode){
43988                 return;
43989             }
43990             var item = this.view.findItemFromChild(t);
43991             
43992             if(item){
43993                 var index = this.view.indexOf(item);
43994                 this.select(index, false);
43995             }
43996         },
43997
43998         // private
43999         onViewClick : function(view, doFocus, el, e)
44000         {
44001             var index = this.view.getSelectedIndexes()[0];
44002             
44003             var r = this.store.getAt(index);
44004             
44005             if(r){
44006                 this.onSelect(r, index);
44007             }
44008             if(doFocus !== false && !this.blockFocus){
44009                 this.inputEl().focus();
44010             }
44011         },
44012         
44013         onViewMove : function(e, t)
44014         {
44015             this.inKeyMode = false;
44016         },
44017         
44018         select : function(index, scrollIntoView)
44019         {
44020             this.selectedIndex = index;
44021             this.view.select(index);
44022             if(scrollIntoView !== false){
44023                 var el = this.view.getNode(index);
44024                 if(el){
44025                     this.list.scrollChildIntoView(el, false);
44026                 }
44027             }
44028         },
44029         
44030         createList : function()
44031         {
44032             this.list = Roo.get(document.body).createChild({
44033                 tag: 'ul',
44034                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44035                 style: 'display:none'
44036             });
44037             
44038             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44039         },
44040         
44041         collapseIf : function(e)
44042         {
44043             var in_combo  = e.within(this.el);
44044             var in_list =  e.within(this.list);
44045             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44046             
44047             if (in_combo || in_list || is_list) {
44048                 return;
44049             }
44050             this.collapse();
44051         },
44052         
44053         onSelect : function(record, index)
44054         {
44055             if(this.fireEvent('beforeselect', this, record, index) !== false){
44056                 
44057                 this.setFlagClass(record.data.iso2);
44058                 this.setDialCode(record.data.dialCode);
44059                 this.hasFocus = false;
44060                 this.collapse();
44061                 this.fireEvent('select', this, record, index);
44062             }
44063         },
44064         
44065         flagEl : function()
44066         {
44067             var flag = this.el.select('div.flag',true).first();
44068             if(!flag){
44069                 return false;
44070             }
44071             return flag;
44072         },
44073         
44074         dialCodeHolderEl : function()
44075         {
44076             var d = this.el.select('input.dial-code-holder',true).first();
44077             if(!d){
44078                 return false;
44079             }
44080             return d;
44081         },
44082         
44083         setDialCode : function(v)
44084         {
44085             this.dialCodeHolder.dom.value = '+'+v;
44086         },
44087         
44088         setFlagClass : function(n)
44089         {
44090             this.flag.dom.className = 'flag '+n;
44091         },
44092         
44093         getValue : function()
44094         {
44095             var v = this.inputEl().getValue();
44096             if(this.dialCodeHolder) {
44097                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44098             }
44099             return v;
44100         },
44101         
44102         setValue : function(v)
44103         {
44104             var d = this.getDialCode(v);
44105             
44106             //invalid dial code
44107             if(v.length == 0 || !d || d.length == 0) {
44108                 if(this.rendered){
44109                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44110                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44111                 }
44112                 return;
44113             }
44114             
44115             //valid dial code
44116             this.setFlagClass(this.dialCodeMapping[d].iso2);
44117             this.setDialCode(d);
44118             this.inputEl().dom.value = v.replace('+'+d,'');
44119             this.hiddenEl().dom.value = this.getValue();
44120             
44121             this.validate();
44122         },
44123         
44124         getDialCode : function(v)
44125         {
44126             v = v ||  '';
44127             
44128             if (v.length == 0) {
44129                 return this.dialCodeHolder.dom.value;
44130             }
44131             
44132             var dialCode = "";
44133             if (v.charAt(0) != "+") {
44134                 return false;
44135             }
44136             var numericChars = "";
44137             for (var i = 1; i < v.length; i++) {
44138               var c = v.charAt(i);
44139               if (!isNaN(c)) {
44140                 numericChars += c;
44141                 if (this.dialCodeMapping[numericChars]) {
44142                   dialCode = v.substr(1, i);
44143                 }
44144                 if (numericChars.length == 4) {
44145                   break;
44146                 }
44147               }
44148             }
44149             return dialCode;
44150         },
44151         
44152         reset : function()
44153         {
44154             this.setValue(this.defaultDialCode);
44155             this.validate();
44156         },
44157         
44158         hiddenEl : function()
44159         {
44160             return this.el.select('input.hidden-tel-input',true).first();
44161         },
44162         
44163         // after setting val
44164         onKeyUp : function(e){
44165             this.setValue(this.getValue());
44166         },
44167         
44168         onKeyPress : function(e){
44169             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44170                 e.stopEvent();
44171             }
44172         }
44173         
44174 });
44175 /**
44176  * @class Roo.bootstrap.MoneyField
44177  * @extends Roo.bootstrap.ComboBox
44178  * Bootstrap MoneyField class
44179  * 
44180  * @constructor
44181  * Create a new MoneyField.
44182  * @param {Object} config Configuration options
44183  */
44184
44185 Roo.bootstrap.MoneyField = function(config) {
44186     
44187     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44188     
44189 };
44190
44191 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44192     
44193     /**
44194      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44195      */
44196     allowDecimals : true,
44197     /**
44198      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44199      */
44200     decimalSeparator : ".",
44201     /**
44202      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44203      */
44204     decimalPrecision : 0,
44205     /**
44206      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44207      */
44208     allowNegative : true,
44209     /**
44210      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44211      */
44212     allowZero: true,
44213     /**
44214      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44215      */
44216     minValue : Number.NEGATIVE_INFINITY,
44217     /**
44218      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44219      */
44220     maxValue : Number.MAX_VALUE,
44221     /**
44222      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44223      */
44224     minText : "The minimum value for this field is {0}",
44225     /**
44226      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44227      */
44228     maxText : "The maximum value for this field is {0}",
44229     /**
44230      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44231      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44232      */
44233     nanText : "{0} is not a valid number",
44234     /**
44235      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44236      */
44237     castInt : true,
44238     /**
44239      * @cfg {String} defaults currency of the MoneyField
44240      * value should be in lkey
44241      */
44242     defaultCurrency : false,
44243     /**
44244      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44245      */
44246     thousandsDelimiter : false,
44247     /**
44248      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44249      */
44250     max_length: false,
44251     
44252     inputlg : 9,
44253     inputmd : 9,
44254     inputsm : 9,
44255     inputxs : 6,
44256     
44257     store : false,
44258     
44259     getAutoCreate : function()
44260     {
44261         var align = this.labelAlign || this.parentLabelAlign();
44262         
44263         var id = Roo.id();
44264
44265         var cfg = {
44266             cls: 'form-group',
44267             cn: []
44268         };
44269
44270         var input =  {
44271             tag: 'input',
44272             id : id,
44273             cls : 'form-control roo-money-amount-input',
44274             autocomplete: 'new-password'
44275         };
44276         
44277         var hiddenInput = {
44278             tag: 'input',
44279             type: 'hidden',
44280             id: Roo.id(),
44281             cls: 'hidden-number-input'
44282         };
44283         
44284         if(this.max_length) {
44285             input.maxlength = this.max_length; 
44286         }
44287         
44288         if (this.name) {
44289             hiddenInput.name = this.name;
44290         }
44291
44292         if (this.disabled) {
44293             input.disabled = true;
44294         }
44295
44296         var clg = 12 - this.inputlg;
44297         var cmd = 12 - this.inputmd;
44298         var csm = 12 - this.inputsm;
44299         var cxs = 12 - this.inputxs;
44300         
44301         var container = {
44302             tag : 'div',
44303             cls : 'row roo-money-field',
44304             cn : [
44305                 {
44306                     tag : 'div',
44307                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44308                     cn : [
44309                         {
44310                             tag : 'div',
44311                             cls: 'roo-select2-container input-group',
44312                             cn: [
44313                                 {
44314                                     tag : 'input',
44315                                     cls : 'form-control roo-money-currency-input',
44316                                     autocomplete: 'new-password',
44317                                     readOnly : 1,
44318                                     name : this.currencyName
44319                                 },
44320                                 {
44321                                     tag :'span',
44322                                     cls : 'input-group-addon',
44323                                     cn : [
44324                                         {
44325                                             tag: 'span',
44326                                             cls: 'caret'
44327                                         }
44328                                     ]
44329                                 }
44330                             ]
44331                         }
44332                     ]
44333                 },
44334                 {
44335                     tag : 'div',
44336                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44337                     cn : [
44338                         {
44339                             tag: 'div',
44340                             cls: this.hasFeedback ? 'has-feedback' : '',
44341                             cn: [
44342                                 input
44343                             ]
44344                         }
44345                     ]
44346                 }
44347             ]
44348             
44349         };
44350         
44351         if (this.fieldLabel.length) {
44352             var indicator = {
44353                 tag: 'i',
44354                 tooltip: 'This field is required'
44355             };
44356
44357             var label = {
44358                 tag: 'label',
44359                 'for':  id,
44360                 cls: 'control-label',
44361                 cn: []
44362             };
44363
44364             var label_text = {
44365                 tag: 'span',
44366                 html: this.fieldLabel
44367             };
44368
44369             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44370             label.cn = [
44371                 indicator,
44372                 label_text
44373             ];
44374
44375             if(this.indicatorpos == 'right') {
44376                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44377                 label.cn = [
44378                     label_text,
44379                     indicator
44380                 ];
44381             }
44382
44383             if(align == 'left') {
44384                 container = {
44385                     tag: 'div',
44386                     cn: [
44387                         container
44388                     ]
44389                 };
44390
44391                 if(this.labelWidth > 12){
44392                     label.style = "width: " + this.labelWidth + 'px';
44393                 }
44394                 if(this.labelWidth < 13 && this.labelmd == 0){
44395                     this.labelmd = this.labelWidth;
44396                 }
44397                 if(this.labellg > 0){
44398                     label.cls += ' col-lg-' + this.labellg;
44399                     input.cls += ' col-lg-' + (12 - this.labellg);
44400                 }
44401                 if(this.labelmd > 0){
44402                     label.cls += ' col-md-' + this.labelmd;
44403                     container.cls += ' col-md-' + (12 - this.labelmd);
44404                 }
44405                 if(this.labelsm > 0){
44406                     label.cls += ' col-sm-' + this.labelsm;
44407                     container.cls += ' col-sm-' + (12 - this.labelsm);
44408                 }
44409                 if(this.labelxs > 0){
44410                     label.cls += ' col-xs-' + this.labelxs;
44411                     container.cls += ' col-xs-' + (12 - this.labelxs);
44412                 }
44413             }
44414         }
44415
44416         cfg.cn = [
44417             label,
44418             container,
44419             hiddenInput
44420         ];
44421         
44422         var settings = this;
44423
44424         ['xs','sm','md','lg'].map(function(size){
44425             if (settings[size]) {
44426                 cfg.cls += ' col-' + size + '-' + settings[size];
44427             }
44428         });
44429         
44430         return cfg;
44431     },
44432     
44433     initEvents : function()
44434     {
44435         this.indicator = this.indicatorEl();
44436         
44437         this.initCurrencyEvent();
44438         
44439         this.initNumberEvent();
44440     },
44441     
44442     initCurrencyEvent : function()
44443     {
44444         if (!this.store) {
44445             throw "can not find store for combo";
44446         }
44447         
44448         this.store = Roo.factory(this.store, Roo.data);
44449         this.store.parent = this;
44450         
44451         this.createList();
44452         
44453         this.triggerEl = this.el.select('.input-group-addon', true).first();
44454         
44455         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44456         
44457         var _this = this;
44458         
44459         (function(){
44460             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44461             _this.list.setWidth(lw);
44462         }).defer(100);
44463         
44464         this.list.on('mouseover', this.onViewOver, this);
44465         this.list.on('mousemove', this.onViewMove, this);
44466         this.list.on('scroll', this.onViewScroll, this);
44467         
44468         if(!this.tpl){
44469             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44470         }
44471         
44472         this.view = new Roo.View(this.list, this.tpl, {
44473             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44474         });
44475         
44476         this.view.on('click', this.onViewClick, this);
44477         
44478         this.store.on('beforeload', this.onBeforeLoad, this);
44479         this.store.on('load', this.onLoad, this);
44480         this.store.on('loadexception', this.onLoadException, this);
44481         
44482         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44483             "up" : function(e){
44484                 this.inKeyMode = true;
44485                 this.selectPrev();
44486             },
44487
44488             "down" : function(e){
44489                 if(!this.isExpanded()){
44490                     this.onTriggerClick();
44491                 }else{
44492                     this.inKeyMode = true;
44493                     this.selectNext();
44494                 }
44495             },
44496
44497             "enter" : function(e){
44498                 this.collapse();
44499                 
44500                 if(this.fireEvent("specialkey", this, e)){
44501                     this.onViewClick(false);
44502                 }
44503                 
44504                 return true;
44505             },
44506
44507             "esc" : function(e){
44508                 this.collapse();
44509             },
44510
44511             "tab" : function(e){
44512                 this.collapse();
44513                 
44514                 if(this.fireEvent("specialkey", this, e)){
44515                     this.onViewClick(false);
44516                 }
44517                 
44518                 return true;
44519             },
44520
44521             scope : this,
44522
44523             doRelay : function(foo, bar, hname){
44524                 if(hname == 'down' || this.scope.isExpanded()){
44525                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44526                 }
44527                 return true;
44528             },
44529
44530             forceKeyDown: true
44531         });
44532         
44533         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44534         
44535     },
44536     
44537     initNumberEvent : function(e)
44538     {
44539         this.inputEl().on("keydown" , this.fireKey,  this);
44540         this.inputEl().on("focus", this.onFocus,  this);
44541         this.inputEl().on("blur", this.onBlur,  this);
44542         
44543         this.inputEl().relayEvent('keyup', this);
44544         
44545         if(this.indicator){
44546             this.indicator.addClass('invisible');
44547         }
44548  
44549         this.originalValue = this.getValue();
44550         
44551         if(this.validationEvent == 'keyup'){
44552             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44553             this.inputEl().on('keyup', this.filterValidation, this);
44554         }
44555         else if(this.validationEvent !== false){
44556             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44557         }
44558         
44559         if(this.selectOnFocus){
44560             this.on("focus", this.preFocus, this);
44561             
44562         }
44563         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44564             this.inputEl().on("keypress", this.filterKeys, this);
44565         } else {
44566             this.inputEl().relayEvent('keypress', this);
44567         }
44568         
44569         var allowed = "0123456789";
44570         
44571         if(this.allowDecimals){
44572             allowed += this.decimalSeparator;
44573         }
44574         
44575         if(this.allowNegative){
44576             allowed += "-";
44577         }
44578         
44579         if(this.thousandsDelimiter) {
44580             allowed += ",";
44581         }
44582         
44583         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44584         
44585         var keyPress = function(e){
44586             
44587             var k = e.getKey();
44588             
44589             var c = e.getCharCode();
44590             
44591             if(
44592                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44593                     allowed.indexOf(String.fromCharCode(c)) === -1
44594             ){
44595                 e.stopEvent();
44596                 return;
44597             }
44598             
44599             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44600                 return;
44601             }
44602             
44603             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44604                 e.stopEvent();
44605             }
44606         };
44607         
44608         this.inputEl().on("keypress", keyPress, this);
44609         
44610     },
44611     
44612     onTriggerClick : function(e)
44613     {   
44614         if(this.disabled){
44615             return;
44616         }
44617         
44618         this.page = 0;
44619         this.loadNext = false;
44620         
44621         if(this.isExpanded()){
44622             this.collapse();
44623             return;
44624         }
44625         
44626         this.hasFocus = true;
44627         
44628         if(this.triggerAction == 'all') {
44629             this.doQuery(this.allQuery, true);
44630             return;
44631         }
44632         
44633         this.doQuery(this.getRawValue());
44634     },
44635     
44636     getCurrency : function()
44637     {   
44638         var v = this.currencyEl().getValue();
44639         
44640         return v;
44641     },
44642     
44643     restrictHeight : function()
44644     {
44645         this.list.alignTo(this.currencyEl(), this.listAlign);
44646         this.list.alignTo(this.currencyEl(), this.listAlign);
44647     },
44648     
44649     onViewClick : function(view, doFocus, el, e)
44650     {
44651         var index = this.view.getSelectedIndexes()[0];
44652         
44653         var r = this.store.getAt(index);
44654         
44655         if(r){
44656             this.onSelect(r, index);
44657         }
44658     },
44659     
44660     onSelect : function(record, index){
44661         
44662         if(this.fireEvent('beforeselect', this, record, index) !== false){
44663         
44664             this.setFromCurrencyData(index > -1 ? record.data : false);
44665             
44666             this.collapse();
44667             
44668             this.fireEvent('select', this, record, index);
44669         }
44670     },
44671     
44672     setFromCurrencyData : function(o)
44673     {
44674         var currency = '';
44675         
44676         this.lastCurrency = o;
44677         
44678         if (this.currencyField) {
44679             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44680         } else {
44681             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44682         }
44683         
44684         this.lastSelectionText = currency;
44685         
44686         //setting default currency
44687         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44688             this.setCurrency(this.defaultCurrency);
44689             return;
44690         }
44691         
44692         this.setCurrency(currency);
44693     },
44694     
44695     setFromData : function(o)
44696     {
44697         var c = {};
44698         
44699         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44700         
44701         this.setFromCurrencyData(c);
44702         
44703         var value = '';
44704         
44705         if (this.name) {
44706             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44707         } else {
44708             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44709         }
44710         
44711         this.setValue(value);
44712         
44713     },
44714     
44715     setCurrency : function(v)
44716     {   
44717         this.currencyValue = v;
44718         
44719         if(this.rendered){
44720             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44721             this.validate();
44722         }
44723     },
44724     
44725     setValue : function(v)
44726     {
44727         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44728         
44729         this.value = v;
44730         
44731         if(this.rendered){
44732             
44733             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44734             
44735             this.inputEl().dom.value = (v == '') ? '' :
44736                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44737             
44738             if(!this.allowZero && v === '0') {
44739                 this.hiddenEl().dom.value = '';
44740                 this.inputEl().dom.value = '';
44741             }
44742             
44743             this.validate();
44744         }
44745     },
44746     
44747     getRawValue : function()
44748     {
44749         var v = this.inputEl().getValue();
44750         
44751         return v;
44752     },
44753     
44754     getValue : function()
44755     {
44756         return this.fixPrecision(this.parseValue(this.getRawValue()));
44757     },
44758     
44759     parseValue : function(value)
44760     {
44761         if(this.thousandsDelimiter) {
44762             value += "";
44763             r = new RegExp(",", "g");
44764             value = value.replace(r, "");
44765         }
44766         
44767         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44768         return isNaN(value) ? '' : value;
44769         
44770     },
44771     
44772     fixPrecision : function(value)
44773     {
44774         if(this.thousandsDelimiter) {
44775             value += "";
44776             r = new RegExp(",", "g");
44777             value = value.replace(r, "");
44778         }
44779         
44780         var nan = isNaN(value);
44781         
44782         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44783             return nan ? '' : value;
44784         }
44785         return parseFloat(value).toFixed(this.decimalPrecision);
44786     },
44787     
44788     decimalPrecisionFcn : function(v)
44789     {
44790         return Math.floor(v);
44791     },
44792     
44793     validateValue : function(value)
44794     {
44795         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44796             return false;
44797         }
44798         
44799         var num = this.parseValue(value);
44800         
44801         if(isNaN(num)){
44802             this.markInvalid(String.format(this.nanText, value));
44803             return false;
44804         }
44805         
44806         if(num < this.minValue){
44807             this.markInvalid(String.format(this.minText, this.minValue));
44808             return false;
44809         }
44810         
44811         if(num > this.maxValue){
44812             this.markInvalid(String.format(this.maxText, this.maxValue));
44813             return false;
44814         }
44815         
44816         return true;
44817     },
44818     
44819     validate : function()
44820     {
44821         if(this.disabled || this.allowBlank){
44822             this.markValid();
44823             return true;
44824         }
44825         
44826         var currency = this.getCurrency();
44827         
44828         if(this.validateValue(this.getRawValue()) && currency.length){
44829             this.markValid();
44830             return true;
44831         }
44832         
44833         this.markInvalid();
44834         return false;
44835     },
44836     
44837     getName: function()
44838     {
44839         return this.name;
44840     },
44841     
44842     beforeBlur : function()
44843     {
44844         if(!this.castInt){
44845             return;
44846         }
44847         
44848         var v = this.parseValue(this.getRawValue());
44849         
44850         if(v || v == 0){
44851             this.setValue(v);
44852         }
44853     },
44854     
44855     onBlur : function()
44856     {
44857         this.beforeBlur();
44858         
44859         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44860             //this.el.removeClass(this.focusClass);
44861         }
44862         
44863         this.hasFocus = false;
44864         
44865         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44866             this.validate();
44867         }
44868         
44869         var v = this.getValue();
44870         
44871         if(String(v) !== String(this.startValue)){
44872             this.fireEvent('change', this, v, this.startValue);
44873         }
44874         
44875         this.fireEvent("blur", this);
44876     },
44877     
44878     inputEl : function()
44879     {
44880         return this.el.select('.roo-money-amount-input', true).first();
44881     },
44882     
44883     currencyEl : function()
44884     {
44885         return this.el.select('.roo-money-currency-input', true).first();
44886     },
44887     
44888     hiddenEl : function()
44889     {
44890         return this.el.select('input.hidden-number-input',true).first();
44891     }
44892     
44893 });/**
44894  * @class Roo.bootstrap.BezierSignature
44895  * @extends Roo.bootstrap.Component
44896  * Bootstrap BezierSignature class
44897  * This script refer to:
44898  *    Title: Signature Pad
44899  *    Author: szimek
44900  *    Availability: https://github.com/szimek/signature_pad
44901  *
44902  * @constructor
44903  * Create a new BezierSignature
44904  * @param {Object} config The config object
44905  */
44906
44907 Roo.bootstrap.BezierSignature = function(config){
44908     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44909     this.addEvents({
44910         "resize" : true
44911     });
44912 };
44913
44914 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44915 {
44916      
44917     curve_data: [],
44918     
44919     is_empty: true,
44920     
44921     mouse_btn_down: true,
44922     
44923     /**
44924      * @cfg {int} canvas height
44925      */
44926     canvas_height: '200px',
44927     
44928     /**
44929      * @cfg {float|function} Radius of a single dot.
44930      */ 
44931     dot_size: false,
44932     
44933     /**
44934      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44935      */
44936     min_width: 0.5,
44937     
44938     /**
44939      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44940      */
44941     max_width: 2.5,
44942     
44943     /**
44944      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44945      */
44946     throttle: 16,
44947     
44948     /**
44949      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44950      */
44951     min_distance: 5,
44952     
44953     /**
44954      * @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.
44955      */
44956     bg_color: 'rgba(0, 0, 0, 0)',
44957     
44958     /**
44959      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44960      */
44961     dot_color: 'black',
44962     
44963     /**
44964      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44965      */ 
44966     velocity_filter_weight: 0.7,
44967     
44968     /**
44969      * @cfg {function} Callback when stroke begin. 
44970      */
44971     onBegin: false,
44972     
44973     /**
44974      * @cfg {function} Callback when stroke end.
44975      */
44976     onEnd: false,
44977     
44978     getAutoCreate : function()
44979     {
44980         var cls = 'roo-signature column';
44981         
44982         if(this.cls){
44983             cls += ' ' + this.cls;
44984         }
44985         
44986         var col_sizes = [
44987             'lg',
44988             'md',
44989             'sm',
44990             'xs'
44991         ];
44992         
44993         for(var i = 0; i < col_sizes.length; i++) {
44994             if(this[col_sizes[i]]) {
44995                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44996             }
44997         }
44998         
44999         var cfg = {
45000             tag: 'div',
45001             cls: cls,
45002             cn: [
45003                 {
45004                     tag: 'div',
45005                     cls: 'roo-signature-body',
45006                     cn: [
45007                         {
45008                             tag: 'canvas',
45009                             cls: 'roo-signature-body-canvas',
45010                             height: this.canvas_height,
45011                             width: this.canvas_width
45012                         }
45013                     ]
45014                 },
45015                 {
45016                     tag: 'input',
45017                     type: 'file',
45018                     style: 'display: none'
45019                 }
45020             ]
45021         };
45022         
45023         return cfg;
45024     },
45025     
45026     initEvents: function() 
45027     {
45028         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45029         
45030         var canvas = this.canvasEl();
45031         
45032         // mouse && touch event swapping...
45033         canvas.dom.style.touchAction = 'none';
45034         canvas.dom.style.msTouchAction = 'none';
45035         
45036         this.mouse_btn_down = false;
45037         canvas.on('mousedown', this._handleMouseDown, this);
45038         canvas.on('mousemove', this._handleMouseMove, this);
45039         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45040         
45041         if (window.PointerEvent) {
45042             canvas.on('pointerdown', this._handleMouseDown, this);
45043             canvas.on('pointermove', this._handleMouseMove, this);
45044             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45045         }
45046         
45047         if ('ontouchstart' in window) {
45048             canvas.on('touchstart', this._handleTouchStart, this);
45049             canvas.on('touchmove', this._handleTouchMove, this);
45050             canvas.on('touchend', this._handleTouchEnd, this);
45051         }
45052         
45053         Roo.EventManager.onWindowResize(this.resize, this, true);
45054         
45055         // file input event
45056         this.fileEl().on('change', this.uploadImage, this);
45057         
45058         this.clear();
45059         
45060         this.resize();
45061     },
45062     
45063     resize: function(){
45064         
45065         var canvas = this.canvasEl().dom;
45066         var ctx = this.canvasElCtx();
45067         var img_data = false;
45068         
45069         if(canvas.width > 0) {
45070             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45071         }
45072         // setting canvas width will clean img data
45073         canvas.width = 0;
45074         
45075         var style = window.getComputedStyle ? 
45076             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45077             
45078         var padding_left = parseInt(style.paddingLeft) || 0;
45079         var padding_right = parseInt(style.paddingRight) || 0;
45080         
45081         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45082         
45083         if(img_data) {
45084             ctx.putImageData(img_data, 0, 0);
45085         }
45086     },
45087     
45088     _handleMouseDown: function(e)
45089     {
45090         if (e.browserEvent.which === 1) {
45091             this.mouse_btn_down = true;
45092             this.strokeBegin(e);
45093         }
45094     },
45095     
45096     _handleMouseMove: function (e)
45097     {
45098         if (this.mouse_btn_down) {
45099             this.strokeMoveUpdate(e);
45100         }
45101     },
45102     
45103     _handleMouseUp: function (e)
45104     {
45105         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45106             this.mouse_btn_down = false;
45107             this.strokeEnd(e);
45108         }
45109     },
45110     
45111     _handleTouchStart: function (e) {
45112         
45113         e.preventDefault();
45114         if (e.browserEvent.targetTouches.length === 1) {
45115             // var touch = e.browserEvent.changedTouches[0];
45116             // this.strokeBegin(touch);
45117             
45118              this.strokeBegin(e); // assume e catching the correct xy...
45119         }
45120     },
45121     
45122     _handleTouchMove: function (e) {
45123         e.preventDefault();
45124         // var touch = event.targetTouches[0];
45125         // _this._strokeMoveUpdate(touch);
45126         this.strokeMoveUpdate(e);
45127     },
45128     
45129     _handleTouchEnd: function (e) {
45130         var wasCanvasTouched = e.target === this.canvasEl().dom;
45131         if (wasCanvasTouched) {
45132             e.preventDefault();
45133             // var touch = event.changedTouches[0];
45134             // _this._strokeEnd(touch);
45135             this.strokeEnd(e);
45136         }
45137     },
45138     
45139     reset: function () {
45140         this._lastPoints = [];
45141         this._lastVelocity = 0;
45142         this._lastWidth = (this.min_width + this.max_width) / 2;
45143         this.canvasElCtx().fillStyle = this.dot_color;
45144     },
45145     
45146     strokeMoveUpdate: function(e)
45147     {
45148         this.strokeUpdate(e);
45149         
45150         if (this.throttle) {
45151             this.throttleStroke(this.strokeUpdate, this.throttle);
45152         }
45153         else {
45154             this.strokeUpdate(e);
45155         }
45156     },
45157     
45158     strokeBegin: function(e)
45159     {
45160         var newPointGroup = {
45161             color: this.dot_color,
45162             points: []
45163         };
45164         
45165         if (typeof this.onBegin === 'function') {
45166             this.onBegin(e);
45167         }
45168         
45169         this.curve_data.push(newPointGroup);
45170         this.reset();
45171         this.strokeUpdate(e);
45172     },
45173     
45174     strokeUpdate: function(e)
45175     {
45176         var rect = this.canvasEl().dom.getBoundingClientRect();
45177         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45178         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45179         var lastPoints = lastPointGroup.points;
45180         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45181         var isLastPointTooClose = lastPoint
45182             ? point.distanceTo(lastPoint) <= this.min_distance
45183             : false;
45184         var color = lastPointGroup.color;
45185         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45186             var curve = this.addPoint(point);
45187             if (!lastPoint) {
45188                 this.drawDot({color: color, point: point});
45189             }
45190             else if (curve) {
45191                 this.drawCurve({color: color, curve: curve});
45192             }
45193             lastPoints.push({
45194                 time: point.time,
45195                 x: point.x,
45196                 y: point.y
45197             });
45198         }
45199     },
45200     
45201     strokeEnd: function(e)
45202     {
45203         this.strokeUpdate(e);
45204         if (typeof this.onEnd === 'function') {
45205             this.onEnd(e);
45206         }
45207     },
45208     
45209     addPoint:  function (point) {
45210         var _lastPoints = this._lastPoints;
45211         _lastPoints.push(point);
45212         if (_lastPoints.length > 2) {
45213             if (_lastPoints.length === 3) {
45214                 _lastPoints.unshift(_lastPoints[0]);
45215             }
45216             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45217             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45218             _lastPoints.shift();
45219             return curve;
45220         }
45221         return null;
45222     },
45223     
45224     calculateCurveWidths: function (startPoint, endPoint) {
45225         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45226             (1 - this.velocity_filter_weight) * this._lastVelocity;
45227
45228         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45229         var widths = {
45230             end: newWidth,
45231             start: this._lastWidth
45232         };
45233         
45234         this._lastVelocity = velocity;
45235         this._lastWidth = newWidth;
45236         return widths;
45237     },
45238     
45239     drawDot: function (_a) {
45240         var color = _a.color, point = _a.point;
45241         var ctx = this.canvasElCtx();
45242         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45243         ctx.beginPath();
45244         this.drawCurveSegment(point.x, point.y, width);
45245         ctx.closePath();
45246         ctx.fillStyle = color;
45247         ctx.fill();
45248     },
45249     
45250     drawCurve: function (_a) {
45251         var color = _a.color, curve = _a.curve;
45252         var ctx = this.canvasElCtx();
45253         var widthDelta = curve.endWidth - curve.startWidth;
45254         var drawSteps = Math.floor(curve.length()) * 2;
45255         ctx.beginPath();
45256         ctx.fillStyle = color;
45257         for (var i = 0; i < drawSteps; i += 1) {
45258         var t = i / drawSteps;
45259         var tt = t * t;
45260         var ttt = tt * t;
45261         var u = 1 - t;
45262         var uu = u * u;
45263         var uuu = uu * u;
45264         var x = uuu * curve.startPoint.x;
45265         x += 3 * uu * t * curve.control1.x;
45266         x += 3 * u * tt * curve.control2.x;
45267         x += ttt * curve.endPoint.x;
45268         var y = uuu * curve.startPoint.y;
45269         y += 3 * uu * t * curve.control1.y;
45270         y += 3 * u * tt * curve.control2.y;
45271         y += ttt * curve.endPoint.y;
45272         var width = curve.startWidth + ttt * widthDelta;
45273         this.drawCurveSegment(x, y, width);
45274         }
45275         ctx.closePath();
45276         ctx.fill();
45277     },
45278     
45279     drawCurveSegment: function (x, y, width) {
45280         var ctx = this.canvasElCtx();
45281         ctx.moveTo(x, y);
45282         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45283         this.is_empty = false;
45284     },
45285     
45286     clear: function()
45287     {
45288         var ctx = this.canvasElCtx();
45289         var canvas = this.canvasEl().dom;
45290         ctx.fillStyle = this.bg_color;
45291         ctx.clearRect(0, 0, canvas.width, canvas.height);
45292         ctx.fillRect(0, 0, canvas.width, canvas.height);
45293         this.curve_data = [];
45294         this.reset();
45295         this.is_empty = true;
45296     },
45297     
45298     fileEl: function()
45299     {
45300         return  this.el.select('input',true).first();
45301     },
45302     
45303     canvasEl: function()
45304     {
45305         return this.el.select('canvas',true).first();
45306     },
45307     
45308     canvasElCtx: function()
45309     {
45310         return this.el.select('canvas',true).first().dom.getContext('2d');
45311     },
45312     
45313     getImage: function(type)
45314     {
45315         if(this.is_empty) {
45316             return false;
45317         }
45318         
45319         // encryption ?
45320         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45321     },
45322     
45323     drawFromImage: function(img_src)
45324     {
45325         var img = new Image();
45326         
45327         img.onload = function(){
45328             this.canvasElCtx().drawImage(img, 0, 0);
45329         }.bind(this);
45330         
45331         img.src = img_src;
45332         
45333         this.is_empty = false;
45334     },
45335     
45336     selectImage: function()
45337     {
45338         this.fileEl().dom.click();
45339     },
45340     
45341     uploadImage: function(e)
45342     {
45343         var reader = new FileReader();
45344         
45345         reader.onload = function(e){
45346             var img = new Image();
45347             img.onload = function(){
45348                 this.reset();
45349                 this.canvasElCtx().drawImage(img, 0, 0);
45350             }.bind(this);
45351             img.src = e.target.result;
45352         }.bind(this);
45353         
45354         reader.readAsDataURL(e.target.files[0]);
45355     },
45356     
45357     // Bezier Point Constructor
45358     Point: (function () {
45359         function Point(x, y, time) {
45360             this.x = x;
45361             this.y = y;
45362             this.time = time || Date.now();
45363         }
45364         Point.prototype.distanceTo = function (start) {
45365             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45366         };
45367         Point.prototype.equals = function (other) {
45368             return this.x === other.x && this.y === other.y && this.time === other.time;
45369         };
45370         Point.prototype.velocityFrom = function (start) {
45371             return this.time !== start.time
45372             ? this.distanceTo(start) / (this.time - start.time)
45373             : 0;
45374         };
45375         return Point;
45376     }()),
45377     
45378     
45379     // Bezier Constructor
45380     Bezier: (function () {
45381         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45382             this.startPoint = startPoint;
45383             this.control2 = control2;
45384             this.control1 = control1;
45385             this.endPoint = endPoint;
45386             this.startWidth = startWidth;
45387             this.endWidth = endWidth;
45388         }
45389         Bezier.fromPoints = function (points, widths, scope) {
45390             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45391             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45392             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45393         };
45394         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45395             var dx1 = s1.x - s2.x;
45396             var dy1 = s1.y - s2.y;
45397             var dx2 = s2.x - s3.x;
45398             var dy2 = s2.y - s3.y;
45399             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45400             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45401             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45402             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45403             var dxm = m1.x - m2.x;
45404             var dym = m1.y - m2.y;
45405             var k = l2 / (l1 + l2);
45406             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45407             var tx = s2.x - cm.x;
45408             var ty = s2.y - cm.y;
45409             return {
45410                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45411                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45412             };
45413         };
45414         Bezier.prototype.length = function () {
45415             var steps = 10;
45416             var length = 0;
45417             var px;
45418             var py;
45419             for (var i = 0; i <= steps; i += 1) {
45420                 var t = i / steps;
45421                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45422                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45423                 if (i > 0) {
45424                     var xdiff = cx - px;
45425                     var ydiff = cy - py;
45426                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45427                 }
45428                 px = cx;
45429                 py = cy;
45430             }
45431             return length;
45432         };
45433         Bezier.prototype.point = function (t, start, c1, c2, end) {
45434             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45435             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45436             + (3.0 * c2 * (1.0 - t) * t * t)
45437             + (end * t * t * t);
45438         };
45439         return Bezier;
45440     }()),
45441     
45442     throttleStroke: function(fn, wait) {
45443       if (wait === void 0) { wait = 250; }
45444       var previous = 0;
45445       var timeout = null;
45446       var result;
45447       var storedContext;
45448       var storedArgs;
45449       var later = function () {
45450           previous = Date.now();
45451           timeout = null;
45452           result = fn.apply(storedContext, storedArgs);
45453           if (!timeout) {
45454               storedContext = null;
45455               storedArgs = [];
45456           }
45457       };
45458       return function wrapper() {
45459           var args = [];
45460           for (var _i = 0; _i < arguments.length; _i++) {
45461               args[_i] = arguments[_i];
45462           }
45463           var now = Date.now();
45464           var remaining = wait - (now - previous);
45465           storedContext = this;
45466           storedArgs = args;
45467           if (remaining <= 0 || remaining > wait) {
45468               if (timeout) {
45469                   clearTimeout(timeout);
45470                   timeout = null;
45471               }
45472               previous = now;
45473               result = fn.apply(storedContext, storedArgs);
45474               if (!timeout) {
45475                   storedContext = null;
45476                   storedArgs = [];
45477               }
45478           }
45479           else if (!timeout) {
45480               timeout = window.setTimeout(later, remaining);
45481           }
45482           return result;
45483       };
45484   }
45485   
45486 });
45487
45488  
45489
45490