roojs-ui.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * Bootstrap Component base class
227  * @cfg {String} cls css class
228  * @cfg {String} style any extra css
229  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
230  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
231  * @cfg {string} dataId cutomer id
232  * @cfg {string} name Specifies name attribute
233  * @cfg {string} tooltip  Text for the tooltip
234  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
235  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
236  
237  * @constructor
238  * Do not use directly - it does not do anything..
239  * @param {Object} config The config object
240  */
241
242
243
244 Roo.bootstrap.Component = function(config){
245     Roo.bootstrap.Component.superclass.constructor.call(this, config);
246        
247     this.addEvents({
248         /**
249          * @event childrenrendered
250          * Fires when the children have been rendered..
251          * @param {Roo.bootstrap.Component} this
252          */
253         "childrenrendered" : true
254         
255         
256         
257     });
258     
259     
260 };
261
262 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
263     
264     
265     allowDomMove : false, // to stop relocations in parent onRender...
266     
267     cls : false,
268     
269     style : false,
270     
271     autoCreate : false,
272     
273     tooltip : null,
274     /**
275      * Initialize Events for the element
276      */
277     initEvents : function() { },
278     
279     xattr : false,
280     
281     parentId : false,
282     
283     can_build_overlaid : true,
284     
285     container_method : false,
286     
287     dataId : false,
288     
289     name : false,
290     
291     parent: function() {
292         // returns the parent component..
293         return Roo.ComponentMgr.get(this.parentId)
294         
295         
296     },
297     
298     // private
299     onRender : function(ct, position)
300     {
301        // Roo.log("Call onRender: " + this.xtype);
302         
303         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
304         
305         if(this.el){
306             if (this.el.attr('xtype')) {
307                 this.el.attr('xtypex', this.el.attr('xtype'));
308                 this.el.dom.removeAttribute('xtype');
309                 
310                 this.initEvents();
311             }
312             
313             return;
314         }
315         
316          
317         
318         var cfg = Roo.apply({},  this.getAutoCreate());
319         
320         cfg.id = this.id || Roo.id();
321         
322         // fill in the extra attributes 
323         if (this.xattr && typeof(this.xattr) =='object') {
324             for (var i in this.xattr) {
325                 cfg[i] = this.xattr[i];
326             }
327         }
328         
329         if(this.dataId){
330             cfg.dataId = this.dataId;
331         }
332         
333         if (this.cls) {
334             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
335         }
336         
337         if (this.style) { // fixme needs to support more complex style data.
338             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
339         }
340         
341         if(this.name){
342             cfg.name = this.name;
343         }
344         
345         this.el = ct.createChild(cfg, position);
346         
347         if (this.tooltip) {
348             this.tooltipEl().attr('tooltip', this.tooltip);
349         }
350         
351         if(this.tabIndex !== undefined){
352             this.el.dom.setAttribute('tabIndex', this.tabIndex);
353         }
354         
355         this.initEvents();
356         
357     },
358     /**
359      * Fetch the element to add children to
360      * @return {Roo.Element} defaults to this.el
361      */
362     getChildContainer : function()
363     {
364         return this.el;
365     },
366     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367     {
368         return Roo.get(document.body);
369     },
370     
371     /**
372      * Fetch the element to display the tooltip on.
373      * @return {Roo.Element} defaults to this.el
374      */
375     tooltipEl : function()
376     {
377         return this.el;
378     },
379         
380     addxtype  : function(tree,cntr)
381     {
382         var cn = this;
383         
384         cn = Roo.factory(tree);
385         //Roo.log(['addxtype', cn]);
386            
387         cn.parentType = this.xtype; //??
388         cn.parentId = this.id;
389         
390         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
391         if (typeof(cn.container_method) == 'string') {
392             cntr = cn.container_method;
393         }
394         
395         
396         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
397         
398         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
399         
400         var build_from_html =  Roo.XComponent.build_from_html;
401           
402         var is_body  = (tree.xtype == 'Body') ;
403           
404         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405           
406         var self_cntr_el = Roo.get(this[cntr](false));
407         
408         // do not try and build conditional elements 
409         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
410             return false;
411         }
412         
413         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
414             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
415                 return this.addxtypeChild(tree,cntr, is_body);
416             }
417             
418             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
419                 
420             if(echild){
421                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
422             }
423             
424             Roo.log('skipping render');
425             return cn;
426             
427         }
428         
429         var ret = false;
430         if (!build_from_html) {
431             return false;
432         }
433         
434         // this i think handles overlaying multiple children of the same type
435         // with the sam eelement.. - which might be buggy..
436         while (true) {
437             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
438             
439             if (!echild) {
440                 break;
441             }
442             
443             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
444                 break;
445             }
446             
447             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448         }
449        
450         return ret;
451     },
452     
453     
454     addxtypeChild : function (tree, cntr, is_body)
455     {
456         Roo.debug && Roo.log('addxtypeChild:' + cntr);
457         var cn = this;
458         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
459         
460         
461         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
462                     (typeof(tree['flexy:foreach']) != 'undefined');
463           
464     
465         
466         skip_children = false;
467         // render the element if it's not BODY.
468         if (!is_body) {
469             
470             // if parent was disabled, then do not try and create the children..
471             if(!this[cntr](true)){
472                 tree.items = [];
473                 return tree;
474             }
475            
476             cn = Roo.factory(tree);
477            
478             cn.parentType = this.xtype; //??
479             cn.parentId = this.id;
480             
481             var build_from_html =  Roo.XComponent.build_from_html;
482             
483             
484             // does the container contain child eleemnts with 'xtype' attributes.
485             // that match this xtype..
486             // note - when we render we create these as well..
487             // so we should check to see if body has xtype set.
488             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489                
490                 var self_cntr_el = Roo.get(this[cntr](false));
491                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492                 if (echild) { 
493                     //Roo.log(Roo.XComponent.build_from_html);
494                     //Roo.log("got echild:");
495                     //Roo.log(echild);
496                 }
497                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
498                 // and are not displayed -this causes this to use up the wrong element when matching.
499                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
500                 
501                 
502                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
503                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
504                   
505                   
506                   
507                     cn.el = echild;
508                   //  Roo.log("GOT");
509                     //echild.dom.removeAttribute('xtype');
510                 } else {
511                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
512                     Roo.debug && Roo.log(self_cntr_el);
513                     Roo.debug && Roo.log(echild);
514                     Roo.debug && Roo.log(cn);
515                 }
516             }
517            
518             
519            
520             // if object has flexy:if - then it may or may not be rendered.
521             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
522                 // skip a flexy if element.
523                 Roo.debug && Roo.log('skipping render');
524                 Roo.debug && Roo.log(tree);
525                 if (!cn.el) {
526                     Roo.debug && Roo.log('skipping all children');
527                     skip_children = true;
528                 }
529                 
530              } else {
531                  
532                 // actually if flexy:foreach is found, we really want to create 
533                 // multiple copies here...
534                 //Roo.log('render');
535                 //Roo.log(this[cntr]());
536                 // some elements do not have render methods.. like the layouts...
537                 /*
538                 if(this[cntr](true) === false){
539                     cn.items = [];
540                     return cn;
541                 }
542                 */
543                 cn.render && cn.render(this[cntr](true));
544                 
545              }
546             // then add the element..
547         }
548          
549         // handle the kids..
550         
551         var nitems = [];
552         /*
553         if (typeof (tree.menu) != 'undefined') {
554             tree.menu.parentType = cn.xtype;
555             tree.menu.triggerEl = cn.el;
556             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
557             
558         }
559         */
560         if (!tree.items || !tree.items.length) {
561             cn.items = nitems;
562             //Roo.log(["no children", this]);
563             
564             return cn;
565         }
566          
567         var items = tree.items;
568         delete tree.items;
569         
570         //Roo.log(items.length);
571             // add the items..
572         if (!skip_children) {    
573             for(var i =0;i < items.length;i++) {
574               //  Roo.log(['add child', items[i]]);
575                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
576             }
577         }
578         
579         cn.items = nitems;
580         
581         //Roo.log("fire childrenrendered");
582         
583         cn.fireEvent('childrenrendered', this);
584         
585         return cn;
586     },
587     
588     /**
589      * Set the element that will be used to show or hide
590      */
591     setVisibilityEl : function(el)
592     {
593         this.visibilityEl = el;
594     },
595     
596      /**
597      * Get the element that will be used to show or hide
598      */
599     getVisibilityEl : function()
600     {
601         if (typeof(this.visibilityEl) == 'object') {
602             return this.visibilityEl;
603         }
604         
605         if (typeof(this.visibilityEl) == 'string') {
606             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607         }
608         
609         return this.getEl();
610     },
611     
612     /**
613      * Show a component - removes 'hidden' class
614      */
615     show : function()
616     {
617         if(!this.getVisibilityEl()){
618             return;
619         }
620          
621         this.getVisibilityEl().removeClass(['hidden','d-none']);
622         
623         this.fireEvent('show', this);
624         
625         
626     },
627     /**
628      * Hide a component - adds 'hidden' class
629      */
630     hide: function()
631     {
632         if(!this.getVisibilityEl()){
633             return;
634         }
635         
636         this.getVisibilityEl().addClass(['hidden','d-none']);
637         
638         this.fireEvent('hide', this);
639         
640     }
641 });
642
643  /*
644  * - LGPL
645  *
646  * element
647  * 
648  */
649
650 /**
651  * @class Roo.bootstrap.Element
652  * @extends Roo.bootstrap.Component
653  * Bootstrap Element class
654  * @cfg {String} html contents of the element
655  * @cfg {String} tag tag of the element
656  * @cfg {String} cls class of the element
657  * @cfg {Boolean} preventDefault (true|false) default false
658  * @cfg {Boolean} clickable (true|false) default false
659  * @cfg {String} role default blank - set to button to force cursor pointer
660  
661  * 
662  * @constructor
663  * Create a new Element
664  * @param {Object} config The config object
665  */
666
667 Roo.bootstrap.Element = function(config){
668     Roo.bootstrap.Element.superclass.constructor.call(this, config);
669     
670     this.addEvents({
671         // raw events
672         /**
673          * @event click
674          * When a element is chick
675          * @param {Roo.bootstrap.Element} this
676          * @param {Roo.EventObject} e
677          */
678         "click" : true 
679         
680       
681     });
682 };
683
684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
685     
686     tag: 'div',
687     cls: '',
688     html: '',
689     preventDefault: false, 
690     clickable: false,
691     tapedTwice : false,
692     role : false,
693     
694     getAutoCreate : function(){
695         
696         var cfg = {
697             tag: this.tag,
698             // cls: this.cls, double assign in parent class Component.js :: onRender
699             html: this.html
700         };
701         if (this.role !== false) {
702             cfg.role = this.role;
703         }
704         
705         return cfg;
706     },
707     
708     initEvents: function() 
709     {
710         Roo.bootstrap.Element.superclass.initEvents.call(this);
711         
712         if(this.clickable){
713             this.el.on('click', this.onClick, this);
714         }
715         
716         
717     },
718     
719     onClick : function(e)
720     {
721         if(this.preventDefault){
722             e.preventDefault();
723         }
724         
725         this.fireEvent('click', this, e); // why was this double click before?
726     },
727     
728     
729     
730
731     
732     
733     getValue : function()
734     {
735         return this.el.dom.innerHTML;
736     },
737     
738     setValue : function(value)
739     {
740         this.el.dom.innerHTML = value;
741     }
742    
743 });
744
745  
746
747  /*
748  * - LGPL
749  *
750  * dropable area
751  * 
752  */
753
754 /**
755  * @class Roo.bootstrap.DropTarget
756  * @extends Roo.bootstrap.Element
757  * Bootstrap DropTarget class
758  
759  * @cfg {string} name dropable name
760  * 
761  * @constructor
762  * Create a new Dropable Area
763  * @param {Object} config The config object
764  */
765
766 Roo.bootstrap.DropTarget = function(config){
767     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
768     
769     this.addEvents({
770         // raw events
771         /**
772          * @event click
773          * When a element is chick
774          * @param {Roo.bootstrap.Element} this
775          * @param {Roo.EventObject} e
776          */
777         "drop" : true
778     });
779 };
780
781 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
782     
783     
784     getAutoCreate : function(){
785         
786          
787     },
788     
789     initEvents: function() 
790     {
791         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
792         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
793             ddGroup: this.name,
794             listeners : {
795                 drop : this.dragDrop.createDelegate(this),
796                 enter : this.dragEnter.createDelegate(this),
797                 out : this.dragOut.createDelegate(this),
798                 over : this.dragOver.createDelegate(this)
799             }
800             
801         });
802         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
803     },
804     
805     dragDrop : function(source,e,data)
806     {
807         // user has to decide how to impliment this.
808         Roo.log('drop');
809         Roo.log(this);
810         //this.fireEvent('drop', this, source, e ,data);
811         return false;
812     },
813     
814     dragEnter : function(n, dd, e, data)
815     {
816         // probably want to resize the element to match the dropped element..
817         Roo.log("enter");
818         this.originalSize = this.el.getSize();
819         this.el.setSize( n.el.getSize());
820         this.dropZone.DDM.refreshCache(this.name);
821         Roo.log([n, dd, e, data]);
822     },
823     
824     dragOut : function(value)
825     {
826         // resize back to normal
827         Roo.log("out");
828         this.el.setSize(this.originalSize);
829         this.dropZone.resetConstraints();
830     },
831     
832     dragOver : function()
833     {
834         // ??? do nothing?
835     }
836    
837 });
838
839  
840
841  /*
842  * - LGPL
843  *
844  * Body
845  *
846  */
847
848 /**
849  * @class Roo.bootstrap.Body
850  * @extends Roo.bootstrap.Component
851  * Bootstrap Body class
852  *
853  * @constructor
854  * Create a new body
855  * @param {Object} config The config object
856  */
857
858 Roo.bootstrap.Body = function(config){
859
860     config = config || {};
861
862     Roo.bootstrap.Body.superclass.constructor.call(this, config);
863     this.el = Roo.get(config.el ? config.el : document.body );
864     if (this.cls && this.cls.length) {
865         Roo.get(document.body).addClass(this.cls);
866     }
867 };
868
869 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
870
871     is_body : true,// just to make sure it's constructed?
872
873         autoCreate : {
874         cls: 'container'
875     },
876     onRender : function(ct, position)
877     {
878        /* Roo.log("Roo.bootstrap.Body - onRender");
879         if (this.cls && this.cls.length) {
880             Roo.get(document.body).addClass(this.cls);
881         }
882         // style??? xttr???
883         */
884     }
885
886
887
888
889 });
890 /*
891  * - LGPL
892  *
893  * button group
894  * 
895  */
896
897
898 /**
899  * @class Roo.bootstrap.ButtonGroup
900  * @extends Roo.bootstrap.Component
901  * Bootstrap ButtonGroup class
902  * @cfg {String} size lg | sm | xs (default empty normal)
903  * @cfg {String} align vertical | justified  (default none)
904  * @cfg {String} direction up | down (default down)
905  * @cfg {Boolean} toolbar false | true
906  * @cfg {Boolean} btn true | false
907  * 
908  * 
909  * @constructor
910  * Create a new Input
911  * @param {Object} config The config object
912  */
913
914 Roo.bootstrap.ButtonGroup = function(config){
915     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
916 };
917
918 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
919     
920     size: '',
921     align: '',
922     direction: '',
923     toolbar: false,
924     btn: true,
925
926     getAutoCreate : function(){
927         var cfg = {
928             cls: 'btn-group',
929             html : null
930         };
931         
932         cfg.html = this.html || cfg.html;
933         
934         if (this.toolbar) {
935             cfg = {
936                 cls: 'btn-toolbar',
937                 html: null
938             };
939             
940             return cfg;
941         }
942         
943         if (['vertical','justified'].indexOf(this.align)!==-1) {
944             cfg.cls = 'btn-group-' + this.align;
945             
946             if (this.align == 'justified') {
947                 console.log(this.items);
948             }
949         }
950         
951         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
952             cfg.cls += ' btn-group-' + this.size;
953         }
954         
955         if (this.direction == 'up') {
956             cfg.cls += ' dropup' ;
957         }
958         
959         return cfg;
960     },
961     /**
962      * Add a button to the group (similar to NavItem API.)
963      */
964     addItem : function(cfg)
965     {
966         var cn = new Roo.bootstrap.Button(cfg);
967         //this.register(cn);
968         cn.parentId = this.id;
969         cn.onRender(this.el, null);
970         return cn;
971     }
972    
973 });
974
975  /*
976  * - LGPL
977  *
978  * button
979  * 
980  */
981
982 /**
983  * @class Roo.bootstrap.Button
984  * @extends Roo.bootstrap.Component
985  * Bootstrap Button class
986  * @cfg {String} html The button content
987  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
988  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
989  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
990  * @cfg {String} size (lg|sm|xs)
991  * @cfg {String} tag (a|input|submit)
992  * @cfg {String} href empty or href
993  * @cfg {Boolean} disabled default false;
994  * @cfg {Boolean} isClose default false;
995  * @cfg {String} glyphicon depricated - use fa
996  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
997  * @cfg {String} badge text for badge
998  * @cfg {String} theme (default|glow)  
999  * @cfg {Boolean} inverse dark themed version
1000  * @cfg {Boolean} toggle is it a slidy toggle button
1001  * @cfg {Boolean} pressed   default null - if the button ahs active state
1002  * @cfg {String} ontext text for on slidy toggle state
1003  * @cfg {String} offtext text for off slidy toggle state
1004  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1005  * @cfg {Boolean} removeClass remove the standard class..
1006  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1007  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1008  * 
1009  * @constructor
1010  * Create a new button
1011  * @param {Object} config The config object
1012  */
1013
1014
1015 Roo.bootstrap.Button = function(config){
1016     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020         /**
1021          * @event click
1022          * When a button is pressed
1023          * @param {Roo.bootstrap.Button} btn
1024          * @param {Roo.EventObject} e
1025          */
1026         "click" : true,
1027         /**
1028          * @event dblclick
1029          * When a button is double clicked
1030          * @param {Roo.bootstrap.Button} btn
1031          * @param {Roo.EventObject} e
1032          */
1033         "dblclick" : true,
1034          /**
1035          * @event toggle
1036          * After the button has been toggles
1037          * @param {Roo.bootstrap.Button} btn
1038          * @param {Roo.EventObject} e
1039          * @param {boolean} pressed (also available as button.pressed)
1040          */
1041         "toggle" : true
1042     });
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1046     html: false,
1047     active: false,
1048     weight: '',
1049     badge_weight: '',
1050     outline : false,
1051     size: '',
1052     tag: 'button',
1053     href: '',
1054     disabled: false,
1055     isClose: false,
1056     glyphicon: '',
1057     fa: '',
1058     badge: '',
1059     theme: 'default',
1060     inverse: false,
1061     
1062     toggle: false,
1063     ontext: 'ON',
1064     offtext: 'OFF',
1065     defaulton: true,
1066     preventDefault: true,
1067     removeClass: false,
1068     name: false,
1069     target: false,
1070     group : false,
1071      
1072     pressed : null,
1073      
1074     
1075     getAutoCreate : function(){
1076         
1077         var cfg = {
1078             tag : 'button',
1079             cls : 'roo-button',
1080             html: ''
1081         };
1082         
1083         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1084             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1085             this.tag = 'button';
1086         } else {
1087             cfg.tag = this.tag;
1088         }
1089         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090         
1091         if (this.toggle == true) {
1092             cfg={
1093                 tag: 'div',
1094                 cls: 'slider-frame roo-button',
1095                 cn: [
1096                     {
1097                         tag: 'span',
1098                         'data-on-text':'ON',
1099                         'data-off-text':'OFF',
1100                         cls: 'slider-button',
1101                         html: this.offtext
1102                     }
1103                 ]
1104             };
1105             // why are we validating the weights?
1106             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1107                 cfg.cls +=  ' ' + this.weight;
1108             }
1109             
1110             return cfg;
1111         }
1112         
1113         if (this.isClose) {
1114             cfg.cls += ' close';
1115             
1116             cfg["aria-hidden"] = true;
1117             
1118             cfg.html = "&times;";
1119             
1120             return cfg;
1121         }
1122              
1123         
1124         if (this.theme==='default') {
1125             cfg.cls = 'btn roo-button';
1126             
1127             //if (this.parentType != 'Navbar') {
1128             this.weight = this.weight.length ?  this.weight : 'default';
1129             //}
1130             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131                 
1132                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1133                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1134                 cfg.cls += ' btn-' + outline + weight;
1135                 if (this.weight == 'default') {
1136                     // BC
1137                     cfg.cls += ' btn-' + this.weight;
1138                 }
1139             }
1140         } else if (this.theme==='glow') {
1141             
1142             cfg.tag = 'a';
1143             cfg.cls = 'btn-glow roo-button';
1144             
1145             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146                 
1147                 cfg.cls += ' ' + this.weight;
1148             }
1149         }
1150    
1151         
1152         if (this.inverse) {
1153             this.cls += ' inverse';
1154         }
1155         
1156         
1157         if (this.active || this.pressed === true) {
1158             cfg.cls += ' active';
1159         }
1160         
1161         if (this.disabled) {
1162             cfg.disabled = 'disabled';
1163         }
1164         
1165         if (this.items) {
1166             Roo.log('changing to ul' );
1167             cfg.tag = 'ul';
1168             this.glyphicon = 'caret';
1169             if (Roo.bootstrap.version == 4) {
1170                 this.fa = 'caret-down';
1171             }
1172             
1173         }
1174         
1175         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176          
1177         //gsRoo.log(this.parentType);
1178         if (this.parentType === 'Navbar' && !this.parent().bar) {
1179             Roo.log('changing to li?');
1180             
1181             cfg.tag = 'li';
1182             
1183             cfg.cls = '';
1184             cfg.cn =  [{
1185                 tag : 'a',
1186                 cls : 'roo-button',
1187                 html : this.html,
1188                 href : this.href || '#'
1189             }];
1190             if (this.menu) {
1191                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1192                 cfg.cls += ' dropdown';
1193             }   
1194             
1195             delete cfg.html;
1196             
1197         }
1198         
1199        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1200         
1201         if (this.glyphicon) {
1202             cfg.html = ' ' + cfg.html;
1203             
1204             cfg.cn = [
1205                 {
1206                     tag: 'span',
1207                     cls: 'glyphicon glyphicon-' + this.glyphicon
1208                 }
1209             ];
1210         }
1211         if (this.fa) {
1212             cfg.html = ' ' + cfg.html;
1213             
1214             cfg.cn = [
1215                 {
1216                     tag: 'i',
1217                     cls: 'fa fas fa-' + this.fa
1218                 }
1219             ];
1220         }
1221         
1222         if (this.badge) {
1223             cfg.html += ' ';
1224             
1225             cfg.tag = 'a';
1226             
1227 //            cfg.cls='btn roo-button';
1228             
1229             cfg.href=this.href;
1230             
1231             var value = cfg.html;
1232             
1233             if(this.glyphicon){
1234                 value = {
1235                     tag: 'span',
1236                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1237                     html: this.html
1238                 };
1239             }
1240             if(this.fa){
1241                 value = {
1242                     tag: 'i',
1243                     cls: 'fa fas fa-' + this.fa,
1244                     html: this.html
1245                 };
1246             }
1247             
1248             var bw = this.badge_weight.length ? this.badge_weight :
1249                 (this.weight.length ? this.weight : 'secondary');
1250             bw = bw == 'default' ? 'secondary' : bw;
1251             
1252             cfg.cn = [
1253                 value,
1254                 {
1255                     tag: 'span',
1256                     cls: 'badge badge-' + bw,
1257                     html: this.badge
1258                 }
1259             ];
1260             
1261             cfg.html='';
1262         }
1263         
1264         if (this.menu) {
1265             cfg.cls += ' dropdown';
1266             cfg.html = typeof(cfg.html) != 'undefined' ?
1267                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1268         }
1269         
1270         if (cfg.tag !== 'a' && this.href !== '') {
1271             throw "Tag must be a to set href.";
1272         } else if (this.href.length > 0) {
1273             cfg.href = this.href;
1274         }
1275         
1276         if(this.removeClass){
1277             cfg.cls = '';
1278         }
1279         
1280         if(this.target){
1281             cfg.target = this.target;
1282         }
1283         
1284         return cfg;
1285     },
1286     initEvents: function() {
1287        // Roo.log('init events?');
1288 //        Roo.log(this.el.dom);
1289         // add the menu...
1290         
1291         if (typeof (this.menu) != 'undefined') {
1292             this.menu.parentType = this.xtype;
1293             this.menu.triggerEl = this.el;
1294             this.addxtype(Roo.apply({}, this.menu));
1295         }
1296
1297
1298         if (this.el.hasClass('roo-button')) {
1299              this.el.on('click', this.onClick, this);
1300              this.el.on('dblclick', this.onDblClick, this);
1301         } else {
1302              this.el.select('.roo-button').on('click', this.onClick, this);
1303              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1304              
1305         }
1306         // why?
1307         if(this.removeClass){
1308             this.el.on('click', this.onClick, this);
1309         }
1310         
1311         if (this.group === true) {
1312              if (this.pressed === false || this.pressed === true) {
1313                 // nothing
1314             } else {
1315                 this.pressed = false;
1316                 this.setActive(this.pressed);
1317             }
1318             
1319         }
1320         
1321         this.el.enableDisplayMode();
1322         
1323     },
1324     onClick : function(e)
1325     {
1326         if (this.disabled) {
1327             return;
1328         }
1329         
1330         Roo.log('button on click ');
1331         if(this.preventDefault){
1332             e.preventDefault();
1333         }
1334         
1335         if (this.group) {
1336             if (this.pressed) {
1337                 // do nothing -
1338                 return;
1339             }
1340             this.setActive(true);
1341             var pi = this.parent().items;
1342             for (var i = 0;i < pi.length;i++) {
1343                 if (this == pi[i]) {
1344                     continue;
1345                 }
1346                 if (pi[i].el.hasClass('roo-button')) {
1347                     pi[i].setActive(false);
1348                 }
1349             }
1350             this.fireEvent('click', this, e);            
1351             return;
1352         }
1353         
1354         if (this.pressed === true || this.pressed === false) {
1355             this.toggleActive(e);
1356         }
1357         
1358         
1359         this.fireEvent('click', this, e);
1360     },
1361     onDblClick: function(e)
1362     {
1363         if (this.disabled) {
1364             return;
1365         }
1366         if(this.preventDefault){
1367             e.preventDefault();
1368         }
1369         this.fireEvent('dblclick', this, e);
1370     },
1371     /**
1372      * Enables this button
1373      */
1374     enable : function()
1375     {
1376         this.disabled = false;
1377         this.el.removeClass('disabled');
1378         this.el.dom.removeAttribute("disabled");
1379     },
1380     
1381     /**
1382      * Disable this button
1383      */
1384     disable : function()
1385     {
1386         this.disabled = true;
1387         this.el.addClass('disabled');
1388         this.el.attr("disabled", "disabled")
1389     },
1390      /**
1391      * sets the active state on/off, 
1392      * @param {Boolean} state (optional) Force a particular state
1393      */
1394     setActive : function(v) {
1395         
1396         this.el[v ? 'addClass' : 'removeClass']('active');
1397         this.pressed = v;
1398     },
1399      /**
1400      * toggles the current active state 
1401      */
1402     toggleActive : function(e)
1403     {
1404         this.setActive(!this.pressed); // this modifies pressed...
1405         this.fireEvent('toggle', this, e, this.pressed);
1406     },
1407      /**
1408      * get the current active state
1409      * @return {boolean} true if it's active
1410      */
1411     isActive : function()
1412     {
1413         return this.el.hasClass('active');
1414     },
1415     /**
1416      * set the text of the first selected button
1417      */
1418     setText : function(str)
1419     {
1420         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1421     },
1422     /**
1423      * get the text of the first selected button
1424      */
1425     getText : function()
1426     {
1427         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1428     },
1429     
1430     setWeight : function(str)
1431     {
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1433         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434         this.weight = str;
1435         var outline = this.outline ? 'outline-' : '';
1436         if (str == 'default') {
1437             this.el.addClass('btn-default btn-outline-secondary');        
1438             return;
1439         }
1440         this.el.addClass('btn-' + outline + str);        
1441     }
1442     
1443     
1444 });
1445 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446
1447 Roo.bootstrap.Button.weights = [
1448     'default',
1449     'secondary' ,
1450     'primary',
1451     'success',
1452     'info',
1453     'warning',
1454     'danger',
1455     'link',
1456     'light',
1457     'dark'              
1458    
1459 ];/*
1460  * - LGPL
1461  *
1462  * column
1463  * 
1464  */
1465
1466 /**
1467  * @class Roo.bootstrap.Column
1468  * @extends Roo.bootstrap.Component
1469  * Bootstrap Column class
1470  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1471  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1472  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1473  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1474  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1475  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1476  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1477  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1478  *
1479  * 
1480  * @cfg {Boolean} hidden (true|false) hide the element
1481  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1482  * @cfg {String} fa (ban|check|...) font awesome icon
1483  * @cfg {Number} fasize (1|2|....) font awsome size
1484
1485  * @cfg {String} icon (info-sign|check|...) glyphicon name
1486
1487  * @cfg {String} html content of column.
1488  * 
1489  * @constructor
1490  * Create a new Column
1491  * @param {Object} config The config object
1492  */
1493
1494 Roo.bootstrap.Column = function(config){
1495     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1496 };
1497
1498 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1499     
1500     xs: false,
1501     sm: false,
1502     md: false,
1503     lg: false,
1504     xsoff: false,
1505     smoff: false,
1506     mdoff: false,
1507     lgoff: false,
1508     html: '',
1509     offset: 0,
1510     alert: false,
1511     fa: false,
1512     icon : false,
1513     hidden : false,
1514     fasize : 1,
1515     
1516     getAutoCreate : function(){
1517         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1518         
1519         cfg = {
1520             tag: 'div',
1521             cls: 'column'
1522         };
1523         
1524         var settings=this;
1525         var sizes =   ['xs','sm','md','lg'];
1526         sizes.map(function(size ,ix){
1527             //Roo.log( size + ':' + settings[size]);
1528             
1529             if (settings[size+'off'] !== false) {
1530                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1531             }
1532             
1533             if (settings[size] === false) {
1534                 return;
1535             }
1536             
1537             if (!settings[size]) { // 0 = hidden
1538                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539                 // bootsrap4
1540                 for (var i = ix; i > -1; i--) {
1541                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1542                 }
1543                 
1544                 
1545                 return;
1546             }
1547             cfg.cls += ' col-' + size + '-' + settings[size] + (
1548                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1549             );
1550             
1551         });
1552         
1553         if (this.hidden) {
1554             cfg.cls += ' hidden';
1555         }
1556         
1557         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1558             cfg.cls +=' alert alert-' + this.alert;
1559         }
1560         
1561         
1562         if (this.html.length) {
1563             cfg.html = this.html;
1564         }
1565         if (this.fa) {
1566             var fasize = '';
1567             if (this.fasize > 1) {
1568                 fasize = ' fa-' + this.fasize + 'x';
1569             }
1570             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1571             
1572             
1573         }
1574         if (this.icon) {
1575             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1576         }
1577         
1578         return cfg;
1579     }
1580    
1581 });
1582
1583  
1584
1585  /*
1586  * - LGPL
1587  *
1588  * page container.
1589  * 
1590  */
1591
1592
1593 /**
1594  * @class Roo.bootstrap.Container
1595  * @extends Roo.bootstrap.Component
1596  * Bootstrap Container class
1597  * @cfg {Boolean} jumbotron is it a jumbotron element
1598  * @cfg {String} html content of element
1599  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1600  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1601  * @cfg {String} header content of header (for panel)
1602  * @cfg {String} footer content of footer (for panel)
1603  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1604  * @cfg {String} tag (header|aside|section) type of HTML tag.
1605  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1606  * @cfg {String} fa font awesome icon
1607  * @cfg {String} icon (info-sign|check|...) glyphicon name
1608  * @cfg {Boolean} hidden (true|false) hide the element
1609  * @cfg {Boolean} expandable (true|false) default false
1610  * @cfg {Boolean} expanded (true|false) default true
1611  * @cfg {String} rheader contet on the right of header
1612  * @cfg {Boolean} clickable (true|false) default false
1613
1614  *     
1615  * @constructor
1616  * Create a new Container
1617  * @param {Object} config The config object
1618  */
1619
1620 Roo.bootstrap.Container = function(config){
1621     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1622     
1623     this.addEvents({
1624         // raw events
1625          /**
1626          * @event expand
1627          * After the panel has been expand
1628          * 
1629          * @param {Roo.bootstrap.Container} this
1630          */
1631         "expand" : true,
1632         /**
1633          * @event collapse
1634          * After the panel has been collapsed
1635          * 
1636          * @param {Roo.bootstrap.Container} this
1637          */
1638         "collapse" : true,
1639         /**
1640          * @event click
1641          * When a element is chick
1642          * @param {Roo.bootstrap.Container} this
1643          * @param {Roo.EventObject} e
1644          */
1645         "click" : true
1646     });
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1650     
1651     jumbotron : false,
1652     well: '',
1653     panel : '',
1654     header: '',
1655     footer : '',
1656     sticky: '',
1657     tag : false,
1658     alert : false,
1659     fa: false,
1660     icon : false,
1661     expandable : false,
1662     rheader : '',
1663     expanded : true,
1664     clickable: false,
1665   
1666      
1667     getChildContainer : function() {
1668         
1669         if(!this.el){
1670             return false;
1671         }
1672         
1673         if (this.panel.length) {
1674             return this.el.select('.panel-body',true).first();
1675         }
1676         
1677         return this.el;
1678     },
1679     
1680     
1681     getAutoCreate : function(){
1682         
1683         var cfg = {
1684             tag : this.tag || 'div',
1685             html : '',
1686             cls : ''
1687         };
1688         if (this.jumbotron) {
1689             cfg.cls = 'jumbotron';
1690         }
1691         
1692         
1693         
1694         // - this is applied by the parent..
1695         //if (this.cls) {
1696         //    cfg.cls = this.cls + '';
1697         //}
1698         
1699         if (this.sticky.length) {
1700             
1701             var bd = Roo.get(document.body);
1702             if (!bd.hasClass('bootstrap-sticky')) {
1703                 bd.addClass('bootstrap-sticky');
1704                 Roo.select('html',true).setStyle('height', '100%');
1705             }
1706              
1707             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1708         }
1709         
1710         
1711         if (this.well.length) {
1712             switch (this.well) {
1713                 case 'lg':
1714                 case 'sm':
1715                     cfg.cls +=' well well-' +this.well;
1716                     break;
1717                 default:
1718                     cfg.cls +=' well';
1719                     break;
1720             }
1721         }
1722         
1723         if (this.hidden) {
1724             cfg.cls += ' hidden';
1725         }
1726         
1727         
1728         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1729             cfg.cls +=' alert alert-' + this.alert;
1730         }
1731         
1732         var body = cfg;
1733         
1734         if (this.panel.length) {
1735             cfg.cls += ' panel panel-' + this.panel;
1736             cfg.cn = [];
1737             if (this.header.length) {
1738                 
1739                 var h = [];
1740                 
1741                 if(this.expandable){
1742                     
1743                     cfg.cls = cfg.cls + ' expandable';
1744                     
1745                     h.push({
1746                         tag: 'i',
1747                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1748                     });
1749                     
1750                 }
1751                 
1752                 h.push(
1753                     {
1754                         tag: 'span',
1755                         cls : 'panel-title',
1756                         html : (this.expandable ? '&nbsp;' : '') + this.header
1757                     },
1758                     {
1759                         tag: 'span',
1760                         cls: 'panel-header-right',
1761                         html: this.rheader
1762                     }
1763                 );
1764                 
1765                 cfg.cn.push({
1766                     cls : 'panel-heading',
1767                     style : this.expandable ? 'cursor: pointer' : '',
1768                     cn : h
1769                 });
1770                 
1771             }
1772             
1773             body = false;
1774             cfg.cn.push({
1775                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1776                 html : this.html
1777             });
1778             
1779             
1780             if (this.footer.length) {
1781                 cfg.cn.push({
1782                     cls : 'panel-footer',
1783                     html : this.footer
1784                     
1785                 });
1786             }
1787             
1788         }
1789         
1790         if (body) {
1791             body.html = this.html || cfg.html;
1792             // prefix with the icons..
1793             if (this.fa) {
1794                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1795             }
1796             if (this.icon) {
1797                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1798             }
1799             
1800             
1801         }
1802         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1803             cfg.cls =  'container';
1804         }
1805         
1806         return cfg;
1807     },
1808     
1809     initEvents: function() 
1810     {
1811         if(this.expandable){
1812             var headerEl = this.headerEl();
1813         
1814             if(headerEl){
1815                 headerEl.on('click', this.onToggleClick, this);
1816             }
1817         }
1818         
1819         if(this.clickable){
1820             this.el.on('click', this.onClick, this);
1821         }
1822         
1823     },
1824     
1825     onToggleClick : function()
1826     {
1827         var headerEl = this.headerEl();
1828         
1829         if(!headerEl){
1830             return;
1831         }
1832         
1833         if(this.expanded){
1834             this.collapse();
1835             return;
1836         }
1837         
1838         this.expand();
1839     },
1840     
1841     expand : function()
1842     {
1843         if(this.fireEvent('expand', this)) {
1844             
1845             this.expanded = true;
1846             
1847             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848             
1849             this.el.select('.panel-body',true).first().removeClass('hide');
1850             
1851             var toggleEl = this.toggleEl();
1852
1853             if(!toggleEl){
1854                 return;
1855             }
1856
1857             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1858         }
1859         
1860     },
1861     
1862     collapse : function()
1863     {
1864         if(this.fireEvent('collapse', this)) {
1865             
1866             this.expanded = false;
1867             
1868             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1869             this.el.select('.panel-body',true).first().addClass('hide');
1870         
1871             var toggleEl = this.toggleEl();
1872
1873             if(!toggleEl){
1874                 return;
1875             }
1876
1877             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1878         }
1879     },
1880     
1881     toggleEl : function()
1882     {
1883         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1884             return;
1885         }
1886         
1887         return this.el.select('.panel-heading .fa',true).first();
1888     },
1889     
1890     headerEl : function()
1891     {
1892         if(!this.el || !this.panel.length || !this.header.length){
1893             return;
1894         }
1895         
1896         return this.el.select('.panel-heading',true).first()
1897     },
1898     
1899     bodyEl : function()
1900     {
1901         if(!this.el || !this.panel.length){
1902             return;
1903         }
1904         
1905         return this.el.select('.panel-body',true).first()
1906     },
1907     
1908     titleEl : function()
1909     {
1910         if(!this.el || !this.panel.length || !this.header.length){
1911             return;
1912         }
1913         
1914         return this.el.select('.panel-title',true).first();
1915     },
1916     
1917     setTitle : function(v)
1918     {
1919         var titleEl = this.titleEl();
1920         
1921         if(!titleEl){
1922             return;
1923         }
1924         
1925         titleEl.dom.innerHTML = v;
1926     },
1927     
1928     getTitle : function()
1929     {
1930         
1931         var titleEl = this.titleEl();
1932         
1933         if(!titleEl){
1934             return '';
1935         }
1936         
1937         return titleEl.dom.innerHTML;
1938     },
1939     
1940     setRightTitle : function(v)
1941     {
1942         var t = this.el.select('.panel-header-right',true).first();
1943         
1944         if(!t){
1945             return;
1946         }
1947         
1948         t.dom.innerHTML = v;
1949     },
1950     
1951     onClick : function(e)
1952     {
1953         e.preventDefault();
1954         
1955         this.fireEvent('click', this, e);
1956     }
1957 });
1958
1959  /*
1960  *  - LGPL
1961  *
1962  *  This is BS4's Card element.. - similar to our containers probably..
1963  * 
1964  */
1965 /**
1966  * @class Roo.bootstrap.Card
1967  * @extends Roo.bootstrap.Component
1968  * Bootstrap Card class
1969  *
1970  *
1971  * possible... may not be implemented..
1972  * @cfg {String} header_image  src url of image.
1973  * @cfg {String|Object} header
1974  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1975  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1976  * 
1977  * @cfg {String} title
1978  * @cfg {String} subtitle
1979  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1980  * @cfg {String} footer
1981  
1982  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983  * 
1984  * @cfg {String} margin (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1990  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991  *
1992  * @cfg {String} padding (0|1|2|3|4|5)
1993  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1994  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1995  * @cfg {String} padding_left (0|1|2|3|4|5)
1996  * @cfg {String} padding_right (0|1|2|3|4|5)
1997  * @cfg {String} padding_x (0|1|2|3|4|5)
1998  * @cfg {String} padding_y (0|1|2|3|4|5)
1999  *
2000  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005  
2006  * @config {Boolean} dragable  if this card can be dragged.
2007  * @config {String} drag_group  group for drag
2008  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2009  * @config {String} drop_group  group for drag
2010  * 
2011  * @config {Boolean} collapsable can the body be collapsed.
2012  * @config {Boolean} collapsed is the body collapsed when rendered...
2013  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2014  * @config {Boolean} rotated is the body rotated when rendered...
2015  * 
2016  * @constructor
2017  * Create a new Container
2018  * @param {Object} config The config object
2019  */
2020
2021 Roo.bootstrap.Card = function(config){
2022     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2023     
2024     this.addEvents({
2025          // raw events
2026         /**
2027          * @event drop
2028          * When a element a card is dropped
2029          * @param {Roo.bootstrap.Card} this
2030          *
2031          * 
2032          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2033          * @param {String} position 'above' or 'below'
2034          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2035         
2036          */
2037         'drop' : true,
2038          /**
2039          * @event rotate
2040          * When a element a card is rotate
2041          * @param {Roo.bootstrap.Card} this
2042          * @param {Roo.Element} n the node being dropped?
2043          * @param {Boolean} rotate status
2044          */
2045         'rotate' : true,
2046         /**
2047          * @event cardover
2048          * When a card element is dragged over ready to drop (return false to block dropable)
2049          * @param {Roo.bootstrap.Card} this
2050          * @param {Object} data from dragdrop 
2051          */
2052          'cardover' : true
2053          
2054     });
2055 };
2056
2057
2058 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2059     
2060     
2061     weight : '',
2062     
2063     margin: '', /// may be better in component?
2064     margin_top: '', 
2065     margin_bottom: '', 
2066     margin_left: '',
2067     margin_right: '',
2068     margin_x: '',
2069     margin_y: '',
2070     
2071     padding : '',
2072     padding_top: '', 
2073     padding_bottom: '', 
2074     padding_left: '',
2075     padding_right: '',
2076     padding_x: '',
2077     padding_y: '',
2078     
2079     display: '', 
2080     display_xs: '', 
2081     display_sm: '', 
2082     display_lg: '',
2083     display_xl: '',
2084  
2085     header_image  : '',
2086     header : '',
2087     header_size : 0,
2088     title : '',
2089     subtitle : '',
2090     html : '',
2091     footer: '',
2092
2093     collapsable : false,
2094     collapsed : false,
2095     rotateable : false,
2096     rotated : false,
2097     
2098     dragable : false,
2099     drag_group : false,
2100     dropable : false,
2101     drop_group : false,
2102     childContainer : false,
2103     dropEl : false, /// the dom placeholde element that indicates drop location.
2104     containerEl: false, // body container
2105     bodyEl: false, // card-body
2106     headerContainerEl : false, //
2107     headerEl : false,
2108     header_imageEl : false,
2109     
2110     
2111     layoutCls : function()
2112     {
2113         var cls = '';
2114         var t = this;
2115         Roo.log(this.margin_bottom.length);
2116         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2117             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118             
2119             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2120                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2121             }
2122             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2123                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2124             }
2125         });
2126         
2127         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2128             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2130             }
2131         });
2132         
2133         // more generic support?
2134         if (this.hidden) {
2135             cls += ' d-none';
2136         }
2137         
2138         return cls;
2139     },
2140  
2141        // Roo.log("Call onRender: " + this.xtype);
2142         /*  We are looking at something like this.
2143 <div class="card">
2144     <img src="..." class="card-img-top" alt="...">
2145     <div class="card-body">
2146         <h5 class="card-title">Card title</h5>
2147          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148
2149         >> this bit is really the body...
2150         <div> << we will ad dthis in hopefully it will not break shit.
2151         
2152         ** card text does not actually have any styling...
2153         
2154             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2155         
2156         </div> <<
2157           <a href="#" class="card-link">Card link</a>
2158           
2159     </div>
2160     <div class="card-footer">
2161         <small class="text-muted">Last updated 3 mins ago</small>
2162     </div>
2163 </div>
2164          */
2165     getAutoCreate : function(){
2166         
2167         var cfg = {
2168             tag : 'div',
2169             cls : 'card',
2170             cn : [ ]
2171         };
2172         
2173         if (this.weight.length && this.weight != 'light') {
2174             cfg.cls += ' text-white';
2175         } else {
2176             cfg.cls += ' text-dark'; // need as it's nested..
2177         }
2178         if (this.weight.length) {
2179             cfg.cls += ' bg-' + this.weight;
2180         }
2181         
2182         cfg.cls += ' ' + this.layoutCls(); 
2183         
2184         var hdr = false;
2185         var hdr_ctr = false;
2186         if (this.header.length) {
2187             hdr = {
2188                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2189                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2190                 cn : []
2191             };
2192             cfg.cn.push(hdr);
2193             hdr_ctr = hdr;
2194         } else {
2195             hdr = {
2196                 tag : 'div',
2197                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2198                 cn : []
2199             };
2200             cfg.cn.push(hdr);
2201             hdr_ctr = hdr;
2202         }
2203         if (this.collapsable) {
2204             hdr_ctr = {
2205             tag : 'a',
2206             cls : 'd-block user-select-none',
2207             cn: [
2208                     {
2209                         tag: 'i',
2210                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2211                     }
2212                    
2213                 ]
2214             };
2215             hdr.cn.push(hdr_ctr);
2216         }
2217         
2218         hdr_ctr.cn.push(        {
2219             tag: 'span',
2220             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2221             html : this.header
2222         });
2223         
2224         
2225         if (this.header_image.length) {
2226             cfg.cn.push({
2227                 tag : 'img',
2228                 cls : 'card-img-top',
2229                 src: this.header_image // escape?
2230             });
2231         } else {
2232             cfg.cn.push({
2233                     tag : 'div',
2234                     cls : 'card-img-top d-none' 
2235                 });
2236         }
2237             
2238         var body = {
2239             tag : 'div',
2240             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2241             cn : []
2242         };
2243         var obody = body;
2244         if (this.collapsable || this.rotateable) {
2245             obody = {
2246                 tag: 'div',
2247                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2248                 cn : [  body ]
2249             };
2250         }
2251         
2252         cfg.cn.push(obody);
2253         
2254         if (this.title.length) {
2255             body.cn.push({
2256                 tag : 'div',
2257                 cls : 'card-title',
2258                 src: this.title // escape?
2259             });
2260         }  
2261         
2262         if (this.subtitle.length) {
2263             body.cn.push({
2264                 tag : 'div',
2265                 cls : 'card-title',
2266                 src: this.subtitle // escape?
2267             });
2268         }
2269         
2270         body.cn.push({
2271             tag : 'div',
2272             cls : 'roo-card-body-ctr'
2273         });
2274         
2275         if (this.html.length) {
2276             body.cn.push({
2277                 tag: 'div',
2278                 html : this.html
2279             });
2280         }
2281         // fixme ? handle objects?
2282         
2283         if (this.footer.length) {
2284            
2285             cfg.cn.push({
2286                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2287                 html : this.footer
2288             });
2289             
2290         } else {
2291             cfg.cn.push({cls : 'card-footer d-none'});
2292         }
2293         
2294         // footer...
2295         
2296         return cfg;
2297     },
2298     
2299     
2300     getCardHeader : function()
2301     {
2302         var  ret = this.el.select('.card-header',true).first();
2303         if (ret.hasClass('d-none')) {
2304             ret.removeClass('d-none');
2305         }
2306         
2307         return ret;
2308     },
2309     getCardFooter : function()
2310     {
2311         var  ret = this.el.select('.card-footer',true).first();
2312         if (ret.hasClass('d-none')) {
2313             ret.removeClass('d-none');
2314         }
2315         
2316         return ret;
2317     },
2318     getCardImageTop : function()
2319     {
2320         var  ret = this.header_imageEl;
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324             
2325         return ret;
2326     },
2327     
2328     getChildContainer : function()
2329     {
2330         
2331         if(!this.el){
2332             return false;
2333         }
2334         return this.el.select('.roo-card-body-ctr',true).first();    
2335     },
2336     
2337     initEvents: function() 
2338     {
2339         this.bodyEl = this.el.select('.card-body',true).first(); 
2340         this.containerEl = this.getChildContainer();
2341         if(this.dragable){
2342             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2343                     containerScroll: true,
2344                     ddGroup: this.drag_group || 'default_card_drag_group'
2345             });
2346             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347         }
2348         if (this.dropable) {
2349             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2350                 containerScroll: true,
2351                 ddGroup: this.drop_group || 'default_card_drag_group'
2352             });
2353             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2354             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2355             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2356             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2357             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2358         }
2359         
2360         if (this.collapsable) {
2361             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362         }
2363         if (this.rotateable) {
2364             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365         }
2366         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367          
2368         this.footerEl = this.el.select('.card-footer',true).first();
2369         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2370         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2371         this.headerEl = this.el.select('.card-header',true).first();
2372         
2373         if (this.rotated) {
2374             this.el.addClass('roo-card-rotated');
2375             this.fireEvent('rotate', this, true);
2376         }
2377         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2378         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2379         
2380     },
2381     getDragData : function(e)
2382     {
2383         var target = this.getEl();
2384         if (target) {
2385             //this.handleSelection(e);
2386             
2387             var dragData = {
2388                 source: this,
2389                 copy: false,
2390                 nodes: this.getEl(),
2391                 records: []
2392             };
2393             
2394             
2395             dragData.ddel = target.dom ;    // the div element
2396             Roo.log(target.getWidth( ));
2397             dragData.ddel.style.width = target.getWidth() + 'px';
2398             
2399             return dragData;
2400         }
2401         return false;
2402     },
2403     /**
2404     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2405     *    whole Element becomes the target, and this causes the drop gesture to append.
2406     *
2407     *    Returns an object:
2408     *     {
2409            
2410            position : 'below' or 'above'
2411            card  : relateive to card OBJECT (or true for no cards listed)
2412            items_n : relative to nth item in list
2413            card_n : relative to  nth card in list
2414     }
2415     *
2416     *    
2417     */
2418     getTargetFromEvent : function(e, dragged_card_el)
2419     {
2420         var target = e.getTarget();
2421         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2422             target = target.parentNode;
2423         }
2424         
2425         var ret = {
2426             position: '',
2427             cards : [],
2428             card_n : -1,
2429             items_n : -1,
2430             card : false 
2431         };
2432         
2433         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2434         // see if target is one of the 'cards'...
2435         
2436         
2437         //Roo.log(this.items.length);
2438         var pos = false;
2439         
2440         var last_card_n = 0;
2441         var cards_len  = 0;
2442         for (var i = 0;i< this.items.length;i++) {
2443             
2444             if (!this.items[i].el.hasClass('card')) {
2445                  continue;
2446             }
2447             pos = this.getDropPoint(e, this.items[i].el.dom);
2448             
2449             cards_len = ret.cards.length;
2450             //Roo.log(this.items[i].el.dom.id);
2451             ret.cards.push(this.items[i]);
2452             last_card_n  = i;
2453             if (ret.card_n < 0 && pos == 'above') {
2454                 ret.position = cards_len > 0 ? 'below' : pos;
2455                 ret.items_n = i > 0 ? i - 1 : 0;
2456                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2457                 ret.card = ret.cards[ret.card_n];
2458             }
2459         }
2460         if (!ret.cards.length) {
2461             ret.card = true;
2462             ret.position = 'below';
2463             ret.items_n;
2464             return ret;
2465         }
2466         // could not find a card.. stick it at the end..
2467         if (ret.card_n < 0) {
2468             ret.card_n = last_card_n;
2469             ret.card = ret.cards[last_card_n];
2470             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2471             ret.position = 'below';
2472         }
2473         
2474         if (this.items[ret.items_n].el == dragged_card_el) {
2475             return false;
2476         }
2477         
2478         if (ret.position == 'below') {
2479             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480             
2481             if (card_after  && card_after.el == dragged_card_el) {
2482                 return false;
2483             }
2484             return ret;
2485         }
2486         
2487         // its's after ..
2488         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489         
2490         if (card_before  && card_before.el == dragged_card_el) {
2491             return false;
2492         }
2493         
2494         return ret;
2495     },
2496     
2497     onNodeEnter : function(n, dd, e, data){
2498         return false;
2499     },
2500     onNodeOver : function(n, dd, e, data)
2501     {
2502        
2503         var target_info = this.getTargetFromEvent(e,data.source.el);
2504         if (target_info === false) {
2505             this.dropPlaceHolder('hide');
2506             return false;
2507         }
2508         Roo.log(['getTargetFromEvent', target_info ]);
2509         
2510         
2511         if (this.fireEvent('cardover', this, [ data ]) === false) {
2512             return false;
2513         }
2514         
2515         this.dropPlaceHolder('show', target_info,data);
2516         
2517         return false; 
2518     },
2519     onNodeOut : function(n, dd, e, data){
2520         this.dropPlaceHolder('hide');
2521      
2522     },
2523     onNodeDrop : function(n, dd, e, data)
2524     {
2525         
2526         // call drop - return false if
2527         
2528         // this could actually fail - if the Network drops..
2529         // we will ignore this at present..- client should probably reload
2530         // the whole set of cards if stuff like that fails.
2531         
2532         
2533         var info = this.getTargetFromEvent(e,data.source.el);
2534         if (info === false) {
2535             return false;
2536         }
2537         this.dropPlaceHolder('hide');
2538   
2539           
2540     
2541         this.acceptCard(data.source, info.position, info.card, info.items_n);
2542         return true;
2543          
2544     },
2545     firstChildCard : function()
2546     {
2547         for (var i = 0;i< this.items.length;i++) {
2548             
2549             if (!this.items[i].el.hasClass('card')) {
2550                  continue;
2551             }
2552             return this.items[i];
2553         }
2554         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2555     },
2556     /**
2557      * accept card
2558      *
2559      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2560      */
2561     acceptCard : function(move_card,  position, next_to_card )
2562     {
2563         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2564             return false;
2565         }
2566         
2567         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568         
2569         move_card.parent().removeCard(move_card);
2570         
2571         
2572         var dom = move_card.el.dom;
2573         dom.style.width = ''; // clear with - which is set by drag.
2574         
2575         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2576             var cardel = next_to_card.el.dom;
2577             
2578             if (position == 'above' ) {
2579                 cardel.parentNode.insertBefore(dom, cardel);
2580             } else if (cardel.nextSibling) {
2581                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582             } else {
2583                 cardel.parentNode.append(dom);
2584             }
2585         } else {
2586             // card container???
2587             this.containerEl.dom.append(dom);
2588         }
2589         
2590         //FIXME HANDLE card = true 
2591         
2592         // add this to the correct place in items.
2593         
2594         // remove Card from items.
2595         
2596        
2597         if (this.items.length) {
2598             var nitems = [];
2599             //Roo.log([info.items_n, info.position, this.items.length]);
2600             for (var i =0; i < this.items.length; i++) {
2601                 if (i == to_items_n && position == 'above') {
2602                     nitems.push(move_card);
2603                 }
2604                 nitems.push(this.items[i]);
2605                 if (i == to_items_n && position == 'below') {
2606                     nitems.push(move_card);
2607                 }
2608             }
2609             this.items = nitems;
2610             Roo.log(this.items);
2611         } else {
2612             this.items.push(move_card);
2613         }
2614         
2615         move_card.parentId = this.id;
2616         
2617         return true;
2618         
2619         
2620     },
2621     removeCard : function(c)
2622     {
2623         this.items = this.items.filter(function(e) { return e != c });
2624  
2625         var dom = c.el.dom;
2626         dom.parentNode.removeChild(dom);
2627         dom.style.width = ''; // clear with - which is set by drag.
2628         c.parentId = false;
2629         
2630     },
2631     
2632     /**    Decide whether to drop above or below a View node. */
2633     getDropPoint : function(e, n, dd)
2634     {
2635         if (dd) {
2636              return false;
2637         }
2638         if (n == this.containerEl.dom) {
2639             return "above";
2640         }
2641         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2642         var c = t + (b - t) / 2;
2643         var y = Roo.lib.Event.getPageY(e);
2644         if(y <= c) {
2645             return "above";
2646         }else{
2647             return "below";
2648         }
2649     },
2650     onToggleCollapse : function(e)
2651         {
2652         if (this.collapsed) {
2653             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2654             this.collapsableEl.addClass('show');
2655             this.collapsed = false;
2656             return;
2657         }
2658         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2659         this.collapsableEl.removeClass('show');
2660         this.collapsed = true;
2661         
2662     
2663     },
2664     
2665     onToggleRotate : function(e)
2666     {
2667         this.collapsableEl.removeClass('show');
2668         this.footerEl.removeClass('d-none');
2669         this.el.removeClass('roo-card-rotated');
2670         this.el.removeClass('d-none');
2671         if (this.rotated) {
2672             
2673             this.collapsableEl.addClass('show');
2674             this.rotated = false;
2675             this.fireEvent('rotate', this, this.rotated);
2676             return;
2677         }
2678         this.el.addClass('roo-card-rotated');
2679         this.footerEl.addClass('d-none');
2680         this.el.select('.roo-collapsable').removeClass('show');
2681         
2682         this.rotated = true;
2683         this.fireEvent('rotate', this, this.rotated);
2684     
2685     },
2686     
2687     dropPlaceHolder: function (action, info, data)
2688     {
2689         if (this.dropEl === false) {
2690             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2691             cls : 'd-none'
2692             },true);
2693         }
2694         this.dropEl.removeClass(['d-none', 'd-block']);        
2695         if (action == 'hide') {
2696             
2697             this.dropEl.addClass('d-none');
2698             return;
2699         }
2700         // FIXME - info.card == true!!!
2701         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702         
2703         if (info.card !== true) {
2704             var cardel = info.card.el.dom;
2705             
2706             if (info.position == 'above') {
2707                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2708             } else if (cardel.nextSibling) {
2709                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710             } else {
2711                 cardel.parentNode.append(this.dropEl.dom);
2712             }
2713         } else {
2714             // card container???
2715             this.containerEl.dom.append(this.dropEl.dom);
2716         }
2717         
2718         this.dropEl.addClass('d-block roo-card-dropzone');
2719         
2720         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2721         
2722         
2723     
2724     
2725     
2726     },
2727     setHeaderText: function(html)
2728     {
2729         this.header = html;
2730         if (this.headerContainerEl) {
2731             this.headerContainerEl.dom.innerHTML = html;
2732         }
2733     },
2734     onHeaderImageLoad : function(ev, he)
2735     {
2736         if (!this.header_image_fit_square) {
2737             return;
2738         }
2739         
2740         var hw = he.naturalHeight / he.naturalWidth;
2741         // wide image = < 0
2742         // tall image = > 1
2743         //var w = he.dom.naturalWidth;
2744         var ww = he.width;
2745         he.style.left =  0;
2746         he.style.position =  'relative';
2747         if (hw > 1) {
2748             var nw = (ww * (1/hw));
2749             Roo.get(he).setSize( ww * (1/hw),  ww);
2750             he.style.left =  ((ww - nw)/ 2) + 'px';
2751             he.style.position =  'relative';
2752         }
2753
2754     }
2755
2756     
2757 });
2758
2759 /*
2760  * - LGPL
2761  *
2762  * Card header - holder for the card header elements.
2763  * 
2764  */
2765
2766 /**
2767  * @class Roo.bootstrap.CardHeader
2768  * @extends Roo.bootstrap.Element
2769  * Bootstrap CardHeader class
2770  * @constructor
2771  * Create a new Card Header - that you can embed children into
2772  * @param {Object} config The config object
2773  */
2774
2775 Roo.bootstrap.CardHeader = function(config){
2776     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2777 };
2778
2779 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2780     
2781     
2782     container_method : 'getCardHeader' 
2783     
2784      
2785     
2786     
2787    
2788 });
2789
2790  
2791
2792  /*
2793  * - LGPL
2794  *
2795  * Card footer - holder for the card footer elements.
2796  * 
2797  */
2798
2799 /**
2800  * @class Roo.bootstrap.CardFooter
2801  * @extends Roo.bootstrap.Element
2802  * Bootstrap CardFooter class
2803  * @constructor
2804  * Create a new Card Footer - that you can embed children into
2805  * @param {Object} config The config object
2806  */
2807
2808 Roo.bootstrap.CardFooter = function(config){
2809     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2810 };
2811
2812 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2813     
2814     
2815     container_method : 'getCardFooter' 
2816     
2817      
2818     
2819     
2820    
2821 });
2822
2823  
2824
2825  /*
2826  * - LGPL
2827  *
2828  * Card header - holder for the card header elements.
2829  * 
2830  */
2831
2832 /**
2833  * @class Roo.bootstrap.CardImageTop
2834  * @extends Roo.bootstrap.Element
2835  * Bootstrap CardImageTop class
2836  * @constructor
2837  * Create a new Card Image Top container
2838  * @param {Object} config The config object
2839  */
2840
2841 Roo.bootstrap.CardImageTop = function(config){
2842     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2843 };
2844
2845 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2846     
2847    
2848     container_method : 'getCardImageTop' 
2849     
2850      
2851     
2852    
2853 });
2854
2855  
2856
2857  
2858 /*
2859 * Licence: LGPL
2860 */
2861
2862 /**
2863  * @class Roo.bootstrap.ButtonUploader
2864  * @extends Roo.bootstrap.Button
2865  * Bootstrap Button Uploader class - it's a button which when you add files to it
2866  *
2867  * 
2868  * @cfg {Number} errorTimeout default 3000
2869  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2870  * @cfg {Array}  html The button text.
2871  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2872  *
2873  * @constructor
2874  * Create a new CardUploader
2875  * @param {Object} config The config object
2876  */
2877
2878 Roo.bootstrap.ButtonUploader = function(config){
2879     
2880  
2881     
2882     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2883     
2884      
2885      this.addEvents({
2886          // raw events
2887         /**
2888          * @event beforeselect
2889          * When button is pressed, before show upload files dialog is shown
2890          * @param {Roo.bootstrap.UploaderButton} this
2891          *
2892          */
2893         'beforeselect' : true,
2894          /**
2895          * @event fired when files have been selected, 
2896          * When a the download link is clicked
2897          * @param {Roo.bootstrap.UploaderButton} this
2898          * @param {Array} Array of files that have been uploaded
2899          */
2900         'uploaded' : true
2901         
2902     });
2903 };
2904  
2905 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2906     
2907      
2908     errorTimeout : 3000,
2909      
2910     images : false,
2911    
2912     fileCollection : false,
2913     allowBlank : true,
2914     
2915     multiple : true,
2916     
2917     getAutoCreate : function()
2918     {
2919         var im = {
2920             tag: 'input',
2921             type : 'file',
2922             cls : 'd-none  roo-card-upload-selector' 
2923           
2924         };
2925         if (this.multiple) {
2926             im.multiple = 'multiple';
2927         }
2928         
2929         return  {
2930             cls :'div' ,
2931             cn : [
2932                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2933                 im
2934
2935             ]
2936         };
2937            
2938          
2939     },
2940      
2941    
2942     initEvents : function()
2943     {
2944         
2945         Roo.bootstrap.Button.prototype.initEvents.call(this);
2946         
2947         
2948         
2949         
2950         
2951         this.urlAPI = (window.createObjectURL && window) || 
2952                                 (window.URL && URL.revokeObjectURL && URL) || 
2953                                 (window.webkitURL && webkitURL);
2954                         
2955          
2956          
2957          
2958         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959         
2960         this.selectorEl.on('change', this.onFileSelected, this);
2961          
2962          
2963        
2964     },
2965     
2966    
2967     onClick : function(e)
2968     {
2969         e.preventDefault();
2970         
2971         if ( this.fireEvent('beforeselect', this) === false) {
2972             return;
2973         }
2974          
2975         this.selectorEl.dom.click();
2976          
2977     },
2978     
2979     onFileSelected : function(e)
2980     {
2981         e.preventDefault();
2982         
2983         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2984             return;
2985         }
2986         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2987         this.selectorEl.dom.value  = '';// hopefully reset..
2988         
2989         this.fireEvent('uploaded', this,  files );
2990         
2991     },
2992     
2993        
2994    
2995     
2996     /**
2997      * addCard - add an Attachment to the uploader
2998      * @param data - the data about the image to upload
2999      *
3000      * {
3001           id : 123
3002           title : "Title of file",
3003           is_uploaded : false,
3004           src : "http://.....",
3005           srcfile : { the File upload object },
3006           mimetype : file.type,
3007           preview : false,
3008           is_deleted : 0
3009           .. any other data...
3010         }
3011      *
3012      * 
3013     */
3014      
3015     reset: function()
3016     {
3017          
3018          this.selectorEl
3019     } 
3020     
3021     
3022     
3023     
3024 });
3025  /*
3026  * - LGPL
3027  *
3028  * image
3029  * 
3030  */
3031
3032
3033 /**
3034  * @class Roo.bootstrap.Img
3035  * @extends Roo.bootstrap.Component
3036  * Bootstrap Img class
3037  * @cfg {Boolean} imgResponsive false | true
3038  * @cfg {String} border rounded | circle | thumbnail
3039  * @cfg {String} src image source
3040  * @cfg {String} alt image alternative text
3041  * @cfg {String} href a tag href
3042  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3043  * @cfg {String} xsUrl xs image source
3044  * @cfg {String} smUrl sm image source
3045  * @cfg {String} mdUrl md image source
3046  * @cfg {String} lgUrl lg image source
3047  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3048  * 
3049  * @constructor
3050  * Create a new Input
3051  * @param {Object} config The config object
3052  */
3053
3054 Roo.bootstrap.Img = function(config){
3055     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3056     
3057     this.addEvents({
3058         // img events
3059         /**
3060          * @event click
3061          * The img click event for the img.
3062          * @param {Roo.EventObject} e
3063          */
3064         "click" : true,
3065         /**
3066          * @event load
3067          * The when any image loads
3068          * @param {Roo.EventObject} e
3069          */
3070         "load" : true
3071     });
3072 };
3073
3074 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3075     
3076     imgResponsive: true,
3077     border: '',
3078     src: 'about:blank',
3079     href: false,
3080     target: false,
3081     xsUrl: '',
3082     smUrl: '',
3083     mdUrl: '',
3084     lgUrl: '',
3085     backgroundContain : false,
3086
3087     getAutoCreate : function()
3088     {   
3089         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3090             return this.createSingleImg();
3091         }
3092         
3093         var cfg = {
3094             tag: 'div',
3095             cls: 'roo-image-responsive-group',
3096             cn: []
3097         };
3098         var _this = this;
3099         
3100         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101             
3102             if(!_this[size + 'Url']){
3103                 return;
3104             }
3105             
3106             var img = {
3107                 tag: 'img',
3108                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3109                 html: _this.html || cfg.html,
3110                 src: _this[size + 'Url']
3111             };
3112             
3113             img.cls += ' roo-image-responsive-' + size;
3114             
3115             var s = ['xs', 'sm', 'md', 'lg'];
3116             
3117             s.splice(s.indexOf(size), 1);
3118             
3119             Roo.each(s, function(ss){
3120                 img.cls += ' hidden-' + ss;
3121             });
3122             
3123             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3124                 cfg.cls += ' img-' + _this.border;
3125             }
3126             
3127             if(_this.alt){
3128                 cfg.alt = _this.alt;
3129             }
3130             
3131             if(_this.href){
3132                 var a = {
3133                     tag: 'a',
3134                     href: _this.href,
3135                     cn: [
3136                         img
3137                     ]
3138                 };
3139
3140                 if(this.target){
3141                     a.target = _this.target;
3142                 }
3143             }
3144             
3145             cfg.cn.push((_this.href) ? a : img);
3146             
3147         });
3148         
3149         return cfg;
3150     },
3151     
3152     createSingleImg : function()
3153     {
3154         var cfg = {
3155             tag: 'img',
3156             cls: (this.imgResponsive) ? 'img-responsive' : '',
3157             html : null,
3158             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3159         };
3160         
3161         if (this.backgroundContain) {
3162             cfg.cls += ' background-contain';
3163         }
3164         
3165         cfg.html = this.html || cfg.html;
3166         
3167         if (this.backgroundContain) {
3168             cfg.style="background-image: url(" + this.src + ')';
3169         } else {
3170             cfg.src = this.src || cfg.src;
3171         }
3172         
3173         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3174             cfg.cls += ' img-' + this.border;
3175         }
3176         
3177         if(this.alt){
3178             cfg.alt = this.alt;
3179         }
3180         
3181         if(this.href){
3182             var a = {
3183                 tag: 'a',
3184                 href: this.href,
3185                 cn: [
3186                     cfg
3187                 ]
3188             };
3189             
3190             if(this.target){
3191                 a.target = this.target;
3192             }
3193             
3194         }
3195         
3196         return (this.href) ? a : cfg;
3197     },
3198     
3199     initEvents: function() 
3200     {
3201         if(!this.href){
3202             this.el.on('click', this.onClick, this);
3203         }
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.on('load', this.onImageLoad, this);
3206         } else {
3207             // not sure if this works.. not tested
3208             this.el.select('img', true).on('load', this.onImageLoad, this);
3209         }
3210         
3211     },
3212     
3213     onClick : function(e)
3214     {
3215         Roo.log('img onclick');
3216         this.fireEvent('click', this, e);
3217     },
3218     onImageLoad: function(e)
3219     {
3220         Roo.log('img load');
3221         this.fireEvent('load', this, e);
3222     },
3223     
3224     /**
3225      * Sets the url of the image - used to update it
3226      * @param {String} url the url of the image
3227      */
3228     
3229     setSrc : function(url)
3230     {
3231         this.src =  url;
3232         
3233         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3234             if (this.backgroundContain) {
3235                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3236             } else {
3237                 this.el.dom.src =  url;
3238             }
3239             return;
3240         }
3241         
3242         this.el.select('img', true).first().dom.src =  url;
3243     }
3244     
3245     
3246    
3247 });
3248
3249  /*
3250  * - LGPL
3251  *
3252  * image
3253  * 
3254  */
3255
3256
3257 /**
3258  * @class Roo.bootstrap.Link
3259  * @extends Roo.bootstrap.Component
3260  * Bootstrap Link Class
3261  * @cfg {String} alt image alternative text
3262  * @cfg {String} href a tag href
3263  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3264  * @cfg {String} html the content of the link.
3265  * @cfg {String} anchor name for the anchor link
3266  * @cfg {String} fa - favicon
3267
3268  * @cfg {Boolean} preventDefault (true | false) default false
3269
3270  * 
3271  * @constructor
3272  * Create a new Input
3273  * @param {Object} config The config object
3274  */
3275
3276 Roo.bootstrap.Link = function(config){
3277     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3278     
3279     this.addEvents({
3280         // img events
3281         /**
3282          * @event click
3283          * The img click event for the img.
3284          * @param {Roo.EventObject} e
3285          */
3286         "click" : true
3287     });
3288 };
3289
3290 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3291     
3292     href: false,
3293     target: false,
3294     preventDefault: false,
3295     anchor : false,
3296     alt : false,
3297     fa: false,
3298
3299
3300     getAutoCreate : function()
3301     {
3302         var html = this.html || '';
3303         
3304         if (this.fa !== false) {
3305             html = '<i class="fa fa-' + this.fa + '"></i>';
3306         }
3307         var cfg = {
3308             tag: 'a'
3309         };
3310         // anchor's do not require html/href...
3311         if (this.anchor === false) {
3312             cfg.html = html;
3313             cfg.href = this.href || '#';
3314         } else {
3315             cfg.name = this.anchor;
3316             if (this.html !== false || this.fa !== false) {
3317                 cfg.html = html;
3318             }
3319             if (this.href !== false) {
3320                 cfg.href = this.href;
3321             }
3322         }
3323         
3324         if(this.alt !== false){
3325             cfg.alt = this.alt;
3326         }
3327         
3328         
3329         if(this.target !== false) {
3330             cfg.target = this.target;
3331         }
3332         
3333         return cfg;
3334     },
3335     
3336     initEvents: function() {
3337         
3338         if(!this.href || this.preventDefault){
3339             this.el.on('click', this.onClick, this);
3340         }
3341     },
3342     
3343     onClick : function(e)
3344     {
3345         if(this.preventDefault){
3346             e.preventDefault();
3347         }
3348         //Roo.log('img onclick');
3349         this.fireEvent('click', this, e);
3350     }
3351    
3352 });
3353
3354  /*
3355  * - LGPL
3356  *
3357  * header
3358  * 
3359  */
3360
3361 /**
3362  * @class Roo.bootstrap.Header
3363  * @extends Roo.bootstrap.Component
3364  * Bootstrap Header class
3365  * @cfg {String} html content of header
3366  * @cfg {Number} level (1|2|3|4|5|6) default 1
3367  * 
3368  * @constructor
3369  * Create a new Header
3370  * @param {Object} config The config object
3371  */
3372
3373
3374 Roo.bootstrap.Header  = function(config){
3375     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3376 };
3377
3378 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3379     
3380     //href : false,
3381     html : false,
3382     level : 1,
3383     
3384     
3385     
3386     getAutoCreate : function(){
3387         
3388         
3389         
3390         var cfg = {
3391             tag: 'h' + (1 *this.level),
3392             html: this.html || ''
3393         } ;
3394         
3395         return cfg;
3396     }
3397    
3398 });
3399
3400  
3401
3402  /*
3403  * Based on:
3404  * Ext JS Library 1.1.1
3405  * Copyright(c) 2006-2007, Ext JS, LLC.
3406  *
3407  * Originally Released Under LGPL - original licence link has changed is not relivant.
3408  *
3409  * Fork - LGPL
3410  * <script type="text/javascript">
3411  */
3412  
3413 /**
3414  * @class Roo.bootstrap.MenuMgr
3415  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3416  * @singleton
3417  */
3418 Roo.bootstrap.MenuMgr = function(){
3419    var menus, active, groups = {}, attached = false, lastShow = new Date();
3420
3421    // private - called when first menu is created
3422    function init(){
3423        menus = {};
3424        active = new Roo.util.MixedCollection();
3425        Roo.get(document).addKeyListener(27, function(){
3426            if(active.length > 0){
3427                hideAll();
3428            }
3429        });
3430    }
3431
3432    // private
3433    function hideAll(){
3434        if(active && active.length > 0){
3435            var c = active.clone();
3436            c.each(function(m){
3437                m.hide();
3438            });
3439        }
3440    }
3441
3442    // private
3443    function onHide(m){
3444        active.remove(m);
3445        if(active.length < 1){
3446            Roo.get(document).un("mouseup", onMouseDown);
3447             
3448            attached = false;
3449        }
3450    }
3451
3452    // private
3453    function onShow(m){
3454        var last = active.last();
3455        lastShow = new Date();
3456        active.add(m);
3457        if(!attached){
3458           Roo.get(document).on("mouseup", onMouseDown);
3459            
3460            attached = true;
3461        }
3462        if(m.parentMenu){
3463           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3464           m.parentMenu.activeChild = m;
3465        }else if(last && last.isVisible()){
3466           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3467        }
3468    }
3469
3470    // private
3471    function onBeforeHide(m){
3472        if(m.activeChild){
3473            m.activeChild.hide();
3474        }
3475        if(m.autoHideTimer){
3476            clearTimeout(m.autoHideTimer);
3477            delete m.autoHideTimer;
3478        }
3479    }
3480
3481    // private
3482    function onBeforeShow(m){
3483        var pm = m.parentMenu;
3484        if(!pm && !m.allowOtherMenus){
3485            hideAll();
3486        }else if(pm && pm.activeChild && active != m){
3487            pm.activeChild.hide();
3488        }
3489    }
3490
3491    // private this should really trigger on mouseup..
3492    function onMouseDown(e){
3493         Roo.log("on Mouse Up");
3494         
3495         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3496             Roo.log("MenuManager hideAll");
3497             hideAll();
3498             e.stopEvent();
3499         }
3500         
3501         
3502    }
3503
3504    // private
3505    function onBeforeCheck(mi, state){
3506        if(state){
3507            var g = groups[mi.group];
3508            for(var i = 0, l = g.length; i < l; i++){
3509                if(g[i] != mi){
3510                    g[i].setChecked(false);
3511                }
3512            }
3513        }
3514    }
3515
3516    return {
3517
3518        /**
3519         * Hides all menus that are currently visible
3520         */
3521        hideAll : function(){
3522             hideAll();  
3523        },
3524
3525        // private
3526        register : function(menu){
3527            if(!menus){
3528                init();
3529            }
3530            menus[menu.id] = menu;
3531            menu.on("beforehide", onBeforeHide);
3532            menu.on("hide", onHide);
3533            menu.on("beforeshow", onBeforeShow);
3534            menu.on("show", onShow);
3535            var g = menu.group;
3536            if(g && menu.events["checkchange"]){
3537                if(!groups[g]){
3538                    groups[g] = [];
3539                }
3540                groups[g].push(menu);
3541                menu.on("checkchange", onCheck);
3542            }
3543        },
3544
3545         /**
3546          * Returns a {@link Roo.menu.Menu} object
3547          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3548          * be used to generate and return a new Menu instance.
3549          */
3550        get : function(menu){
3551            if(typeof menu == "string"){ // menu id
3552                return menus[menu];
3553            }else if(menu.events){  // menu instance
3554                return menu;
3555            }
3556            /*else if(typeof menu.length == 'number'){ // array of menu items?
3557                return new Roo.bootstrap.Menu({items:menu});
3558            }else{ // otherwise, must be a config
3559                return new Roo.bootstrap.Menu(menu);
3560            }
3561            */
3562            return false;
3563        },
3564
3565        // private
3566        unregister : function(menu){
3567            delete menus[menu.id];
3568            menu.un("beforehide", onBeforeHide);
3569            menu.un("hide", onHide);
3570            menu.un("beforeshow", onBeforeShow);
3571            menu.un("show", onShow);
3572            var g = menu.group;
3573            if(g && menu.events["checkchange"]){
3574                groups[g].remove(menu);
3575                menu.un("checkchange", onCheck);
3576            }
3577        },
3578
3579        // private
3580        registerCheckable : function(menuItem){
3581            var g = menuItem.group;
3582            if(g){
3583                if(!groups[g]){
3584                    groups[g] = [];
3585                }
3586                groups[g].push(menuItem);
3587                menuItem.on("beforecheckchange", onBeforeCheck);
3588            }
3589        },
3590
3591        // private
3592        unregisterCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                groups[g].remove(menuItem);
3596                menuItem.un("beforecheckchange", onBeforeCheck);
3597            }
3598        }
3599    };
3600 }();/*
3601  * - LGPL
3602  *
3603  * menu
3604  * 
3605  */
3606
3607 /**
3608  * @class Roo.bootstrap.Menu
3609  * @extends Roo.bootstrap.Component
3610  * Bootstrap Menu class - container for MenuItems
3611  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3612  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3613  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3614  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3615   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3616   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3617  
3618  * @constructor
3619  * Create a new Menu
3620  * @param {Object} config The config object
3621  */
3622
3623
3624 Roo.bootstrap.Menu = function(config){
3625     
3626     if (config.type == 'treeview') {
3627         // normally menu's are drawn attached to the document to handle layering etc..
3628         // however treeview (used by the docs menu is drawn into the parent element)
3629         this.container_method = 'getChildContainer'; 
3630     }
3631     
3632     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3633     if (this.registerMenu && this.type != 'treeview')  {
3634         Roo.bootstrap.MenuMgr.register(this);
3635     }
3636     
3637     
3638     this.addEvents({
3639         /**
3640          * @event beforeshow
3641          * Fires before this menu is displayed (return false to block)
3642          * @param {Roo.menu.Menu} this
3643          */
3644         beforeshow : true,
3645         /**
3646          * @event beforehide
3647          * Fires before this menu is hidden (return false to block)
3648          * @param {Roo.menu.Menu} this
3649          */
3650         beforehide : true,
3651         /**
3652          * @event show
3653          * Fires after this menu is displayed
3654          * @param {Roo.menu.Menu} this
3655          */
3656         show : true,
3657         /**
3658          * @event hide
3659          * Fires after this menu is hidden
3660          * @param {Roo.menu.Menu} this
3661          */
3662         hide : true,
3663         /**
3664          * @event click
3665          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3666          * @param {Roo.menu.Menu} this
3667          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3668          * @param {Roo.EventObject} e
3669          */
3670         click : true,
3671         /**
3672          * @event mouseover
3673          * Fires when the mouse is hovering over this menu
3674          * @param {Roo.menu.Menu} this
3675          * @param {Roo.EventObject} e
3676          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3677          */
3678         mouseover : true,
3679         /**
3680          * @event mouseout
3681          * Fires when the mouse exits this menu
3682          * @param {Roo.menu.Menu} this
3683          * @param {Roo.EventObject} e
3684          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3685          */
3686         mouseout : true,
3687         /**
3688          * @event itemclick
3689          * Fires when a menu item contained in this menu is clicked
3690          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3691          * @param {Roo.EventObject} e
3692          */
3693         itemclick: true
3694     });
3695     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3696 };
3697
3698 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3699     
3700    /// html : false,
3701    
3702     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3703     type: false,
3704     /**
3705      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706      */
3707     registerMenu : true,
3708     
3709     menuItems :false, // stores the menu items..
3710     
3711     hidden:true,
3712         
3713     parentMenu : false,
3714     
3715     stopEvent : true,
3716     
3717     isLink : false,
3718     
3719     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720     
3721     hideTrigger : false,
3722     
3723     align : 'tl-bl?',
3724     
3725     
3726     getChildContainer : function() {
3727         return this.el;  
3728     },
3729     
3730     getAutoCreate : function(){
3731          
3732         //if (['right'].indexOf(this.align)!==-1) {
3733         //    cfg.cn[1].cls += ' pull-right'
3734         //}
3735          
3736         var cfg = {
3737             tag : 'ul',
3738             cls : 'dropdown-menu shadow' ,
3739             style : 'z-index:1000'
3740             
3741         };
3742         
3743         if (this.type === 'submenu') {
3744             cfg.cls = 'submenu active';
3745         }
3746         if (this.type === 'treeview') {
3747             cfg.cls = 'treeview-menu';
3748         }
3749         
3750         return cfg;
3751     },
3752     initEvents : function() {
3753         
3754        // Roo.log("ADD event");
3755        // Roo.log(this.triggerEl.dom);
3756         if (this.triggerEl) {
3757             
3758             this.triggerEl.on('click', this.onTriggerClick, this);
3759             
3760             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761             
3762             if (!this.hideTrigger) {
3763                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3764                     // dropdown toggle on the 'a' in BS4?
3765                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766                 } else {
3767                     this.triggerEl.addClass('dropdown-toggle');
3768                 }
3769             }
3770         }
3771         
3772         if (Roo.isTouch) {
3773             this.el.on('touchstart'  , this.onTouch, this);
3774         }
3775         this.el.on('click' , this.onClick, this);
3776
3777         this.el.on("mouseover", this.onMouseOver, this);
3778         this.el.on("mouseout", this.onMouseOut, this);
3779         
3780     },
3781     
3782     findTargetItem : function(e)
3783     {
3784         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3785         if(!t){
3786             return false;
3787         }
3788         //Roo.log(t);         Roo.log(t.id);
3789         if(t && t.id){
3790             //Roo.log(this.menuitems);
3791             return this.menuitems.get(t.id);
3792             
3793             //return this.items.get(t.menuItemId);
3794         }
3795         
3796         return false;
3797     },
3798     
3799     onTouch : function(e) 
3800     {
3801         Roo.log("menu.onTouch");
3802         //e.stopEvent(); this make the user popdown broken
3803         this.onClick(e);
3804     },
3805     
3806     onClick : function(e)
3807     {
3808         Roo.log("menu.onClick");
3809         
3810         var t = this.findTargetItem(e);
3811         if(!t || t.isContainer){
3812             return;
3813         }
3814         Roo.log(e);
3815         /*
3816         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3817             if(t == this.activeItem && t.shouldDeactivate(e)){
3818                 this.activeItem.deactivate();
3819                 delete this.activeItem;
3820                 return;
3821             }
3822             if(t.canActivate){
3823                 this.setActiveItem(t, true);
3824             }
3825             return;
3826             
3827             
3828         }
3829         */
3830        
3831         Roo.log('pass click event');
3832         
3833         t.onClick(e);
3834         
3835         this.fireEvent("click", this, t, e);
3836         
3837         var _this = this;
3838         
3839         if(!t.href.length || t.href == '#'){
3840             (function() { _this.hide(); }).defer(100);
3841         }
3842         
3843     },
3844     
3845     onMouseOver : function(e){
3846         var t  = this.findTargetItem(e);
3847         //Roo.log(t);
3848         //if(t){
3849         //    if(t.canActivate && !t.disabled){
3850         //        this.setActiveItem(t, true);
3851         //    }
3852         //}
3853         
3854         this.fireEvent("mouseover", this, e, t);
3855     },
3856     isVisible : function(){
3857         return !this.hidden;
3858     },
3859     onMouseOut : function(e){
3860         var t  = this.findTargetItem(e);
3861         
3862         //if(t ){
3863         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3864         //        this.activeItem.deactivate();
3865         //        delete this.activeItem;
3866         //    }
3867         //}
3868         this.fireEvent("mouseout", this, e, t);
3869     },
3870     
3871     
3872     /**
3873      * Displays this menu relative to another element
3874      * @param {String/HTMLElement/Roo.Element} element The element to align to
3875      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3876      * the element (defaults to this.defaultAlign)
3877      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878      */
3879     show : function(el, pos, parentMenu)
3880     {
3881         if (false === this.fireEvent("beforeshow", this)) {
3882             Roo.log("show canceled");
3883             return;
3884         }
3885         this.parentMenu = parentMenu;
3886         if(!this.el){
3887             this.render();
3888         }
3889         this.el.addClass('show'); // show otherwise we do not know how big we are..
3890          
3891         var xy = this.el.getAlignToXY(el, pos);
3892         
3893         // bl-tl << left align  below
3894         // tl-bl << left align 
3895         
3896         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3897             // if it goes to far to the right.. -> align left.
3898             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3899         }
3900         if(xy[0] < 0){
3901             // was left align - go right?
3902             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3903         }
3904         
3905         // goes down the bottom
3906         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907            xy[1]  < 0 ){
3908             var a = this.align.replace('?', '').split('-');
3909             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3910             
3911         }
3912         
3913         this.showAt(  xy , parentMenu, false);
3914     },
3915      /**
3916      * Displays this menu at a specific xy position
3917      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3918      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919      */
3920     showAt : function(xy, parentMenu, /* private: */_e){
3921         this.parentMenu = parentMenu;
3922         if(!this.el){
3923             this.render();
3924         }
3925         if(_e !== false){
3926             this.fireEvent("beforeshow", this);
3927             //xy = this.el.adjustForConstraints(xy);
3928         }
3929         
3930         //this.el.show();
3931         this.hideMenuItems();
3932         this.hidden = false;
3933         if (this.triggerEl) {
3934             this.triggerEl.addClass('open');
3935         }
3936         
3937         this.el.addClass('show');
3938         
3939         
3940         
3941         // reassign x when hitting right
3942         
3943         // reassign y when hitting bottom
3944         
3945         // but the list may align on trigger left or trigger top... should it be a properity?
3946         
3947         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3948             this.el.setXY(xy);
3949         }
3950         
3951         this.focus();
3952         this.fireEvent("show", this);
3953     },
3954     
3955     focus : function(){
3956         return;
3957         if(!this.hidden){
3958             this.doFocus.defer(50, this);
3959         }
3960     },
3961
3962     doFocus : function(){
3963         if(!this.hidden){
3964             this.focusEl.focus();
3965         }
3966     },
3967
3968     /**
3969      * Hides this menu and optionally all parent menus
3970      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971      */
3972     hide : function(deep)
3973     {
3974         if (false === this.fireEvent("beforehide", this)) {
3975             Roo.log("hide canceled");
3976             return;
3977         }
3978         this.hideMenuItems();
3979         if(this.el && this.isVisible()){
3980            
3981             if(this.activeItem){
3982                 this.activeItem.deactivate();
3983                 this.activeItem = null;
3984             }
3985             if (this.triggerEl) {
3986                 this.triggerEl.removeClass('open');
3987             }
3988             
3989             this.el.removeClass('show');
3990             this.hidden = true;
3991             this.fireEvent("hide", this);
3992         }
3993         if(deep === true && this.parentMenu){
3994             this.parentMenu.hide(true);
3995         }
3996     },
3997     
3998     onTriggerClick : function(e)
3999     {
4000         Roo.log('trigger click');
4001         
4002         var target = e.getTarget();
4003         
4004         Roo.log(target.nodeName.toLowerCase());
4005         
4006         if(target.nodeName.toLowerCase() === 'i'){
4007             e.preventDefault();
4008         }
4009         
4010     },
4011     
4012     onTriggerPress  : function(e)
4013     {
4014         Roo.log('trigger press');
4015         //Roo.log(e.getTarget());
4016        // Roo.log(this.triggerEl.dom);
4017        
4018         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4019         var pel = Roo.get(e.getTarget());
4020         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4021             Roo.log('is treeview or dropdown?');
4022             return;
4023         }
4024         
4025         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4026             return;
4027         }
4028         
4029         if (this.isVisible()) {
4030             Roo.log('hide');
4031             this.hide();
4032         } else {
4033             Roo.log('show');
4034             
4035             this.show(this.triggerEl, this.align, false);
4036         }
4037         
4038         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4039             e.stopEvent();
4040         }
4041         
4042     },
4043        
4044     
4045     hideMenuItems : function()
4046     {
4047         Roo.log("hide Menu Items");
4048         if (!this.el) { 
4049             return;
4050         }
4051         
4052         this.el.select('.open',true).each(function(aa) {
4053             
4054             aa.removeClass('open');
4055          
4056         });
4057     },
4058     addxtypeChild : function (tree, cntr) {
4059         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060           
4061         this.menuitems.add(comp);
4062         return comp;
4063
4064     },
4065     getEl : function()
4066     {
4067         Roo.log(this.el);
4068         return this.el;
4069     },
4070     
4071     clear : function()
4072     {
4073         this.getEl().dom.innerHTML = '';
4074         this.menuitems.clear();
4075     }
4076 });
4077
4078  
4079  /*
4080  * - LGPL
4081  *
4082  * menu item
4083  * 
4084  */
4085
4086
4087 /**
4088  * @class Roo.bootstrap.MenuItem
4089  * @extends Roo.bootstrap.Component
4090  * Bootstrap MenuItem class
4091  * @cfg {String} html the menu label
4092  * @cfg {String} href the link
4093  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4094  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4095  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4096  * @cfg {String} fa favicon to show on left of menu item.
4097  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4098  * 
4099  * 
4100  * @constructor
4101  * Create a new MenuItem
4102  * @param {Object} config The config object
4103  */
4104
4105
4106 Roo.bootstrap.MenuItem = function(config){
4107     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4108     this.addEvents({
4109         // raw events
4110         /**
4111          * @event click
4112          * The raw click event for the entire grid.
4113          * @param {Roo.bootstrap.MenuItem} this
4114          * @param {Roo.EventObject} e
4115          */
4116         "click" : true
4117     });
4118 };
4119
4120 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4121     
4122     href : false,
4123     html : false,
4124     preventDefault: false,
4125     isContainer : false,
4126     active : false,
4127     fa: false,
4128     
4129     getAutoCreate : function(){
4130         
4131         if(this.isContainer){
4132             return {
4133                 tag: 'li',
4134                 cls: 'dropdown-menu-item '
4135             };
4136         }
4137         var ctag = {
4138             tag: 'span',
4139             html: 'Link'
4140         };
4141         
4142         var anc = {
4143             tag : 'a',
4144             cls : 'dropdown-item',
4145             href : '#',
4146             cn : [  ]
4147         };
4148         
4149         if (this.fa !== false) {
4150             anc.cn.push({
4151                 tag : 'i',
4152                 cls : 'fa fa-' + this.fa
4153             });
4154         }
4155         
4156         anc.cn.push(ctag);
4157         
4158         
4159         var cfg= {
4160             tag: 'li',
4161             cls: 'dropdown-menu-item',
4162             cn: [ anc ]
4163         };
4164         if (this.parent().type == 'treeview') {
4165             cfg.cls = 'treeview-menu';
4166         }
4167         if (this.active) {
4168             cfg.cls += ' active';
4169         }
4170         
4171         
4172         
4173         anc.href = this.href || cfg.cn[0].href ;
4174         ctag.html = this.html || cfg.cn[0].html ;
4175         return cfg;
4176     },
4177     
4178     initEvents: function()
4179     {
4180         if (this.parent().type == 'treeview') {
4181             this.el.select('a').on('click', this.onClick, this);
4182         }
4183         
4184         if (this.menu) {
4185             this.menu.parentType = this.xtype;
4186             this.menu.triggerEl = this.el;
4187             this.menu = this.addxtype(Roo.apply({}, this.menu));
4188         }
4189         
4190     },
4191     onClick : function(e)
4192     {
4193         Roo.log('item on click ');
4194         
4195         if(this.preventDefault){
4196             e.preventDefault();
4197         }
4198         //this.parent().hideMenuItems();
4199         
4200         this.fireEvent('click', this, e);
4201     },
4202     getEl : function()
4203     {
4204         return this.el;
4205     } 
4206 });
4207
4208  
4209
4210  /*
4211  * - LGPL
4212  *
4213  * menu separator
4214  * 
4215  */
4216
4217
4218 /**
4219  * @class Roo.bootstrap.MenuSeparator
4220  * @extends Roo.bootstrap.Component
4221  * Bootstrap MenuSeparator class
4222  * 
4223  * @constructor
4224  * Create a new MenuItem
4225  * @param {Object} config The config object
4226  */
4227
4228
4229 Roo.bootstrap.MenuSeparator = function(config){
4230     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4231 };
4232
4233 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4234     
4235     getAutoCreate : function(){
4236         var cfg = {
4237             cls: 'divider',
4238             tag : 'li'
4239         };
4240         
4241         return cfg;
4242     }
4243    
4244 });
4245
4246  
4247
4248  
4249 /*
4250 * Licence: LGPL
4251 */
4252
4253 /**
4254  * @class Roo.bootstrap.Modal
4255  * @extends Roo.bootstrap.Component
4256  * Bootstrap Modal class
4257  * @cfg {String} title Title of dialog
4258  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4259  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4260  * @cfg {Boolean} specificTitle default false
4261  * @cfg {Array} buttons Array of buttons or standard button set..
4262  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4263  * @cfg {Boolean} animate default true
4264  * @cfg {Boolean} allow_close default true
4265  * @cfg {Boolean} fitwindow default false
4266  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4267  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4268  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4269  * @cfg {String} size (sm|lg|xl) default empty
4270  * @cfg {Number} max_width set the max width of modal
4271  * @cfg {Boolean} editableTitle can the title be edited
4272
4273  *
4274  *
4275  * @constructor
4276  * Create a new Modal Dialog
4277  * @param {Object} config The config object
4278  */
4279
4280 Roo.bootstrap.Modal = function(config){
4281     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4282     this.addEvents({
4283         // raw events
4284         /**
4285          * @event btnclick
4286          * The raw btnclick event for the button
4287          * @param {Roo.EventObject} e
4288          */
4289         "btnclick" : true,
4290         /**
4291          * @event resize
4292          * Fire when dialog resize
4293          * @param {Roo.bootstrap.Modal} this
4294          * @param {Roo.EventObject} e
4295          */
4296         "resize" : true,
4297         /**
4298          * @event titlechanged
4299          * Fire when the editable title has been changed
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} value
4302          */
4303         "titlechanged" : true 
4304         
4305     });
4306     this.buttons = this.buttons || [];
4307
4308     if (this.tmpl) {
4309         this.tmpl = Roo.factory(this.tmpl);
4310     }
4311
4312 };
4313
4314 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4315
4316     title : 'test dialog',
4317
4318     buttons : false,
4319
4320     // set on load...
4321
4322     html: false,
4323
4324     tmp: false,
4325
4326     specificTitle: false,
4327
4328     buttonPosition: 'right',
4329
4330     allow_close : true,
4331
4332     animate : true,
4333
4334     fitwindow: false,
4335     
4336      // private
4337     dialogEl: false,
4338     bodyEl:  false,
4339     footerEl:  false,
4340     titleEl:  false,
4341     closeEl:  false,
4342
4343     size: '',
4344     
4345     max_width: 0,
4346     
4347     max_height: 0,
4348     
4349     fit_content: false,
4350     editableTitle  : false,
4351
4352     onRender : function(ct, position)
4353     {
4354         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4355
4356         if(!this.el){
4357             var cfg = Roo.apply({},  this.getAutoCreate());
4358             cfg.id = Roo.id();
4359             //if(!cfg.name){
4360             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361             //}
4362             //if (!cfg.name.length) {
4363             //    delete cfg.name;
4364            // }
4365             if (this.cls) {
4366                 cfg.cls += ' ' + this.cls;
4367             }
4368             if (this.style) {
4369                 cfg.style = this.style;
4370             }
4371             this.el = Roo.get(document.body).createChild(cfg, position);
4372         }
4373         //var type = this.el.dom.type;
4374
4375
4376         if(this.tabIndex !== undefined){
4377             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4378         }
4379
4380         this.dialogEl = this.el.select('.modal-dialog',true).first();
4381         this.bodyEl = this.el.select('.modal-body',true).first();
4382         this.closeEl = this.el.select('.modal-header .close', true).first();
4383         this.headerEl = this.el.select('.modal-header',true).first();
4384         this.titleEl = this.el.select('.modal-title',true).first();
4385         this.footerEl = this.el.select('.modal-footer',true).first();
4386
4387         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388         
4389         //this.el.addClass("x-dlg-modal");
4390
4391         if (this.buttons.length) {
4392             Roo.each(this.buttons, function(bb) {
4393                 var b = Roo.apply({}, bb);
4394                 b.xns = b.xns || Roo.bootstrap;
4395                 b.xtype = b.xtype || 'Button';
4396                 if (typeof(b.listeners) == 'undefined') {
4397                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4398                 }
4399
4400                 var btn = Roo.factory(b);
4401
4402                 btn.render(this.getButtonContainer());
4403
4404             },this);
4405         }
4406         // render the children.
4407         var nitems = [];
4408
4409         if(typeof(this.items) != 'undefined'){
4410             var items = this.items;
4411             delete this.items;
4412
4413             for(var i =0;i < items.length;i++) {
4414                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4415             }
4416         }
4417
4418         this.items = nitems;
4419
4420         // where are these used - they used to be body/close/footer
4421
4422
4423         this.initEvents();
4424         //this.el.addClass([this.fieldClass, this.cls]);
4425
4426     },
4427
4428     getAutoCreate : function()
4429     {
4430         // we will default to modal-body-overflow - might need to remove or make optional later.
4431         var bdy = {
4432                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4433                 html : this.html || ''
4434         };
4435
4436         var title = {
4437             tag: 'h5',
4438             cls : 'modal-title',
4439             html : this.title
4440         };
4441
4442         if(this.specificTitle){ // WTF is this?
4443             title = this.title;
4444         }
4445
4446         var header = [];
4447         if (this.allow_close && Roo.bootstrap.version == 3) {
4448             header.push({
4449                 tag: 'button',
4450                 cls : 'close',
4451                 html : '&times'
4452             });
4453         }
4454
4455         header.push(title);
4456
4457         if (this.editableTitle) {
4458             header.push({
4459                 cls: 'form-control roo-editable-title d-none',
4460                 tag: 'input',
4461                 type: 'text'
4462             });
4463         }
4464         
4465         if (this.allow_close && Roo.bootstrap.version == 4) {
4466             header.push({
4467                 tag: 'button',
4468                 cls : 'close',
4469                 html : '&times'
4470             });
4471         }
4472         
4473         var size = '';
4474
4475         if(this.size.length){
4476             size = 'modal-' + this.size;
4477         }
4478         
4479         var footer = Roo.bootstrap.version == 3 ?
4480             {
4481                 cls : 'modal-footer',
4482                 cn : [
4483                     {
4484                         tag: 'div',
4485                         cls: 'btn-' + this.buttonPosition
4486                     }
4487                 ]
4488
4489             } :
4490             {  // BS4 uses mr-auto on left buttons....
4491                 cls : 'modal-footer'
4492             };
4493
4494             
4495
4496         
4497         
4498         var modal = {
4499             cls: "modal",
4500              cn : [
4501                 {
4502                     cls: "modal-dialog " + size,
4503                     cn : [
4504                         {
4505                             cls : "modal-content",
4506                             cn : [
4507                                 {
4508                                     cls : 'modal-header',
4509                                     cn : header
4510                                 },
4511                                 bdy,
4512                                 footer
4513                             ]
4514
4515                         }
4516                     ]
4517
4518                 }
4519             ]
4520         };
4521
4522         if(this.animate){
4523             modal.cls += ' fade';
4524         }
4525
4526         return modal;
4527
4528     },
4529     getChildContainer : function() {
4530
4531          return this.bodyEl;
4532
4533     },
4534     getButtonContainer : function() {
4535         
4536          return Roo.bootstrap.version == 4 ?
4537             this.el.select('.modal-footer',true).first()
4538             : this.el.select('.modal-footer div',true).first();
4539
4540     },
4541     initEvents : function()
4542     {
4543         if (this.allow_close) {
4544             this.closeEl.on('click', this.hide, this);
4545         }
4546         Roo.EventManager.onWindowResize(this.resize, this, true);
4547         if (this.editableTitle) {
4548             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4549             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4550             this.headerEditEl.on('keyup', function(e) {
4551                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4552                         this.toggleHeaderInput(false)
4553                     }
4554                 }, this);
4555             this.headerEditEl.on('blur', function(e) {
4556                 this.toggleHeaderInput(false)
4557             },this);
4558         }
4559
4560     },
4561   
4562
4563     resize : function()
4564     {
4565         this.maskEl.setSize(
4566             Roo.lib.Dom.getViewWidth(true),
4567             Roo.lib.Dom.getViewHeight(true)
4568         );
4569         
4570         if (this.fitwindow) {
4571             
4572            this.dialogEl.setStyle( { 'max-width' : '100%' });
4573             this.setSize(
4574                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4575                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4576             );
4577             return;
4578         }
4579         
4580         if(this.max_width !== 0) {
4581             
4582             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4583             
4584             if(this.height) {
4585                 this.setSize(w, this.height);
4586                 return;
4587             }
4588             
4589             if(this.max_height) {
4590                 this.setSize(w,Math.min(
4591                     this.max_height,
4592                     Roo.lib.Dom.getViewportHeight(true) - 60
4593                 ));
4594                 
4595                 return;
4596             }
4597             
4598             if(!this.fit_content) {
4599                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4600                 return;
4601             }
4602             
4603             this.setSize(w, Math.min(
4604                 60 +
4605                 this.headerEl.getHeight() + 
4606                 this.footerEl.getHeight() + 
4607                 this.getChildHeight(this.bodyEl.dom.childNodes),
4608                 Roo.lib.Dom.getViewportHeight(true) - 60)
4609             );
4610         }
4611         
4612     },
4613
4614     setSize : function(w,h)
4615     {
4616         if (!w && !h) {
4617             return;
4618         }
4619         
4620         this.resizeTo(w,h);
4621     },
4622
4623     show : function() {
4624
4625         if (!this.rendered) {
4626             this.render();
4627         }
4628         this.toggleHeaderInput(false);
4629         //this.el.setStyle('display', 'block');
4630         this.el.removeClass('hideing');
4631         this.el.dom.style.display='block';
4632         
4633         Roo.get(document.body).addClass('modal-open');
4634  
4635         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4636             
4637             (function(){
4638                 this.el.addClass('show');
4639                 this.el.addClass('in');
4640             }).defer(50, this);
4641         }else{
4642             this.el.addClass('show');
4643             this.el.addClass('in');
4644         }
4645
4646         // not sure how we can show data in here..
4647         //if (this.tmpl) {
4648         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4649         //}
4650
4651         Roo.get(document.body).addClass("x-body-masked");
4652         
4653         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4654         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4655         this.maskEl.dom.style.display = 'block';
4656         this.maskEl.addClass('show');
4657         
4658         
4659         this.resize();
4660         
4661         this.fireEvent('show', this);
4662
4663         // set zindex here - otherwise it appears to be ignored...
4664         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4665
4666         (function () {
4667             this.items.forEach( function(e) {
4668                 e.layout ? e.layout() : false;
4669
4670             });
4671         }).defer(100,this);
4672
4673     },
4674     hide : function()
4675     {
4676         if(this.fireEvent("beforehide", this) !== false){
4677             
4678             this.maskEl.removeClass('show');
4679             
4680             this.maskEl.dom.style.display = '';
4681             Roo.get(document.body).removeClass("x-body-masked");
4682             this.el.removeClass('in');
4683             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684
4685             if(this.animate){ // why
4686                 this.el.addClass('hideing');
4687                 this.el.removeClass('show');
4688                 (function(){
4689                     if (!this.el.hasClass('hideing')) {
4690                         return; // it's been shown again...
4691                     }
4692                     
4693                     this.el.dom.style.display='';
4694
4695                     Roo.get(document.body).removeClass('modal-open');
4696                     this.el.removeClass('hideing');
4697                 }).defer(150,this);
4698                 
4699             }else{
4700                 this.el.removeClass('show');
4701                 this.el.dom.style.display='';
4702                 Roo.get(document.body).removeClass('modal-open');
4703
4704             }
4705             this.fireEvent('hide', this);
4706         }
4707     },
4708     isVisible : function()
4709     {
4710         
4711         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4712         
4713     },
4714
4715     addButton : function(str, cb)
4716     {
4717
4718
4719         var b = Roo.apply({}, { html : str } );
4720         b.xns = b.xns || Roo.bootstrap;
4721         b.xtype = b.xtype || 'Button';
4722         if (typeof(b.listeners) == 'undefined') {
4723             b.listeners = { click : cb.createDelegate(this)  };
4724         }
4725
4726         var btn = Roo.factory(b);
4727
4728         btn.render(this.getButtonContainer());
4729
4730         return btn;
4731
4732     },
4733
4734     setDefaultButton : function(btn)
4735     {
4736         //this.el.select('.modal-footer').()
4737     },
4738
4739     resizeTo: function(w,h)
4740     {
4741         this.dialogEl.setWidth(w);
4742         
4743         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4744
4745         this.bodyEl.setHeight(h - diff);
4746         
4747         this.fireEvent('resize', this);
4748     },
4749     
4750     setContentSize  : function(w, h)
4751     {
4752
4753     },
4754     onButtonClick: function(btn,e)
4755     {
4756         //Roo.log([a,b,c]);
4757         this.fireEvent('btnclick', btn.name, e);
4758     },
4759      /**
4760      * Set the title of the Dialog
4761      * @param {String} str new Title
4762      */
4763     setTitle: function(str) {
4764         this.titleEl.dom.innerHTML = str;
4765         this.title = str;
4766     },
4767     /**
4768      * Set the body of the Dialog
4769      * @param {String} str new Title
4770      */
4771     setBody: function(str) {
4772         this.bodyEl.dom.innerHTML = str;
4773     },
4774     /**
4775      * Set the body of the Dialog using the template
4776      * @param {Obj} data - apply this data to the template and replace the body contents.
4777      */
4778     applyBody: function(obj)
4779     {
4780         if (!this.tmpl) {
4781             Roo.log("Error - using apply Body without a template");
4782             //code
4783         }
4784         this.tmpl.overwrite(this.bodyEl, obj);
4785     },
4786     
4787     getChildHeight : function(child_nodes)
4788     {
4789         if(
4790             !child_nodes ||
4791             child_nodes.length == 0
4792         ) {
4793             return 0;
4794         }
4795         
4796         var child_height = 0;
4797         
4798         for(var i = 0; i < child_nodes.length; i++) {
4799             
4800             /*
4801             * for modal with tabs...
4802             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803                 
4804                 var layout_childs = child_nodes[i].childNodes;
4805                 
4806                 for(var j = 0; j < layout_childs.length; j++) {
4807                     
4808                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809                         
4810                         var layout_body_childs = layout_childs[j].childNodes;
4811                         
4812                         for(var k = 0; k < layout_body_childs.length; k++) {
4813                             
4814                             if(layout_body_childs[k].classList.contains('navbar')) {
4815                                 child_height += layout_body_childs[k].offsetHeight;
4816                                 continue;
4817                             }
4818                             
4819                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820                                 
4821                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822                                 
4823                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824                                     
4825                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4826                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4827                                         continue;
4828                                     }
4829                                     
4830                                 }
4831                                 
4832                             }
4833                             
4834                         }
4835                     }
4836                 }
4837                 continue;
4838             }
4839             */
4840             
4841             child_height += child_nodes[i].offsetHeight;
4842             // Roo.log(child_nodes[i].offsetHeight);
4843         }
4844         
4845         return child_height;
4846     },
4847     toggleHeaderInput : function(is_edit)
4848     {
4849         if (!this.editableTitle) {
4850             return; // not editable.
4851         }
4852         if (is_edit && this.is_header_editing) {
4853             return; // already editing..
4854         }
4855         if (is_edit) {
4856     
4857             this.headerEditEl.dom.value = this.title;
4858             this.headerEditEl.removeClass('d-none');
4859             this.headerEditEl.dom.focus();
4860             this.titleEl.addClass('d-none');
4861             
4862             this.is_header_editing = true;
4863             return
4864         }
4865         // flip back to not editing.
4866         this.title = this.headerEditEl.dom.value;
4867         this.headerEditEl.addClass('d-none');
4868         this.titleEl.removeClass('d-none');
4869         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4870         this.is_header_editing = false;
4871         this.fireEvent('titlechanged', this, this.title);
4872     
4873             
4874         
4875     }
4876
4877 });
4878
4879
4880 Roo.apply(Roo.bootstrap.Modal,  {
4881     /**
4882          * Button config that displays a single OK button
4883          * @type Object
4884          */
4885         OK :  [{
4886             name : 'ok',
4887             weight : 'primary',
4888             html : 'OK'
4889         }],
4890         /**
4891          * Button config that displays Yes and No buttons
4892          * @type Object
4893          */
4894         YESNO : [
4895             {
4896                 name  : 'no',
4897                 html : 'No'
4898             },
4899             {
4900                 name  :'yes',
4901                 weight : 'primary',
4902                 html : 'Yes'
4903             }
4904         ],
4905
4906         /**
4907          * Button config that displays OK and Cancel buttons
4908          * @type Object
4909          */
4910         OKCANCEL : [
4911             {
4912                name : 'cancel',
4913                 html : 'Cancel'
4914             },
4915             {
4916                 name : 'ok',
4917                 weight : 'primary',
4918                 html : 'OK'
4919             }
4920         ],
4921         /**
4922          * Button config that displays Yes, No and Cancel buttons
4923          * @type Object
4924          */
4925         YESNOCANCEL : [
4926             {
4927                 name : 'yes',
4928                 weight : 'primary',
4929                 html : 'Yes'
4930             },
4931             {
4932                 name : 'no',
4933                 html : 'No'
4934             },
4935             {
4936                 name : 'cancel',
4937                 html : 'Cancel'
4938             }
4939         ],
4940         
4941         zIndex : 10001
4942 });
4943
4944 /*
4945  * - LGPL
4946  *
4947  * messagebox - can be used as a replace
4948  * 
4949  */
4950 /**
4951  * @class Roo.MessageBox
4952  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4953  * Example usage:
4954  *<pre><code>
4955 // Basic alert:
4956 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957
4958 // Prompt for user data:
4959 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960     if (btn == 'ok'){
4961         // process text value...
4962     }
4963 });
4964
4965 // Show a dialog using config options:
4966 Roo.Msg.show({
4967    title:'Save Changes?',
4968    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4969    buttons: Roo.Msg.YESNOCANCEL,
4970    fn: processResult,
4971    animEl: 'elId'
4972 });
4973 </code></pre>
4974  * @singleton
4975  */
4976 Roo.bootstrap.MessageBox = function(){
4977     var dlg, opt, mask, waitTimer;
4978     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4979     var buttons, activeTextEl, bwidth;
4980
4981     
4982     // private
4983     var handleButton = function(button){
4984         dlg.hide();
4985         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4986     };
4987
4988     // private
4989     var handleHide = function(){
4990         if(opt && opt.cls){
4991             dlg.el.removeClass(opt.cls);
4992         }
4993         //if(waitTimer){
4994         //    Roo.TaskMgr.stop(waitTimer);
4995         //    waitTimer = null;
4996         //}
4997     };
4998
4999     // private
5000     var updateButtons = function(b){
5001         var width = 0;
5002         if(!b){
5003             buttons["ok"].hide();
5004             buttons["cancel"].hide();
5005             buttons["yes"].hide();
5006             buttons["no"].hide();
5007             dlg.footerEl.hide();
5008             
5009             return width;
5010         }
5011         dlg.footerEl.show();
5012         for(var k in buttons){
5013             if(typeof buttons[k] != "function"){
5014                 if(b[k]){
5015                     buttons[k].show();
5016                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5017                     width += buttons[k].el.getWidth()+15;
5018                 }else{
5019                     buttons[k].hide();
5020                 }
5021             }
5022         }
5023         return width;
5024     };
5025
5026     // private
5027     var handleEsc = function(d, k, e){
5028         if(opt && opt.closable !== false){
5029             dlg.hide();
5030         }
5031         if(e){
5032             e.stopEvent();
5033         }
5034     };
5035
5036     return {
5037         /**
5038          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5039          * @return {Roo.BasicDialog} The BasicDialog element
5040          */
5041         getDialog : function(){
5042            if(!dlg){
5043                 dlg = new Roo.bootstrap.Modal( {
5044                     //draggable: true,
5045                     //resizable:false,
5046                     //constraintoviewport:false,
5047                     //fixedcenter:true,
5048                     //collapsible : false,
5049                     //shim:true,
5050                     //modal: true,
5051                 //    width: 'auto',
5052                   //  height:100,
5053                     //buttonAlign:"center",
5054                     closeClick : function(){
5055                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5056                             handleButton("no");
5057                         }else{
5058                             handleButton("cancel");
5059                         }
5060                     }
5061                 });
5062                 dlg.render();
5063                 dlg.on("hide", handleHide);
5064                 mask = dlg.mask;
5065                 //dlg.addKeyListener(27, handleEsc);
5066                 buttons = {};
5067                 this.buttons = buttons;
5068                 var bt = this.buttonText;
5069                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5070                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5071                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5072                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073                 //Roo.log(buttons);
5074                 bodyEl = dlg.bodyEl.createChild({
5075
5076                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5077                         '<textarea class="roo-mb-textarea"></textarea>' +
5078                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5079                 });
5080                 msgEl = bodyEl.dom.firstChild;
5081                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5082                 textboxEl.enableDisplayMode();
5083                 textboxEl.addKeyListener([10,13], function(){
5084                     if(dlg.isVisible() && opt && opt.buttons){
5085                         if(opt.buttons.ok){
5086                             handleButton("ok");
5087                         }else if(opt.buttons.yes){
5088                             handleButton("yes");
5089                         }
5090                     }
5091                 });
5092                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5093                 textareaEl.enableDisplayMode();
5094                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5095                 progressEl.enableDisplayMode();
5096                 
5097                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5098                 var pf = progressEl.dom.firstChild;
5099                 if (pf) {
5100                     pp = Roo.get(pf.firstChild);
5101                     pp.setHeight(pf.offsetHeight);
5102                 }
5103                 
5104             }
5105             return dlg;
5106         },
5107
5108         /**
5109          * Updates the message box body text
5110          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5111          * the XHTML-compliant non-breaking space character '&amp;#160;')
5112          * @return {Roo.MessageBox} This message box
5113          */
5114         updateText : function(text)
5115         {
5116             if(!dlg.isVisible() && !opt.width){
5117                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5118                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119             }
5120             msgEl.innerHTML = text || '&#160;';
5121       
5122             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5123             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124             var w = Math.max(
5125                     Math.min(opt.width || cw , this.maxWidth), 
5126                     Math.max(opt.minWidth || this.minWidth, bwidth)
5127             );
5128             if(opt.prompt){
5129                 activeTextEl.setWidth(w);
5130             }
5131             if(dlg.isVisible()){
5132                 dlg.fixedcenter = false;
5133             }
5134             // to big, make it scroll. = But as usual stupid IE does not support
5135             // !important..
5136             
5137             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5138                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5139                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140             } else {
5141                 bodyEl.dom.style.height = '';
5142                 bodyEl.dom.style.overflowY = '';
5143             }
5144             if (cw > w) {
5145                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146             } else {
5147                 bodyEl.dom.style.overflowX = '';
5148             }
5149             
5150             dlg.setContentSize(w, bodyEl.getHeight());
5151             if(dlg.isVisible()){
5152                 dlg.fixedcenter = true;
5153             }
5154             return this;
5155         },
5156
5157         /**
5158          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5159          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5160          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5161          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5162          * @return {Roo.MessageBox} This message box
5163          */
5164         updateProgress : function(value, text){
5165             if(text){
5166                 this.updateText(text);
5167             }
5168             
5169             if (pp) { // weird bug on my firefox - for some reason this is not defined
5170                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5171                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5172             }
5173             return this;
5174         },        
5175
5176         /**
5177          * Returns true if the message box is currently displayed
5178          * @return {Boolean} True if the message box is visible, else false
5179          */
5180         isVisible : function(){
5181             return dlg && dlg.isVisible();  
5182         },
5183
5184         /**
5185          * Hides the message box if it is displayed
5186          */
5187         hide : function(){
5188             if(this.isVisible()){
5189                 dlg.hide();
5190             }  
5191         },
5192
5193         /**
5194          * Displays a new message box, or reinitializes an existing message box, based on the config options
5195          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5196          * The following config object properties are supported:
5197          * <pre>
5198 Property    Type             Description
5199 ----------  ---------------  ------------------------------------------------------------------------------------
5200 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5201                                    closes (defaults to undefined)
5202 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5203                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5204 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5205                                    progress and wait dialogs will ignore this property and always hide the
5206                                    close button as they can only be closed programmatically.
5207 cls               String           A custom CSS class to apply to the message box element
5208 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5209                                    displayed (defaults to 75)
5210 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5211                                    function will be btn (the name of the button that was clicked, if applicable,
5212                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5213                                    Progress and wait dialogs will ignore this option since they do not respond to
5214                                    user actions and can only be closed programmatically, so any required function
5215                                    should be called by the same code after it closes the dialog.
5216 icon              String           A CSS class that provides a background image to be used as an icon for
5217                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5218 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5219 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5220 modal             Boolean          False to allow user interaction with the page while the message box is
5221                                    displayed (defaults to true)
5222 msg               String           A string that will replace the existing message box body text (defaults
5223                                    to the XHTML-compliant non-breaking space character '&#160;')
5224 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5225 progress          Boolean          True to display a progress bar (defaults to false)
5226 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5227 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5228 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5229 title             String           The title text
5230 value             String           The string value to set into the active textbox element if displayed
5231 wait              Boolean          True to display a progress bar (defaults to false)
5232 width             Number           The width of the dialog in pixels
5233 </pre>
5234          *
5235          * Example usage:
5236          * <pre><code>
5237 Roo.Msg.show({
5238    title: 'Address',
5239    msg: 'Please enter your address:',
5240    width: 300,
5241    buttons: Roo.MessageBox.OKCANCEL,
5242    multiline: true,
5243    fn: saveAddress,
5244    animEl: 'addAddressBtn'
5245 });
5246 </code></pre>
5247          * @param {Object} config Configuration options
5248          * @return {Roo.MessageBox} This message box
5249          */
5250         show : function(options)
5251         {
5252             
5253             // this causes nightmares if you show one dialog after another
5254             // especially on callbacks..
5255              
5256             if(this.isVisible()){
5257                 
5258                 this.hide();
5259                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5260                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5261                 Roo.log("New Dialog Message:" +  options.msg )
5262                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5263                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5264                 
5265             }
5266             var d = this.getDialog();
5267             opt = options;
5268             d.setTitle(opt.title || "&#160;");
5269             d.closeEl.setDisplayed(opt.closable !== false);
5270             activeTextEl = textboxEl;
5271             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5272             if(opt.prompt){
5273                 if(opt.multiline){
5274                     textboxEl.hide();
5275                     textareaEl.show();
5276                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5277                         opt.multiline : this.defaultTextHeight);
5278                     activeTextEl = textareaEl;
5279                 }else{
5280                     textboxEl.show();
5281                     textareaEl.hide();
5282                 }
5283             }else{
5284                 textboxEl.hide();
5285                 textareaEl.hide();
5286             }
5287             progressEl.setDisplayed(opt.progress === true);
5288             if (opt.progress) {
5289                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290             }
5291             this.updateProgress(0);
5292             activeTextEl.dom.value = opt.value || "";
5293             if(opt.prompt){
5294                 dlg.setDefaultButton(activeTextEl);
5295             }else{
5296                 var bs = opt.buttons;
5297                 var db = null;
5298                 if(bs && bs.ok){
5299                     db = buttons["ok"];
5300                 }else if(bs && bs.yes){
5301                     db = buttons["yes"];
5302                 }
5303                 dlg.setDefaultButton(db);
5304             }
5305             bwidth = updateButtons(opt.buttons);
5306             this.updateText(opt.msg);
5307             if(opt.cls){
5308                 d.el.addClass(opt.cls);
5309             }
5310             d.proxyDrag = opt.proxyDrag === true;
5311             d.modal = opt.modal !== false;
5312             d.mask = opt.modal !== false ? mask : false;
5313             if(!d.isVisible()){
5314                 // force it to the end of the z-index stack so it gets a cursor in FF
5315                 document.body.appendChild(dlg.el.dom);
5316                 d.animateTarget = null;
5317                 d.show(options.animEl);
5318             }
5319             return this;
5320         },
5321
5322         /**
5323          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5324          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5325          * and closing the message box when the process is complete.
5326          * @param {String} title The title bar text
5327          * @param {String} msg The message box body text
5328          * @return {Roo.MessageBox} This message box
5329          */
5330         progress : function(title, msg){
5331             this.show({
5332                 title : title,
5333                 msg : msg,
5334                 buttons: false,
5335                 progress:true,
5336                 closable:false,
5337                 minWidth: this.minProgressWidth,
5338                 modal : true
5339             });
5340             return this;
5341         },
5342
5343         /**
5344          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5345          * If a callback function is passed it will be called after the user clicks the button, and the
5346          * id of the button that was clicked will be passed as the only parameter to the callback
5347          * (could also be the top-right close button).
5348          * @param {String} title The title bar text
5349          * @param {String} msg The message box body text
5350          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5351          * @param {Object} scope (optional) The scope of the callback function
5352          * @return {Roo.MessageBox} This message box
5353          */
5354         alert : function(title, msg, fn, scope)
5355         {
5356             this.show({
5357                 title : title,
5358                 msg : msg,
5359                 buttons: this.OK,
5360                 fn: fn,
5361                 closable : false,
5362                 scope : scope,
5363                 modal : true
5364             });
5365             return this;
5366         },
5367
5368         /**
5369          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5370          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5371          * You are responsible for closing the message box when the process is complete.
5372          * @param {String} msg The message box body text
5373          * @param {String} title (optional) The title bar text
5374          * @return {Roo.MessageBox} This message box
5375          */
5376         wait : function(msg, title){
5377             this.show({
5378                 title : title,
5379                 msg : msg,
5380                 buttons: false,
5381                 closable:false,
5382                 progress:true,
5383                 modal:true,
5384                 width:300,
5385                 wait:true
5386             });
5387             waitTimer = Roo.TaskMgr.start({
5388                 run: function(i){
5389                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5390                 },
5391                 interval: 1000
5392             });
5393             return this;
5394         },
5395
5396         /**
5397          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5398          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5399          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5400          * @param {String} title The title bar text
5401          * @param {String} msg The message box body text
5402          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5403          * @param {Object} scope (optional) The scope of the callback function
5404          * @return {Roo.MessageBox} This message box
5405          */
5406         confirm : function(title, msg, fn, scope){
5407             this.show({
5408                 title : title,
5409                 msg : msg,
5410                 buttons: this.YESNO,
5411                 fn: fn,
5412                 scope : scope,
5413                 modal : true
5414             });
5415             return this;
5416         },
5417
5418         /**
5419          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5420          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5421          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5422          * (could also be the top-right close button) and the text that was entered will be passed as the two
5423          * parameters to the callback.
5424          * @param {String} title The title bar text
5425          * @param {String} msg The message box body text
5426          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5427          * @param {Object} scope (optional) The scope of the callback function
5428          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5429          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5430          * @return {Roo.MessageBox} This message box
5431          */
5432         prompt : function(title, msg, fn, scope, multiline){
5433             this.show({
5434                 title : title,
5435                 msg : msg,
5436                 buttons: this.OKCANCEL,
5437                 fn: fn,
5438                 minWidth:250,
5439                 scope : scope,
5440                 prompt:true,
5441                 multiline: multiline,
5442                 modal : true
5443             });
5444             return this;
5445         },
5446
5447         /**
5448          * Button config that displays a single OK button
5449          * @type Object
5450          */
5451         OK : {ok:true},
5452         /**
5453          * Button config that displays Yes and No buttons
5454          * @type Object
5455          */
5456         YESNO : {yes:true, no:true},
5457         /**
5458          * Button config that displays OK and Cancel buttons
5459          * @type Object
5460          */
5461         OKCANCEL : {ok:true, cancel:true},
5462         /**
5463          * Button config that displays Yes, No and Cancel buttons
5464          * @type Object
5465          */
5466         YESNOCANCEL : {yes:true, no:true, cancel:true},
5467
5468         /**
5469          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5470          * @type Number
5471          */
5472         defaultTextHeight : 75,
5473         /**
5474          * The maximum width in pixels of the message box (defaults to 600)
5475          * @type Number
5476          */
5477         maxWidth : 600,
5478         /**
5479          * The minimum width in pixels of the message box (defaults to 100)
5480          * @type Number
5481          */
5482         minWidth : 100,
5483         /**
5484          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5485          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5486          * @type Number
5487          */
5488         minProgressWidth : 250,
5489         /**
5490          * An object containing the default button text strings that can be overriden for localized language support.
5491          * Supported properties are: ok, cancel, yes and no.
5492          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5493          * @type Object
5494          */
5495         buttonText : {
5496             ok : "OK",
5497             cancel : "Cancel",
5498             yes : "Yes",
5499             no : "No"
5500         }
5501     };
5502 }();
5503
5504 /**
5505  * Shorthand for {@link Roo.MessageBox}
5506  */
5507 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5508 Roo.Msg = Roo.Msg || Roo.MessageBox;
5509 /*
5510  * - LGPL
5511  *
5512  * navbar
5513  * 
5514  */
5515
5516 /**
5517  * @class Roo.bootstrap.Navbar
5518  * @extends Roo.bootstrap.Component
5519  * Bootstrap Navbar class
5520
5521  * @constructor
5522  * Create a new Navbar
5523  * @param {Object} config The config object
5524  */
5525
5526
5527 Roo.bootstrap.Navbar = function(config){
5528     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5529     this.addEvents({
5530         // raw events
5531         /**
5532          * @event beforetoggle
5533          * Fire before toggle the menu
5534          * @param {Roo.EventObject} e
5535          */
5536         "beforetoggle" : true
5537     });
5538 };
5539
5540 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5541     
5542     
5543    
5544     // private
5545     navItems : false,
5546     loadMask : false,
5547     
5548     
5549     getAutoCreate : function(){
5550         
5551         
5552         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5553         
5554     },
5555     
5556     initEvents :function ()
5557     {
5558         //Roo.log(this.el.select('.navbar-toggle',true));
5559         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5560         
5561         var mark = {
5562             tag: "div",
5563             cls:"x-dlg-mask"
5564         };
5565         
5566         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567         
5568         var size = this.el.getSize();
5569         this.maskEl.setSize(size.width, size.height);
5570         this.maskEl.enableDisplayMode("block");
5571         this.maskEl.hide();
5572         
5573         if(this.loadMask){
5574             this.maskEl.show();
5575         }
5576     },
5577     
5578     
5579     getChildContainer : function()
5580     {
5581         if (this.el && this.el.select('.collapse').getCount()) {
5582             return this.el.select('.collapse',true).first();
5583         }
5584         
5585         return this.el;
5586     },
5587     
5588     mask : function()
5589     {
5590         this.maskEl.show();
5591     },
5592     
5593     unmask : function()
5594     {
5595         this.maskEl.hide();
5596     },
5597     onToggle : function()
5598     {
5599         
5600         if(this.fireEvent('beforetoggle', this) === false){
5601             return;
5602         }
5603         var ce = this.el.select('.navbar-collapse',true).first();
5604       
5605         if (!ce.hasClass('show')) {
5606            this.expand();
5607         } else {
5608             this.collapse();
5609         }
5610         
5611         
5612     
5613     },
5614     /**
5615      * Expand the navbar pulldown 
5616      */
5617     expand : function ()
5618     {
5619        
5620         var ce = this.el.select('.navbar-collapse',true).first();
5621         if (ce.hasClass('collapsing')) {
5622             return;
5623         }
5624         ce.dom.style.height = '';
5625                // show it...
5626         ce.addClass('in'); // old...
5627         ce.removeClass('collapse');
5628         ce.addClass('show');
5629         var h = ce.getHeight();
5630         Roo.log(h);
5631         ce.removeClass('show');
5632         // at this point we should be able to see it..
5633         ce.addClass('collapsing');
5634         
5635         ce.setHeight(0); // resize it ...
5636         ce.on('transitionend', function() {
5637             //Roo.log('done transition');
5638             ce.removeClass('collapsing');
5639             ce.addClass('show');
5640             ce.removeClass('collapse');
5641
5642             ce.dom.style.height = '';
5643         }, this, { single: true} );
5644         ce.setHeight(h);
5645         ce.dom.scrollTop = 0;
5646     },
5647     /**
5648      * Collapse the navbar pulldown 
5649      */
5650     collapse : function()
5651     {
5652          var ce = this.el.select('.navbar-collapse',true).first();
5653        
5654         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5655             // it's collapsed or collapsing..
5656             return;
5657         }
5658         ce.removeClass('in'); // old...
5659         ce.setHeight(ce.getHeight());
5660         ce.removeClass('show');
5661         ce.addClass('collapsing');
5662         
5663         ce.on('transitionend', function() {
5664             ce.dom.style.height = '';
5665             ce.removeClass('collapsing');
5666             ce.addClass('collapse');
5667         }, this, { single: true} );
5668         ce.setHeight(0);
5669     }
5670     
5671     
5672     
5673 });
5674
5675
5676
5677  
5678
5679  /*
5680  * - LGPL
5681  *
5682  * navbar
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.NavSimplebar
5688  * @extends Roo.bootstrap.Navbar
5689  * Bootstrap Sidebar class
5690  *
5691  * @cfg {Boolean} inverse is inverted color
5692  * 
5693  * @cfg {String} type (nav | pills | tabs)
5694  * @cfg {Boolean} arrangement stacked | justified
5695  * @cfg {String} align (left | right) alignment
5696  * 
5697  * @cfg {Boolean} main (true|false) main nav bar? default false
5698  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699  * 
5700  * @cfg {String} tag (header|footer|nav|div) default is nav 
5701
5702  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5703  * 
5704  * 
5705  * @constructor
5706  * Create a new Sidebar
5707  * @param {Object} config The config object
5708  */
5709
5710
5711 Roo.bootstrap.NavSimplebar = function(config){
5712     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5713 };
5714
5715 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5716     
5717     inverse: false,
5718     
5719     type: false,
5720     arrangement: '',
5721     align : false,
5722     
5723     weight : 'light',
5724     
5725     main : false,
5726     
5727     
5728     tag : false,
5729     
5730     
5731     getAutoCreate : function(){
5732         
5733         
5734         var cfg = {
5735             tag : this.tag || 'div',
5736             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737         };
5738         if (['light','white'].indexOf(this.weight) > -1) {
5739             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740         }
5741         cfg.cls += ' bg-' + this.weight;
5742         
5743         if (this.inverse) {
5744             cfg.cls += ' navbar-inverse';
5745             
5746         }
5747         
5748         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749         
5750         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5751             return cfg;
5752         }
5753         
5754         
5755     
5756         
5757         cfg.cn = [
5758             {
5759                 cls: 'nav nav-' + this.xtype,
5760                 tag : 'ul'
5761             }
5762         ];
5763         
5764          
5765         this.type = this.type || 'nav';
5766         if (['tabs','pills'].indexOf(this.type) != -1) {
5767             cfg.cn[0].cls += ' nav-' + this.type
5768         
5769         
5770         } else {
5771             if (this.type!=='nav') {
5772                 Roo.log('nav type must be nav/tabs/pills')
5773             }
5774             cfg.cn[0].cls += ' navbar-nav'
5775         }
5776         
5777         
5778         
5779         
5780         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5781             cfg.cn[0].cls += ' nav-' + this.arrangement;
5782         }
5783         
5784         
5785         if (this.align === 'right') {
5786             cfg.cn[0].cls += ' navbar-right';
5787         }
5788         
5789         
5790         
5791         
5792         return cfg;
5793     
5794         
5795     }
5796     
5797     
5798     
5799 });
5800
5801
5802
5803  
5804
5805  
5806        /*
5807  * - LGPL
5808  *
5809  * navbar
5810  * navbar-fixed-top
5811  * navbar-expand-md  fixed-top 
5812  */
5813
5814 /**
5815  * @class Roo.bootstrap.NavHeaderbar
5816  * @extends Roo.bootstrap.NavSimplebar
5817  * Bootstrap Sidebar class
5818  *
5819  * @cfg {String} brand what is brand
5820  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5821  * @cfg {String} brand_href href of the brand
5822  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5823  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5824  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5825  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5826  * 
5827  * @constructor
5828  * Create a new Sidebar
5829  * @param {Object} config The config object
5830  */
5831
5832
5833 Roo.bootstrap.NavHeaderbar = function(config){
5834     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5835       
5836 };
5837
5838 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5839     
5840     position: '',
5841     brand: '',
5842     brand_href: false,
5843     srButton : true,
5844     autohide : false,
5845     desktopCenter : false,
5846    
5847     
5848     getAutoCreate : function(){
5849         
5850         var   cfg = {
5851             tag: this.nav || 'nav',
5852             cls: 'navbar navbar-expand-md',
5853             role: 'navigation',
5854             cn: []
5855         };
5856         
5857         var cn = cfg.cn;
5858         if (this.desktopCenter) {
5859             cn.push({cls : 'container', cn : []});
5860             cn = cn[0].cn;
5861         }
5862         
5863         if(this.srButton){
5864             var btn = {
5865                 tag: 'button',
5866                 type: 'button',
5867                 cls: 'navbar-toggle navbar-toggler',
5868                 'data-toggle': 'collapse',
5869                 cn: [
5870                     {
5871                         tag: 'span',
5872                         cls: 'sr-only',
5873                         html: 'Toggle navigation'
5874                     },
5875                     {
5876                         tag: 'span',
5877                         cls: 'icon-bar navbar-toggler-icon'
5878                     },
5879                     {
5880                         tag: 'span',
5881                         cls: 'icon-bar'
5882                     },
5883                     {
5884                         tag: 'span',
5885                         cls: 'icon-bar'
5886                     }
5887                 ]
5888             };
5889             
5890             cn.push( Roo.bootstrap.version == 4 ? btn : {
5891                 tag: 'div',
5892                 cls: 'navbar-header',
5893                 cn: [
5894                     btn
5895                 ]
5896             });
5897         }
5898         
5899         cn.push({
5900             tag: 'div',
5901             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5902             cn : []
5903         });
5904         
5905         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906         
5907         if (['light','white'].indexOf(this.weight) > -1) {
5908             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909         }
5910         cfg.cls += ' bg-' + this.weight;
5911         
5912         
5913         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5914             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915             
5916             // tag can override this..
5917             
5918             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5919         }
5920         
5921         if (this.brand !== '') {
5922             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5923             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924                 tag: 'a',
5925                 href: this.brand_href ? this.brand_href : '#',
5926                 cls: 'navbar-brand',
5927                 cn: [
5928                 this.brand
5929                 ]
5930             });
5931         }
5932         
5933         if(this.main){
5934             cfg.cls += ' main-nav';
5935         }
5936         
5937         
5938         return cfg;
5939
5940         
5941     },
5942     getHeaderChildContainer : function()
5943     {
5944         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5945             return this.el.select('.navbar-header',true).first();
5946         }
5947         
5948         return this.getChildContainer();
5949     },
5950     
5951     getChildContainer : function()
5952     {
5953          
5954         return this.el.select('.roo-navbar-collapse',true).first();
5955          
5956         
5957     },
5958     
5959     initEvents : function()
5960     {
5961         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962         
5963         if (this.autohide) {
5964             
5965             var prevScroll = 0;
5966             var ft = this.el;
5967             
5968             Roo.get(document).on('scroll',function(e) {
5969                 var ns = Roo.get(document).getScroll().top;
5970                 var os = prevScroll;
5971                 prevScroll = ns;
5972                 
5973                 if(ns > os){
5974                     ft.removeClass('slideDown');
5975                     ft.addClass('slideUp');
5976                     return;
5977                 }
5978                 ft.removeClass('slideUp');
5979                 ft.addClass('slideDown');
5980                  
5981               
5982           },this);
5983         }
5984     }    
5985     
5986 });
5987
5988
5989
5990  
5991
5992  /*
5993  * - LGPL
5994  *
5995  * navbar
5996  * 
5997  */
5998
5999 /**
6000  * @class Roo.bootstrap.NavSidebar
6001  * @extends Roo.bootstrap.Navbar
6002  * Bootstrap Sidebar class
6003  * 
6004  * @constructor
6005  * Create a new Sidebar
6006  * @param {Object} config The config object
6007  */
6008
6009
6010 Roo.bootstrap.NavSidebar = function(config){
6011     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6012 };
6013
6014 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6015     
6016     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017     
6018     getAutoCreate : function(){
6019         
6020         
6021         return  {
6022             tag: 'div',
6023             cls: 'sidebar sidebar-nav'
6024         };
6025     
6026         
6027     }
6028     
6029     
6030     
6031 });
6032
6033
6034
6035  
6036
6037  /*
6038  * - LGPL
6039  *
6040  * nav group
6041  * 
6042  */
6043
6044 /**
6045  * @class Roo.bootstrap.NavGroup
6046  * @extends Roo.bootstrap.Component
6047  * Bootstrap NavGroup class
6048  * @cfg {String} align (left|right)
6049  * @cfg {Boolean} inverse
6050  * @cfg {String} type (nav|pills|tab) default nav
6051  * @cfg {String} navId - reference Id for navbar.
6052  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6053  * 
6054  * @constructor
6055  * Create a new nav group
6056  * @param {Object} config The config object
6057  */
6058
6059 Roo.bootstrap.NavGroup = function(config){
6060     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6061     this.navItems = [];
6062    
6063     Roo.bootstrap.NavGroup.register(this);
6064      this.addEvents({
6065         /**
6066              * @event changed
6067              * Fires when the active item changes
6068              * @param {Roo.bootstrap.NavGroup} this
6069              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6070              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6071          */
6072         'changed': true
6073      });
6074     
6075 };
6076
6077 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6078     
6079     align: '',
6080     inverse: false,
6081     form: false,
6082     type: 'nav',
6083     navId : '',
6084     // private
6085     pilltype : true,
6086     
6087     navItems : false, 
6088     
6089     getAutoCreate : function()
6090     {
6091         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6092         
6093         cfg = {
6094             tag : 'ul',
6095             cls: 'nav' 
6096         };
6097         if (Roo.bootstrap.version == 4) {
6098             if (['tabs','pills'].indexOf(this.type) != -1) {
6099                 cfg.cls += ' nav-' + this.type; 
6100             } else {
6101                 // trying to remove so header bar can right align top?
6102                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6103                     // do not use on header bar... 
6104                     cfg.cls += ' navbar-nav';
6105                 }
6106             }
6107             
6108         } else {
6109             if (['tabs','pills'].indexOf(this.type) != -1) {
6110                 cfg.cls += ' nav-' + this.type
6111             } else {
6112                 if (this.type !== 'nav') {
6113                     Roo.log('nav type must be nav/tabs/pills')
6114                 }
6115                 cfg.cls += ' navbar-nav'
6116             }
6117         }
6118         
6119         if (this.parent() && this.parent().sidebar) {
6120             cfg = {
6121                 tag: 'ul',
6122                 cls: 'dashboard-menu sidebar-menu'
6123             };
6124             
6125             return cfg;
6126         }
6127         
6128         if (this.form === true) {
6129             cfg = {
6130                 tag: 'form',
6131                 cls: 'navbar-form form-inline'
6132             };
6133             //nav navbar-right ml-md-auto
6134             if (this.align === 'right') {
6135                 cfg.cls += ' navbar-right ml-md-auto';
6136             } else {
6137                 cfg.cls += ' navbar-left';
6138             }
6139         }
6140         
6141         if (this.align === 'right') {
6142             cfg.cls += ' navbar-right ml-md-auto';
6143         } else {
6144             cfg.cls += ' mr-auto';
6145         }
6146         
6147         if (this.inverse) {
6148             cfg.cls += ' navbar-inverse';
6149             
6150         }
6151         
6152         
6153         return cfg;
6154     },
6155     /**
6156     * sets the active Navigation item
6157     * @param {Roo.bootstrap.NavItem} the new current navitem
6158     */
6159     setActiveItem : function(item)
6160     {
6161         var prev = false;
6162         Roo.each(this.navItems, function(v){
6163             if (v == item) {
6164                 return ;
6165             }
6166             if (v.isActive()) {
6167                 v.setActive(false, true);
6168                 prev = v;
6169                 
6170             }
6171             
6172         });
6173
6174         item.setActive(true, true);
6175         this.fireEvent('changed', this, item, prev);
6176         
6177         
6178     },
6179     /**
6180     * gets the active Navigation item
6181     * @return {Roo.bootstrap.NavItem} the current navitem
6182     */
6183     getActive : function()
6184     {
6185         
6186         var prev = false;
6187         Roo.each(this.navItems, function(v){
6188             
6189             if (v.isActive()) {
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195         return prev;
6196     },
6197     
6198     indexOfNav : function()
6199     {
6200         
6201         var prev = false;
6202         Roo.each(this.navItems, function(v,i){
6203             
6204             if (v.isActive()) {
6205                 prev = i;
6206                 
6207             }
6208             
6209         });
6210         return prev;
6211     },
6212     /**
6213     * adds a Navigation item
6214     * @param {Roo.bootstrap.NavItem} the navitem to add
6215     */
6216     addItem : function(cfg)
6217     {
6218         if (this.form && Roo.bootstrap.version == 4) {
6219             cfg.tag = 'div';
6220         }
6221         var cn = new Roo.bootstrap.NavItem(cfg);
6222         this.register(cn);
6223         cn.parentId = this.id;
6224         cn.onRender(this.el, null);
6225         return cn;
6226     },
6227     /**
6228     * register a Navigation item
6229     * @param {Roo.bootstrap.NavItem} the navitem to add
6230     */
6231     register : function(item)
6232     {
6233         this.navItems.push( item);
6234         item.navId = this.navId;
6235     
6236     },
6237     
6238     /**
6239     * clear all the Navigation item
6240     */
6241    
6242     clearAll : function()
6243     {
6244         this.navItems = [];
6245         this.el.dom.innerHTML = '';
6246     },
6247     
6248     getNavItem: function(tabId)
6249     {
6250         var ret = false;
6251         Roo.each(this.navItems, function(e) {
6252             if (e.tabId == tabId) {
6253                ret =  e;
6254                return false;
6255             }
6256             return true;
6257             
6258         });
6259         return ret;
6260     },
6261     
6262     setActiveNext : function()
6263     {
6264         var i = this.indexOfNav(this.getActive());
6265         if (i > this.navItems.length) {
6266             return;
6267         }
6268         this.setActiveItem(this.navItems[i+1]);
6269     },
6270     setActivePrev : function()
6271     {
6272         var i = this.indexOfNav(this.getActive());
6273         if (i  < 1) {
6274             return;
6275         }
6276         this.setActiveItem(this.navItems[i-1]);
6277     },
6278     clearWasActive : function(except) {
6279         Roo.each(this.navItems, function(e) {
6280             if (e.tabId != except.tabId && e.was_active) {
6281                e.was_active = false;
6282                return false;
6283             }
6284             return true;
6285             
6286         });
6287     },
6288     getWasActive : function ()
6289     {
6290         var r = false;
6291         Roo.each(this.navItems, function(e) {
6292             if (e.was_active) {
6293                r = e;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299         return r;
6300     }
6301     
6302     
6303 });
6304
6305  
6306 Roo.apply(Roo.bootstrap.NavGroup, {
6307     
6308     groups: {},
6309      /**
6310     * register a Navigation Group
6311     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312     */
6313     register : function(navgrp)
6314     {
6315         this.groups[navgrp.navId] = navgrp;
6316         
6317     },
6318     /**
6319     * fetch a Navigation Group based on the navigation ID
6320     * @param {string} the navgroup to add
6321     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6322     */
6323     get: function(navId) {
6324         if (typeof(this.groups[navId]) == 'undefined') {
6325             return false;
6326             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327         }
6328         return this.groups[navId] ;
6329     }
6330     
6331     
6332     
6333 });
6334
6335  /*
6336  * - LGPL
6337  *
6338  * row
6339  * 
6340  */
6341
6342 /**
6343  * @class Roo.bootstrap.NavItem
6344  * @extends Roo.bootstrap.Component
6345  * Bootstrap Navbar.NavItem class
6346  * @cfg {String} href  link to
6347  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6348  * @cfg {Boolean} button_outline show and outlined button
6349  * @cfg {String} html content of button
6350  * @cfg {String} badge text inside badge
6351  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6352  * @cfg {String} glyphicon DEPRICATED - use fa
6353  * @cfg {String} icon DEPRICATED - use fa
6354  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6355  * @cfg {Boolean} active Is item active
6356  * @cfg {Boolean} disabled Is item disabled
6357  * @cfg {String} linkcls  Link Class
6358  * @cfg {Boolean} preventDefault (true | false) default false
6359  * @cfg {String} tabId the tab that this item activates.
6360  * @cfg {String} tagtype (a|span) render as a href or span?
6361  * @cfg {Boolean} animateRef (true|false) link to element default false  
6362   
6363  * @constructor
6364  * Create a new Navbar Item
6365  * @param {Object} config The config object
6366  */
6367 Roo.bootstrap.NavItem = function(config){
6368     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6369     this.addEvents({
6370         // raw events
6371         /**
6372          * @event click
6373          * The raw click event for the entire grid.
6374          * @param {Roo.EventObject} e
6375          */
6376         "click" : true,
6377          /**
6378             * @event changed
6379             * Fires when the active item active state changes
6380             * @param {Roo.bootstrap.NavItem} this
6381             * @param {boolean} state the new state
6382              
6383          */
6384         'changed': true,
6385         /**
6386             * @event scrollto
6387             * Fires when scroll to element
6388             * @param {Roo.bootstrap.NavItem} this
6389             * @param {Object} options
6390             * @param {Roo.EventObject} e
6391              
6392          */
6393         'scrollto': true
6394     });
6395    
6396 };
6397
6398 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6399     
6400     href: false,
6401     html: '',
6402     badge: '',
6403     icon: false,
6404     fa : false,
6405     glyphicon: false,
6406     active: false,
6407     preventDefault : false,
6408     tabId : false,
6409     tagtype : 'a',
6410     tag: 'li',
6411     disabled : false,
6412     animateRef : false,
6413     was_active : false,
6414     button_weight : '',
6415     button_outline : false,
6416     linkcls : '',
6417     navLink: false,
6418     
6419     getAutoCreate : function(){
6420          
6421         var cfg = {
6422             tag: this.tag,
6423             cls: 'nav-item'
6424         };
6425         
6426         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6427         
6428         if (this.active) {
6429             cfg.cls +=  ' active' ;
6430         }
6431         if (this.disabled) {
6432             cfg.cls += ' disabled';
6433         }
6434         
6435         // BS4 only?
6436         if (this.button_weight.length) {
6437             cfg.tag = this.href ? 'a' : 'button';
6438             cfg.html = this.html || '';
6439             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440             if (this.href) {
6441                 cfg.href = this.href;
6442             }
6443             if (this.fa) {
6444                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445             } else {
6446                 cfg.cls += " nav-html";
6447             }
6448             
6449             // menu .. should add dropdown-menu class - so no need for carat..
6450             
6451             if (this.badge !== '') {
6452                  
6453                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6454             }
6455             return cfg;
6456         }
6457         
6458         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6459             cfg.cn = [
6460                 {
6461                     tag: this.tagtype,
6462                     href : this.href || "#",
6463                     html: this.html || '',
6464                     cls : ''
6465                 }
6466             ];
6467             if (this.tagtype == 'a') {
6468                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6469         
6470             }
6471             if (this.icon) {
6472                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6473             } else  if (this.fa) {
6474                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6475             } else if(this.glyphicon) {
6476                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6477             } else {
6478                 cfg.cn[0].cls += " nav-html";
6479             }
6480             
6481             if (this.menu) {
6482                 cfg.cn[0].html += " <span class='caret'></span>";
6483              
6484             }
6485             
6486             if (this.badge !== '') {
6487                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6488             }
6489         }
6490         
6491         
6492         
6493         return cfg;
6494     },
6495     onRender : function(ct, position)
6496     {
6497        // Roo.log("Call onRender: " + this.xtype);
6498         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6499             this.tag = 'div';
6500         }
6501         
6502         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6503         this.navLink = this.el.select('.nav-link',true).first();
6504         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6505         return ret;
6506     },
6507       
6508     
6509     initEvents: function() 
6510     {
6511         if (typeof (this.menu) != 'undefined') {
6512             this.menu.parentType = this.xtype;
6513             this.menu.triggerEl = this.el;
6514             this.menu = this.addxtype(Roo.apply({}, this.menu));
6515         }
6516         
6517         this.el.on('click', this.onClick, this);
6518         
6519         //if(this.tagtype == 'span'){
6520         //    this.el.select('span',true).on('click', this.onClick, this);
6521         //}
6522        
6523         // at this point parent should be available..
6524         this.parent().register(this);
6525     },
6526     
6527     onClick : function(e)
6528     {
6529         if (e.getTarget('.dropdown-menu-item')) {
6530             // did you click on a menu itemm.... - then don't trigger onclick..
6531             return;
6532         }
6533         
6534         if(
6535                 this.preventDefault || 
6536                 this.href == '#' 
6537         ){
6538             Roo.log("NavItem - prevent Default?");
6539             e.preventDefault();
6540         }
6541         
6542         if (this.disabled) {
6543             return;
6544         }
6545         
6546         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6547         if (tg && tg.transition) {
6548             Roo.log("waiting for the transitionend");
6549             return;
6550         }
6551         
6552         
6553         
6554         //Roo.log("fire event clicked");
6555         if(this.fireEvent('click', this, e) === false){
6556             return;
6557         };
6558         
6559         if(this.tagtype == 'span'){
6560             return;
6561         }
6562         
6563         //Roo.log(this.href);
6564         var ael = this.el.select('a',true).first();
6565         //Roo.log(ael);
6566         
6567         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6568             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6569             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6570                 return; // ignore... - it's a 'hash' to another page.
6571             }
6572             Roo.log("NavItem - prevent Default?");
6573             e.preventDefault();
6574             this.scrollToElement(e);
6575         }
6576         
6577         
6578         var p =  this.parent();
6579    
6580         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6581             if (typeof(p.setActiveItem) !== 'undefined') {
6582                 p.setActiveItem(this);
6583             }
6584         }
6585         
6586         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6587         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6588             // remove the collapsed menu expand...
6589             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6590         }
6591     },
6592     
6593     isActive: function () {
6594         return this.active
6595     },
6596     setActive : function(state, fire, is_was_active)
6597     {
6598         if (this.active && !state && this.navId) {
6599             this.was_active = true;
6600             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601             if (nv) {
6602                 nv.clearWasActive(this);
6603             }
6604             
6605         }
6606         this.active = state;
6607         
6608         if (!state ) {
6609             this.el.removeClass('active');
6610             this.navLink ? this.navLink.removeClass('active') : false;
6611         } else if (!this.el.hasClass('active')) {
6612             
6613             this.el.addClass('active');
6614             if (Roo.bootstrap.version == 4 && this.navLink ) {
6615                 this.navLink.addClass('active');
6616             }
6617             
6618         }
6619         if (fire) {
6620             this.fireEvent('changed', this, state);
6621         }
6622         
6623         // show a panel if it's registered and related..
6624         
6625         if (!this.navId || !this.tabId || !state || is_was_active) {
6626             return;
6627         }
6628         
6629         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6630         if (!tg) {
6631             return;
6632         }
6633         var pan = tg.getPanelByName(this.tabId);
6634         if (!pan) {
6635             return;
6636         }
6637         // if we can not flip to new panel - go back to old nav highlight..
6638         if (false == tg.showPanel(pan)) {
6639             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640             if (nv) {
6641                 var onav = nv.getWasActive();
6642                 if (onav) {
6643                     onav.setActive(true, false, true);
6644                 }
6645             }
6646             
6647         }
6648         
6649         
6650         
6651     },
6652      // this should not be here...
6653     setDisabled : function(state)
6654     {
6655         this.disabled = state;
6656         if (!state ) {
6657             this.el.removeClass('disabled');
6658         } else if (!this.el.hasClass('disabled')) {
6659             this.el.addClass('disabled');
6660         }
6661         
6662     },
6663     
6664     /**
6665      * Fetch the element to display the tooltip on.
6666      * @return {Roo.Element} defaults to this.el
6667      */
6668     tooltipEl : function()
6669     {
6670         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6671     },
6672     
6673     scrollToElement : function(e)
6674     {
6675         var c = document.body;
6676         
6677         /*
6678          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679          */
6680         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6681             c = document.documentElement;
6682         }
6683         
6684         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6685         
6686         if(!target){
6687             return;
6688         }
6689
6690         var o = target.calcOffsetsTo(c);
6691         
6692         var options = {
6693             target : target,
6694             value : o[1]
6695         };
6696         
6697         this.fireEvent('scrollto', this, options, e);
6698         
6699         Roo.get(c).scrollTo('top', options.value, true);
6700         
6701         return;
6702     },
6703     /**
6704      * Set the HTML (text content) of the item
6705      * @param {string} html  content for the nav item
6706      */
6707     setHtml : function(html)
6708     {
6709         this.html = html;
6710         this.htmlEl.dom.innerHTML = html;
6711         
6712     } 
6713 });
6714  
6715
6716  /*
6717  * - LGPL
6718  *
6719  * sidebar item
6720  *
6721  *  li
6722  *    <span> icon </span>
6723  *    <span> text </span>
6724  *    <span>badge </span>
6725  */
6726
6727 /**
6728  * @class Roo.bootstrap.NavSidebarItem
6729  * @extends Roo.bootstrap.NavItem
6730  * Bootstrap Navbar.NavSidebarItem class
6731  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6732  * {Boolean} open is the menu open
6733  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6734  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6735  * {String} buttonSize (sm|md|lg)the extra classes for the button
6736  * {Boolean} showArrow show arrow next to the text (default true)
6737  * @constructor
6738  * Create a new Navbar Button
6739  * @param {Object} config The config object
6740  */
6741 Roo.bootstrap.NavSidebarItem = function(config){
6742     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6743     this.addEvents({
6744         // raw events
6745         /**
6746          * @event click
6747          * The raw click event for the entire grid.
6748          * @param {Roo.EventObject} e
6749          */
6750         "click" : true,
6751          /**
6752             * @event changed
6753             * Fires when the active item active state changes
6754             * @param {Roo.bootstrap.NavSidebarItem} this
6755             * @param {boolean} state the new state
6756              
6757          */
6758         'changed': true
6759     });
6760    
6761 };
6762
6763 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6764     
6765     badgeWeight : 'default',
6766     
6767     open: false,
6768     
6769     buttonView : false,
6770     
6771     buttonWeight : 'default',
6772     
6773     buttonSize : 'md',
6774     
6775     showArrow : true,
6776     
6777     getAutoCreate : function(){
6778         
6779         
6780         var a = {
6781                 tag: 'a',
6782                 href : this.href || '#',
6783                 cls: '',
6784                 html : '',
6785                 cn : []
6786         };
6787         
6788         if(this.buttonView){
6789             a = {
6790                 tag: 'button',
6791                 href : this.href || '#',
6792                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6793                 html : this.html,
6794                 cn : []
6795             };
6796         }
6797         
6798         var cfg = {
6799             tag: 'li',
6800             cls: '',
6801             cn: [ a ]
6802         };
6803         
6804         if (this.active) {
6805             cfg.cls += ' active';
6806         }
6807         
6808         if (this.disabled) {
6809             cfg.cls += ' disabled';
6810         }
6811         if (this.open) {
6812             cfg.cls += ' open x-open';
6813         }
6814         // left icon..
6815         if (this.glyphicon || this.icon) {
6816             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6817             a.cn.push({ tag : 'i', cls : c }) ;
6818         }
6819         
6820         if(!this.buttonView){
6821             var span = {
6822                 tag: 'span',
6823                 html : this.html || ''
6824             };
6825
6826             a.cn.push(span);
6827             
6828         }
6829         
6830         if (this.badge !== '') {
6831             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6832         }
6833         
6834         if (this.menu) {
6835             
6836             if(this.showArrow){
6837                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6838             }
6839             
6840             a.cls += ' dropdown-toggle treeview' ;
6841         }
6842         
6843         return cfg;
6844     },
6845     
6846     initEvents : function()
6847     { 
6848         if (typeof (this.menu) != 'undefined') {
6849             this.menu.parentType = this.xtype;
6850             this.menu.triggerEl = this.el;
6851             this.menu = this.addxtype(Roo.apply({}, this.menu));
6852         }
6853         
6854         this.el.on('click', this.onClick, this);
6855         
6856         if(this.badge !== ''){
6857             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6858         }
6859         
6860     },
6861     
6862     onClick : function(e)
6863     {
6864         if(this.disabled){
6865             e.preventDefault();
6866             return;
6867         }
6868         
6869         if(this.preventDefault){
6870             e.preventDefault();
6871         }
6872         
6873         this.fireEvent('click', this, e);
6874     },
6875     
6876     disable : function()
6877     {
6878         this.setDisabled(true);
6879     },
6880     
6881     enable : function()
6882     {
6883         this.setDisabled(false);
6884     },
6885     
6886     setDisabled : function(state)
6887     {
6888         if(this.disabled == state){
6889             return;
6890         }
6891         
6892         this.disabled = state;
6893         
6894         if (state) {
6895             this.el.addClass('disabled');
6896             return;
6897         }
6898         
6899         this.el.removeClass('disabled');
6900         
6901         return;
6902     },
6903     
6904     setActive : function(state)
6905     {
6906         if(this.active == state){
6907             return;
6908         }
6909         
6910         this.active = state;
6911         
6912         if (state) {
6913             this.el.addClass('active');
6914             return;
6915         }
6916         
6917         this.el.removeClass('active');
6918         
6919         return;
6920     },
6921     
6922     isActive: function () 
6923     {
6924         return this.active;
6925     },
6926     
6927     setBadge : function(str)
6928     {
6929         if(!this.badgeEl){
6930             return;
6931         }
6932         
6933         this.badgeEl.dom.innerHTML = str;
6934     }
6935     
6936    
6937      
6938  
6939 });
6940  
6941
6942  /*
6943  * - LGPL
6944  *
6945  *  Breadcrumb Nav
6946  * 
6947  */
6948 Roo.namespace('Roo.bootstrap.breadcrumb');
6949
6950
6951 /**
6952  * @class Roo.bootstrap.breadcrumb.Nav
6953  * @extends Roo.bootstrap.Component
6954  * Bootstrap Breadcrumb Nav Class
6955  *  
6956  * @children Roo.bootstrap.breadcrumb.Item
6957  * 
6958  * @constructor
6959  * Create a new breadcrumb.Nav
6960  * @param {Object} config The config object
6961  */
6962
6963
6964 Roo.bootstrap.breadcrumb.Nav = function(config){
6965     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6966     
6967     
6968 };
6969
6970 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6971     
6972     getAutoCreate : function()
6973     {
6974
6975         var cfg = {
6976             tag: 'nav',
6977             cn : [
6978                 {
6979                     tag : 'ol',
6980                     cls : 'breadcrumb'
6981                 }
6982             ]
6983             
6984         };
6985           
6986         return cfg;
6987     },
6988     
6989     initEvents: function()
6990     {
6991         this.olEl = this.el.select('ol',true).first();    
6992     },
6993     getChildContainer : function()
6994     {
6995         return this.olEl;  
6996     }
6997     
6998 });
6999
7000  /*
7001  * - LGPL
7002  *
7003  *  Breadcrumb Item
7004  * 
7005  */
7006
7007
7008 /**
7009  * @class Roo.bootstrap.breadcrumb.Nav
7010  * @extends Roo.bootstrap.Component
7011  * Bootstrap Breadcrumb Nav Class
7012  *  
7013  * @children Roo.bootstrap.breadcrumb.Component
7014  * @cfg {String} html the content of the link.
7015  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7016  * @cfg {Boolean} active is it active
7017
7018  * 
7019  * @constructor
7020  * Create a new breadcrumb.Nav
7021  * @param {Object} config The config object
7022  */
7023
7024 Roo.bootstrap.breadcrumb.Item = function(config){
7025     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7026     this.addEvents({
7027         // img events
7028         /**
7029          * @event click
7030          * The img click event for the img.
7031          * @param {Roo.EventObject} e
7032          */
7033         "click" : true
7034     });
7035     
7036 };
7037
7038 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7039     
7040     href: false,
7041     html : '',
7042     
7043     getAutoCreate : function()
7044     {
7045
7046         var cfg = {
7047             tag: 'li',
7048             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049         };
7050         if (this.href !== false) {
7051             cfg.cn = [{
7052                 tag : 'a',
7053                 href : this.href,
7054                 html : this.html
7055             }];
7056         } else {
7057             cfg.html = this.html;
7058         }
7059         
7060         return cfg;
7061     },
7062     
7063     initEvents: function()
7064     {
7065         if (this.href) {
7066             this.el.select('a', true).first().on('click',this.onClick, this)
7067         }
7068         
7069     },
7070     onClick : function(e)
7071     {
7072         e.preventDefault();
7073         this.fireEvent('click',this,  e);
7074     }
7075     
7076 });
7077
7078  /*
7079  * - LGPL
7080  *
7081  * row
7082  * 
7083  */
7084
7085 /**
7086  * @class Roo.bootstrap.Row
7087  * @extends Roo.bootstrap.Component
7088  * Bootstrap Row class (contains columns...)
7089  * 
7090  * @constructor
7091  * Create a new Row
7092  * @param {Object} config The config object
7093  */
7094
7095 Roo.bootstrap.Row = function(config){
7096     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7097 };
7098
7099 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7100     
7101     getAutoCreate : function(){
7102        return {
7103             cls: 'row clearfix'
7104        };
7105     }
7106     
7107     
7108 });
7109
7110  
7111
7112  /*
7113  * - LGPL
7114  *
7115  * pagination
7116  * 
7117  */
7118
7119 /**
7120  * @class Roo.bootstrap.Pagination
7121  * @extends Roo.bootstrap.Component
7122  * Bootstrap Pagination class
7123  * @cfg {String} size xs | sm | md | lg
7124  * @cfg {Boolean} inverse false | true
7125  * 
7126  * @constructor
7127  * Create a new Pagination
7128  * @param {Object} config The config object
7129  */
7130
7131 Roo.bootstrap.Pagination = function(config){
7132     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7133 };
7134
7135 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7136     
7137     cls: false,
7138     size: false,
7139     inverse: false,
7140     
7141     getAutoCreate : function(){
7142         var cfg = {
7143             tag: 'ul',
7144                 cls: 'pagination'
7145         };
7146         if (this.inverse) {
7147             cfg.cls += ' inverse';
7148         }
7149         if (this.html) {
7150             cfg.html=this.html;
7151         }
7152         if (this.cls) {
7153             cfg.cls += " " + this.cls;
7154         }
7155         return cfg;
7156     }
7157    
7158 });
7159
7160  
7161
7162  /*
7163  * - LGPL
7164  *
7165  * Pagination item
7166  * 
7167  */
7168
7169
7170 /**
7171  * @class Roo.bootstrap.PaginationItem
7172  * @extends Roo.bootstrap.Component
7173  * Bootstrap PaginationItem class
7174  * @cfg {String} html text
7175  * @cfg {String} href the link
7176  * @cfg {Boolean} preventDefault (true | false) default true
7177  * @cfg {Boolean} active (true | false) default false
7178  * @cfg {Boolean} disabled default false
7179  * 
7180  * 
7181  * @constructor
7182  * Create a new PaginationItem
7183  * @param {Object} config The config object
7184  */
7185
7186
7187 Roo.bootstrap.PaginationItem = function(config){
7188     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7189     this.addEvents({
7190         // raw events
7191         /**
7192          * @event click
7193          * The raw click event for the entire grid.
7194          * @param {Roo.EventObject} e
7195          */
7196         "click" : true
7197     });
7198 };
7199
7200 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7201     
7202     href : false,
7203     html : false,
7204     preventDefault: true,
7205     active : false,
7206     cls : false,
7207     disabled: false,
7208     
7209     getAutoCreate : function(){
7210         var cfg= {
7211             tag: 'li',
7212             cn: [
7213                 {
7214                     tag : 'a',
7215                     href : this.href ? this.href : '#',
7216                     html : this.html ? this.html : ''
7217                 }
7218             ]
7219         };
7220         
7221         if(this.cls){
7222             cfg.cls = this.cls;
7223         }
7224         
7225         if(this.disabled){
7226             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7227         }
7228         
7229         if(this.active){
7230             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7231         }
7232         
7233         return cfg;
7234     },
7235     
7236     initEvents: function() {
7237         
7238         this.el.on('click', this.onClick, this);
7239         
7240     },
7241     onClick : function(e)
7242     {
7243         Roo.log('PaginationItem on click ');
7244         if(this.preventDefault){
7245             e.preventDefault();
7246         }
7247         
7248         if(this.disabled){
7249             return;
7250         }
7251         
7252         this.fireEvent('click', this, e);
7253     }
7254    
7255 });
7256
7257  
7258
7259  /*
7260  * - LGPL
7261  *
7262  * slider
7263  * 
7264  */
7265
7266
7267 /**
7268  * @class Roo.bootstrap.Slider
7269  * @extends Roo.bootstrap.Component
7270  * Bootstrap Slider class
7271  *    
7272  * @constructor
7273  * Create a new Slider
7274  * @param {Object} config The config object
7275  */
7276
7277 Roo.bootstrap.Slider = function(config){
7278     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7279 };
7280
7281 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7282     
7283     getAutoCreate : function(){
7284         
7285         var cfg = {
7286             tag: 'div',
7287             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7288             cn: [
7289                 {
7290                     tag: 'a',
7291                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7292                 }
7293             ]
7294         };
7295         
7296         return cfg;
7297     }
7298    
7299 });
7300
7301  /*
7302  * Based on:
7303  * Ext JS Library 1.1.1
7304  * Copyright(c) 2006-2007, Ext JS, LLC.
7305  *
7306  * Originally Released Under LGPL - original licence link has changed is not relivant.
7307  *
7308  * Fork - LGPL
7309  * <script type="text/javascript">
7310  */
7311  /**
7312  * @extends Roo.dd.DDProxy
7313  * @class Roo.grid.SplitDragZone
7314  * Support for Column Header resizing
7315  * @constructor
7316  * @param {Object} config
7317  */
7318 // private
7319 // This is a support class used internally by the Grid components
7320 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7321     this.grid = grid;
7322     this.view = grid.getView();
7323     this.proxy = this.view.resizeProxy;
7324     Roo.grid.SplitDragZone.superclass.constructor.call(
7325         this,
7326         hd, // ID
7327         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7328         {  // CONFIG
7329             dragElId : Roo.id(this.proxy.dom),
7330             resizeFrame:false
7331         }
7332     );
7333     
7334     this.setHandleElId(Roo.id(hd));
7335     if (hd2 !== false) {
7336         this.setOuterHandleElId(Roo.id(hd2));
7337     }
7338     
7339     this.scroll = false;
7340 };
7341 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7342     fly: Roo.Element.fly,
7343
7344     b4StartDrag : function(x, y){
7345         this.view.headersDisabled = true;
7346         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7347                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7348         );
7349         this.proxy.setHeight(h);
7350         
7351         // for old system colWidth really stored the actual width?
7352         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7353         // which in reality did not work.. - it worked only for fixed sizes
7354         // for resizable we need to use actual sizes.
7355         var w = this.cm.getColumnWidth(this.cellIndex);
7356         if (!this.view.mainWrap) {
7357             // bootstrap.
7358             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7359         }
7360         
7361         
7362         
7363         // this was w-this.grid.minColumnWidth;
7364         // doesnt really make sense? - w = thie curren width or the rendered one?
7365         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7366         this.resetConstraints();
7367         this.setXConstraint(minw, 1000);
7368         this.setYConstraint(0, 0);
7369         this.minX = x - minw;
7370         this.maxX = x + 1000;
7371         this.startPos = x;
7372         if (!this.view.mainWrap) { // this is Bootstrap code..
7373             this.getDragEl().style.display='block';
7374         }
7375         
7376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7377     },
7378
7379
7380     handleMouseDown : function(e){
7381         ev = Roo.EventObject.setEvent(e);
7382         var t = this.fly(ev.getTarget());
7383         if(t.hasClass("x-grid-split")){
7384             this.cellIndex = this.view.getCellIndex(t.dom);
7385             this.split = t.dom;
7386             this.cm = this.grid.colModel;
7387             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7388                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7389             }
7390         }
7391     },
7392
7393     endDrag : function(e){
7394         this.view.headersDisabled = false;
7395         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7396         var diff = endX - this.startPos;
7397         // 
7398         var w = this.cm.getColumnWidth(this.cellIndex);
7399         if (!this.view.mainWrap) {
7400             w = 0;
7401         }
7402         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7403     },
7404
7405     autoOffset : function(){
7406         this.setDelta(0,0);
7407     }
7408 });/*
7409  * Based on:
7410  * Ext JS Library 1.1.1
7411  * Copyright(c) 2006-2007, Ext JS, LLC.
7412  *
7413  * Originally Released Under LGPL - original licence link has changed is not relivant.
7414  *
7415  * Fork - LGPL
7416  * <script type="text/javascript">
7417  */
7418
7419 /**
7420  * @class Roo.grid.AbstractSelectionModel
7421  * @extends Roo.util.Observable
7422  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7423  * implemented by descendant classes.  This class should not be directly instantiated.
7424  * @constructor
7425  */
7426 Roo.grid.AbstractSelectionModel = function(){
7427     this.locked = false;
7428     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7429 };
7430
7431 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7432     /** @ignore Called by the grid automatically. Do not call directly. */
7433     init : function(grid){
7434         this.grid = grid;
7435         this.initEvents();
7436     },
7437
7438     /**
7439      * Locks the selections.
7440      */
7441     lock : function(){
7442         this.locked = true;
7443     },
7444
7445     /**
7446      * Unlocks the selections.
7447      */
7448     unlock : function(){
7449         this.locked = false;
7450     },
7451
7452     /**
7453      * Returns true if the selections are locked.
7454      * @return {Boolean}
7455      */
7456     isLocked : function(){
7457         return this.locked;
7458     }
7459 });/*
7460  * Based on:
7461  * Ext JS Library 1.1.1
7462  * Copyright(c) 2006-2007, Ext JS, LLC.
7463  *
7464  * Originally Released Under LGPL - original licence link has changed is not relivant.
7465  *
7466  * Fork - LGPL
7467  * <script type="text/javascript">
7468  */
7469 /**
7470  * @extends Roo.grid.AbstractSelectionModel
7471  * @class Roo.grid.RowSelectionModel
7472  * The default SelectionModel used by {@link Roo.grid.Grid}.
7473  * It supports multiple selections and keyboard selection/navigation. 
7474  * @constructor
7475  * @param {Object} config
7476  */
7477 Roo.grid.RowSelectionModel = function(config){
7478     Roo.apply(this, config);
7479     this.selections = new Roo.util.MixedCollection(false, function(o){
7480         return o.id;
7481     });
7482
7483     this.last = false;
7484     this.lastActive = false;
7485
7486     this.addEvents({
7487         /**
7488         * @event selectionchange
7489         * Fires when the selection changes
7490         * @param {SelectionModel} this
7491         */
7492        "selectionchange" : true,
7493        /**
7494         * @event afterselectionchange
7495         * Fires after the selection changes (eg. by key press or clicking)
7496         * @param {SelectionModel} this
7497         */
7498        "afterselectionchange" : true,
7499        /**
7500         * @event beforerowselect
7501         * Fires when a row is selected being selected, return false to cancel.
7502         * @param {SelectionModel} this
7503         * @param {Number} rowIndex The selected index
7504         * @param {Boolean} keepExisting False if other selections will be cleared
7505         */
7506        "beforerowselect" : true,
7507        /**
7508         * @event rowselect
7509         * Fires when a row is selected.
7510         * @param {SelectionModel} this
7511         * @param {Number} rowIndex The selected index
7512         * @param {Roo.data.Record} r The record
7513         */
7514        "rowselect" : true,
7515        /**
7516         * @event rowdeselect
7517         * Fires when a row is deselected.
7518         * @param {SelectionModel} this
7519         * @param {Number} rowIndex The selected index
7520         */
7521         "rowdeselect" : true
7522     });
7523     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7524     this.locked = false;
7525 };
7526
7527 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7528     /**
7529      * @cfg {Boolean} singleSelect
7530      * True to allow selection of only one row at a time (defaults to false)
7531      */
7532     singleSelect : false,
7533
7534     // private
7535     initEvents : function(){
7536
7537         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7538             this.grid.on("mousedown", this.handleMouseDown, this);
7539         }else{ // allow click to work like normal
7540             this.grid.on("rowclick", this.handleDragableRowClick, this);
7541         }
7542         // bootstrap does not have a view..
7543         var view = this.grid.view ? this.grid.view : this.grid;
7544         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7545             "up" : function(e){
7546                 if(!e.shiftKey){
7547                     this.selectPrevious(e.shiftKey);
7548                 }else if(this.last !== false && this.lastActive !== false){
7549                     var last = this.last;
7550                     this.selectRange(this.last,  this.lastActive-1);
7551                     view.focusRow(this.lastActive);
7552                     if(last !== false){
7553                         this.last = last;
7554                     }
7555                 }else{
7556                     this.selectFirstRow();
7557                 }
7558                 this.fireEvent("afterselectionchange", this);
7559             },
7560             "down" : function(e){
7561                 if(!e.shiftKey){
7562                     this.selectNext(e.shiftKey);
7563                 }else if(this.last !== false && this.lastActive !== false){
7564                     var last = this.last;
7565                     this.selectRange(this.last,  this.lastActive+1);
7566                     view.focusRow(this.lastActive);
7567                     if(last !== false){
7568                         this.last = last;
7569                     }
7570                 }else{
7571                     this.selectFirstRow();
7572                 }
7573                 this.fireEvent("afterselectionchange", this);
7574             },
7575             scope: this
7576         });
7577
7578          
7579         view.on("refresh", this.onRefresh, this);
7580         view.on("rowupdated", this.onRowUpdated, this);
7581         view.on("rowremoved", this.onRemove, this);
7582     },
7583
7584     // private
7585     onRefresh : function(){
7586         var ds = this.grid.ds, i, v = this.grid.view;
7587         var s = this.selections;
7588         s.each(function(r){
7589             if((i = ds.indexOfId(r.id)) != -1){
7590                 v.onRowSelect(i);
7591                 s.add(ds.getAt(i)); // updating the selection relate data
7592             }else{
7593                 s.remove(r);
7594             }
7595         });
7596     },
7597
7598     // private
7599     onRemove : function(v, index, r){
7600         this.selections.remove(r);
7601     },
7602
7603     // private
7604     onRowUpdated : function(v, index, r){
7605         if(this.isSelected(r)){
7606             v.onRowSelect(index);
7607         }
7608     },
7609
7610     /**
7611      * Select records.
7612      * @param {Array} records The records to select
7613      * @param {Boolean} keepExisting (optional) True to keep existing selections
7614      */
7615     selectRecords : function(records, keepExisting){
7616         if(!keepExisting){
7617             this.clearSelections();
7618         }
7619         var ds = this.grid.ds;
7620         for(var i = 0, len = records.length; i < len; i++){
7621             this.selectRow(ds.indexOf(records[i]), true);
7622         }
7623     },
7624
7625     /**
7626      * Gets the number of selected rows.
7627      * @return {Number}
7628      */
7629     getCount : function(){
7630         return this.selections.length;
7631     },
7632
7633     /**
7634      * Selects the first row in the grid.
7635      */
7636     selectFirstRow : function(){
7637         this.selectRow(0);
7638     },
7639
7640     /**
7641      * Select the last row.
7642      * @param {Boolean} keepExisting (optional) True to keep existing selections
7643      */
7644     selectLastRow : function(keepExisting){
7645         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7646     },
7647
7648     /**
7649      * Selects the row immediately following the last selected row.
7650      * @param {Boolean} keepExisting (optional) True to keep existing selections
7651      */
7652     selectNext : function(keepExisting){
7653         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7654             this.selectRow(this.last+1, keepExisting);
7655             var view = this.grid.view ? this.grid.view : this.grid;
7656             view.focusRow(this.last);
7657         }
7658     },
7659
7660     /**
7661      * Selects the row that precedes the last selected row.
7662      * @param {Boolean} keepExisting (optional) True to keep existing selections
7663      */
7664     selectPrevious : function(keepExisting){
7665         if(this.last){
7666             this.selectRow(this.last-1, keepExisting);
7667             var view = this.grid.view ? this.grid.view : this.grid;
7668             view.focusRow(this.last);
7669         }
7670     },
7671
7672     /**
7673      * Returns the selected records
7674      * @return {Array} Array of selected records
7675      */
7676     getSelections : function(){
7677         return [].concat(this.selections.items);
7678     },
7679
7680     /**
7681      * Returns the first selected record.
7682      * @return {Record}
7683      */
7684     getSelected : function(){
7685         return this.selections.itemAt(0);
7686     },
7687
7688
7689     /**
7690      * Clears all selections.
7691      */
7692     clearSelections : function(fast){
7693         if(this.locked) {
7694             return;
7695         }
7696         if(fast !== true){
7697             var ds = this.grid.ds;
7698             var s = this.selections;
7699             s.each(function(r){
7700                 this.deselectRow(ds.indexOfId(r.id));
7701             }, this);
7702             s.clear();
7703         }else{
7704             this.selections.clear();
7705         }
7706         this.last = false;
7707     },
7708
7709
7710     /**
7711      * Selects all rows.
7712      */
7713     selectAll : function(){
7714         if(this.locked) {
7715             return;
7716         }
7717         this.selections.clear();
7718         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7719             this.selectRow(i, true);
7720         }
7721     },
7722
7723     /**
7724      * Returns True if there is a selection.
7725      * @return {Boolean}
7726      */
7727     hasSelection : function(){
7728         return this.selections.length > 0;
7729     },
7730
7731     /**
7732      * Returns True if the specified row is selected.
7733      * @param {Number/Record} record The record or index of the record to check
7734      * @return {Boolean}
7735      */
7736     isSelected : function(index){
7737         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7738         return (r && this.selections.key(r.id) ? true : false);
7739     },
7740
7741     /**
7742      * Returns True if the specified record id is selected.
7743      * @param {String} id The id of record to check
7744      * @return {Boolean}
7745      */
7746     isIdSelected : function(id){
7747         return (this.selections.key(id) ? true : false);
7748     },
7749
7750     // private
7751     handleMouseDown : function(e, t)
7752     {
7753         var view = this.grid.view ? this.grid.view : this.grid;
7754         var rowIndex;
7755         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7756             return;
7757         };
7758         if(e.shiftKey && this.last !== false){
7759             var last = this.last;
7760             this.selectRange(last, rowIndex, e.ctrlKey);
7761             this.last = last; // reset the last
7762             view.focusRow(rowIndex);
7763         }else{
7764             var isSelected = this.isSelected(rowIndex);
7765             if(e.button !== 0 && isSelected){
7766                 view.focusRow(rowIndex);
7767             }else if(e.ctrlKey && isSelected){
7768                 this.deselectRow(rowIndex);
7769             }else if(!isSelected){
7770                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7771                 view.focusRow(rowIndex);
7772             }
7773         }
7774         this.fireEvent("afterselectionchange", this);
7775     },
7776     // private
7777     handleDragableRowClick :  function(grid, rowIndex, e) 
7778     {
7779         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7780             this.selectRow(rowIndex, false);
7781             var view = this.grid.view ? this.grid.view : this.grid;
7782             view.focusRow(rowIndex);
7783              this.fireEvent("afterselectionchange", this);
7784         }
7785     },
7786     
7787     /**
7788      * Selects multiple rows.
7789      * @param {Array} rows Array of the indexes of the row to select
7790      * @param {Boolean} keepExisting (optional) True to keep existing selections
7791      */
7792     selectRows : function(rows, keepExisting){
7793         if(!keepExisting){
7794             this.clearSelections();
7795         }
7796         for(var i = 0, len = rows.length; i < len; i++){
7797             this.selectRow(rows[i], true);
7798         }
7799     },
7800
7801     /**
7802      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7803      * @param {Number} startRow The index of the first row in the range
7804      * @param {Number} endRow The index of the last row in the range
7805      * @param {Boolean} keepExisting (optional) True to retain existing selections
7806      */
7807     selectRange : function(startRow, endRow, keepExisting){
7808         if(this.locked) {
7809             return;
7810         }
7811         if(!keepExisting){
7812             this.clearSelections();
7813         }
7814         if(startRow <= endRow){
7815             for(var i = startRow; i <= endRow; i++){
7816                 this.selectRow(i, true);
7817             }
7818         }else{
7819             for(var i = startRow; i >= endRow; i--){
7820                 this.selectRow(i, true);
7821             }
7822         }
7823     },
7824
7825     /**
7826      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7827      * @param {Number} startRow The index of the first row in the range
7828      * @param {Number} endRow The index of the last row in the range
7829      */
7830     deselectRange : function(startRow, endRow, preventViewNotify){
7831         if(this.locked) {
7832             return;
7833         }
7834         for(var i = startRow; i <= endRow; i++){
7835             this.deselectRow(i, preventViewNotify);
7836         }
7837     },
7838
7839     /**
7840      * Selects a row.
7841      * @param {Number} row The index of the row to select
7842      * @param {Boolean} keepExisting (optional) True to keep existing selections
7843      */
7844     selectRow : function(index, keepExisting, preventViewNotify){
7845         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7846             return;
7847         }
7848         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7849             if(!keepExisting || this.singleSelect){
7850                 this.clearSelections();
7851             }
7852             var r = this.grid.ds.getAt(index);
7853             this.selections.add(r);
7854             this.last = this.lastActive = index;
7855             if(!preventViewNotify){
7856                 var view = this.grid.view ? this.grid.view : this.grid;
7857                 view.onRowSelect(index);
7858             }
7859             this.fireEvent("rowselect", this, index, r);
7860             this.fireEvent("selectionchange", this);
7861         }
7862     },
7863
7864     /**
7865      * Deselects a row.
7866      * @param {Number} row The index of the row to deselect
7867      */
7868     deselectRow : function(index, preventViewNotify){
7869         if(this.locked) {
7870             return;
7871         }
7872         if(this.last == index){
7873             this.last = false;
7874         }
7875         if(this.lastActive == index){
7876             this.lastActive = false;
7877         }
7878         var r = this.grid.ds.getAt(index);
7879         this.selections.remove(r);
7880         if(!preventViewNotify){
7881             var view = this.grid.view ? this.grid.view : this.grid;
7882             view.onRowDeselect(index);
7883         }
7884         this.fireEvent("rowdeselect", this, index);
7885         this.fireEvent("selectionchange", this);
7886     },
7887
7888     // private
7889     restoreLast : function(){
7890         if(this._last){
7891             this.last = this._last;
7892         }
7893     },
7894
7895     // private
7896     acceptsNav : function(row, col, cm){
7897         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7898     },
7899
7900     // private
7901     onEditorKey : function(field, e){
7902         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7903         if(k == e.TAB){
7904             e.stopEvent();
7905             ed.completeEdit();
7906             if(e.shiftKey){
7907                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7908             }else{
7909                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7910             }
7911         }else if(k == e.ENTER && !e.ctrlKey){
7912             e.stopEvent();
7913             ed.completeEdit();
7914             if(e.shiftKey){
7915                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7916             }else{
7917                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7918             }
7919         }else if(k == e.ESC){
7920             ed.cancelEdit();
7921         }
7922         if(newCell){
7923             g.startEditing(newCell[0], newCell[1]);
7924         }
7925     }
7926 });/*
7927  * Based on:
7928  * Ext JS Library 1.1.1
7929  * Copyright(c) 2006-2007, Ext JS, LLC.
7930  *
7931  * Originally Released Under LGPL - original licence link has changed is not relivant.
7932  *
7933  * Fork - LGPL
7934  * <script type="text/javascript">
7935  */
7936  
7937
7938 /**
7939  * @class Roo.grid.ColumnModel
7940  * @extends Roo.util.Observable
7941  * This is the default implementation of a ColumnModel used by the Grid. It defines
7942  * the columns in the grid.
7943  * <br>Usage:<br>
7944  <pre><code>
7945  var colModel = new Roo.grid.ColumnModel([
7946         {header: "Ticker", width: 60, sortable: true, locked: true},
7947         {header: "Company Name", width: 150, sortable: true},
7948         {header: "Market Cap.", width: 100, sortable: true},
7949         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7950         {header: "Employees", width: 100, sortable: true, resizable: false}
7951  ]);
7952  </code></pre>
7953  * <p>
7954  
7955  * The config options listed for this class are options which may appear in each
7956  * individual column definition.
7957  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7958  * @constructor
7959  * @param {Object} config An Array of column config objects. See this class's
7960  * config objects for details.
7961 */
7962 Roo.grid.ColumnModel = function(config){
7963         /**
7964      * The config passed into the constructor
7965      */
7966     this.config = []; //config;
7967     this.lookup = {};
7968
7969     // if no id, create one
7970     // if the column does not have a dataIndex mapping,
7971     // map it to the order it is in the config
7972     for(var i = 0, len = config.length; i < len; i++){
7973         this.addColumn(config[i]);
7974         
7975     }
7976
7977     /**
7978      * The width of columns which have no width specified (defaults to 100)
7979      * @type Number
7980      */
7981     this.defaultWidth = 100;
7982
7983     /**
7984      * Default sortable of columns which have no sortable specified (defaults to false)
7985      * @type Boolean
7986      */
7987     this.defaultSortable = false;
7988
7989     this.addEvents({
7990         /**
7991              * @event widthchange
7992              * Fires when the width of a column changes.
7993              * @param {ColumnModel} this
7994              * @param {Number} columnIndex The column index
7995              * @param {Number} newWidth The new width
7996              */
7997             "widthchange": true,
7998         /**
7999              * @event headerchange
8000              * Fires when the text of a header changes.
8001              * @param {ColumnModel} this
8002              * @param {Number} columnIndex The column index
8003              * @param {Number} newText The new header text
8004              */
8005             "headerchange": true,
8006         /**
8007              * @event hiddenchange
8008              * Fires when a column is hidden or "unhidden".
8009              * @param {ColumnModel} this
8010              * @param {Number} columnIndex The column index
8011              * @param {Boolean} hidden true if hidden, false otherwise
8012              */
8013             "hiddenchange": true,
8014             /**
8015          * @event columnmoved
8016          * Fires when a column is moved.
8017          * @param {ColumnModel} this
8018          * @param {Number} oldIndex
8019          * @param {Number} newIndex
8020          */
8021         "columnmoved" : true,
8022         /**
8023          * @event columlockchange
8024          * Fires when a column's locked state is changed
8025          * @param {ColumnModel} this
8026          * @param {Number} colIndex
8027          * @param {Boolean} locked true if locked
8028          */
8029         "columnlockchange" : true
8030     });
8031     Roo.grid.ColumnModel.superclass.constructor.call(this);
8032 };
8033 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8034     /**
8035      * @cfg {String} header The header text to display in the Grid view.
8036      */
8037         /**
8038      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8039      */
8040         /**
8041      * @cfg {String} smHeader Header at Bootsrap Small width
8042      */
8043         /**
8044      * @cfg {String} mdHeader Header at Bootsrap Medium width
8045      */
8046         /**
8047      * @cfg {String} lgHeader Header at Bootsrap Large width
8048      */
8049         /**
8050      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8051      */
8052     /**
8053      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8054      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8055      * specified, the column's index is used as an index into the Record's data Array.
8056      */
8057     /**
8058      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8059      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8060      */
8061     /**
8062      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8063      * Defaults to the value of the {@link #defaultSortable} property.
8064      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8065      */
8066     /**
8067      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8068      */
8069     /**
8070      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8071      */
8072     /**
8073      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8074      */
8075     /**
8076      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8077      */
8078     /**
8079      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8080      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8081      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8082      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8083      */
8084        /**
8085      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8086      */
8087     /**
8088      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8089      */
8090     /**
8091      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8092      */
8093     /**
8094      * @cfg {String} cursor (Optional)
8095      */
8096     /**
8097      * @cfg {String} tooltip (Optional)
8098      */
8099     /**
8100      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8101      */
8102     /**
8103      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8104      */
8105     /**
8106      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8107      */
8108     /**
8109      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8110      */
8111         /**
8112      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8113      */
8114     /**
8115      * Returns the id of the column at the specified index.
8116      * @param {Number} index The column index
8117      * @return {String} the id
8118      */
8119     getColumnId : function(index){
8120         return this.config[index].id;
8121     },
8122
8123     /**
8124      * Returns the column for a specified id.
8125      * @param {String} id The column id
8126      * @return {Object} the column
8127      */
8128     getColumnById : function(id){
8129         return this.lookup[id];
8130     },
8131
8132     
8133     /**
8134      * Returns the column Object for a specified dataIndex.
8135      * @param {String} dataIndex The column dataIndex
8136      * @return {Object|Boolean} the column or false if not found
8137      */
8138     getColumnByDataIndex: function(dataIndex){
8139         var index = this.findColumnIndex(dataIndex);
8140         return index > -1 ? this.config[index] : false;
8141     },
8142     
8143     /**
8144      * Returns the index for a specified column id.
8145      * @param {String} id The column id
8146      * @return {Number} the index, or -1 if not found
8147      */
8148     getIndexById : function(id){
8149         for(var i = 0, len = this.config.length; i < len; i++){
8150             if(this.config[i].id == id){
8151                 return i;
8152             }
8153         }
8154         return -1;
8155     },
8156     
8157     /**
8158      * Returns the index for a specified column dataIndex.
8159      * @param {String} dataIndex The column dataIndex
8160      * @return {Number} the index, or -1 if not found
8161      */
8162     
8163     findColumnIndex : function(dataIndex){
8164         for(var i = 0, len = this.config.length; i < len; i++){
8165             if(this.config[i].dataIndex == dataIndex){
8166                 return i;
8167             }
8168         }
8169         return -1;
8170     },
8171     
8172     
8173     moveColumn : function(oldIndex, newIndex){
8174         var c = this.config[oldIndex];
8175         this.config.splice(oldIndex, 1);
8176         this.config.splice(newIndex, 0, c);
8177         this.dataMap = null;
8178         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8179     },
8180
8181     isLocked : function(colIndex){
8182         return this.config[colIndex].locked === true;
8183     },
8184
8185     setLocked : function(colIndex, value, suppressEvent){
8186         if(this.isLocked(colIndex) == value){
8187             return;
8188         }
8189         this.config[colIndex].locked = value;
8190         if(!suppressEvent){
8191             this.fireEvent("columnlockchange", this, colIndex, value);
8192         }
8193     },
8194
8195     getTotalLockedWidth : function(){
8196         var totalWidth = 0;
8197         for(var i = 0; i < this.config.length; i++){
8198             if(this.isLocked(i) && !this.isHidden(i)){
8199                 this.totalWidth += this.getColumnWidth(i);
8200             }
8201         }
8202         return totalWidth;
8203     },
8204
8205     getLockedCount : function(){
8206         for(var i = 0, len = this.config.length; i < len; i++){
8207             if(!this.isLocked(i)){
8208                 return i;
8209             }
8210         }
8211         
8212         return this.config.length;
8213     },
8214
8215     /**
8216      * Returns the number of columns.
8217      * @return {Number}
8218      */
8219     getColumnCount : function(visibleOnly){
8220         if(visibleOnly === true){
8221             var c = 0;
8222             for(var i = 0, len = this.config.length; i < len; i++){
8223                 if(!this.isHidden(i)){
8224                     c++;
8225                 }
8226             }
8227             return c;
8228         }
8229         return this.config.length;
8230     },
8231
8232     /**
8233      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8234      * @param {Function} fn
8235      * @param {Object} scope (optional)
8236      * @return {Array} result
8237      */
8238     getColumnsBy : function(fn, scope){
8239         var r = [];
8240         for(var i = 0, len = this.config.length; i < len; i++){
8241             var c = this.config[i];
8242             if(fn.call(scope||this, c, i) === true){
8243                 r[r.length] = c;
8244             }
8245         }
8246         return r;
8247     },
8248
8249     /**
8250      * Returns true if the specified column is sortable.
8251      * @param {Number} col The column index
8252      * @return {Boolean}
8253      */
8254     isSortable : function(col){
8255         if(typeof this.config[col].sortable == "undefined"){
8256             return this.defaultSortable;
8257         }
8258         return this.config[col].sortable;
8259     },
8260
8261     /**
8262      * Returns the rendering (formatting) function defined for the column.
8263      * @param {Number} col The column index.
8264      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8265      */
8266     getRenderer : function(col){
8267         if(!this.config[col].renderer){
8268             return Roo.grid.ColumnModel.defaultRenderer;
8269         }
8270         return this.config[col].renderer;
8271     },
8272
8273     /**
8274      * Sets the rendering (formatting) function for a column.
8275      * @param {Number} col The column index
8276      * @param {Function} fn The function to use to process the cell's raw data
8277      * to return HTML markup for the grid view. The render function is called with
8278      * the following parameters:<ul>
8279      * <li>Data value.</li>
8280      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8281      * <li>css A CSS style string to apply to the table cell.</li>
8282      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8283      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8284      * <li>Row index</li>
8285      * <li>Column index</li>
8286      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8287      */
8288     setRenderer : function(col, fn){
8289         this.config[col].renderer = fn;
8290     },
8291
8292     /**
8293      * Returns the width for the specified column.
8294      * @param {Number} col The column index
8295      * @param (optional) {String} gridSize bootstrap width size.
8296      * @return {Number}
8297      */
8298     getColumnWidth : function(col, gridSize)
8299         {
8300                 var cfg = this.config[col];
8301                 
8302                 if (typeof(gridSize) == 'undefined') {
8303                         return cfg.width * 1 || this.defaultWidth;
8304                 }
8305                 if (gridSize === false) { // if we set it..
8306                         return cfg.width || false;
8307                 }
8308                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8309                 
8310                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8311                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8312                                 continue;
8313                         }
8314                         return cfg[ sizes[i] ];
8315                 }
8316                 return 1;
8317                 
8318     },
8319
8320     /**
8321      * Sets the width for a column.
8322      * @param {Number} col The column index
8323      * @param {Number} width The new width
8324      */
8325     setColumnWidth : function(col, width, suppressEvent){
8326         this.config[col].width = width;
8327         this.totalWidth = null;
8328         if(!suppressEvent){
8329              this.fireEvent("widthchange", this, col, width);
8330         }
8331     },
8332
8333     /**
8334      * Returns the total width of all columns.
8335      * @param {Boolean} includeHidden True to include hidden column widths
8336      * @return {Number}
8337      */
8338     getTotalWidth : function(includeHidden){
8339         if(!this.totalWidth){
8340             this.totalWidth = 0;
8341             for(var i = 0, len = this.config.length; i < len; i++){
8342                 if(includeHidden || !this.isHidden(i)){
8343                     this.totalWidth += this.getColumnWidth(i);
8344                 }
8345             }
8346         }
8347         return this.totalWidth;
8348     },
8349
8350     /**
8351      * Returns the header for the specified column.
8352      * @param {Number} col The column index
8353      * @return {String}
8354      */
8355     getColumnHeader : function(col){
8356         return this.config[col].header;
8357     },
8358
8359     /**
8360      * Sets the header for a column.
8361      * @param {Number} col The column index
8362      * @param {String} header The new header
8363      */
8364     setColumnHeader : function(col, header){
8365         this.config[col].header = header;
8366         this.fireEvent("headerchange", this, col, header);
8367     },
8368
8369     /**
8370      * Returns the tooltip for the specified column.
8371      * @param {Number} col The column index
8372      * @return {String}
8373      */
8374     getColumnTooltip : function(col){
8375             return this.config[col].tooltip;
8376     },
8377     /**
8378      * Sets the tooltip for a column.
8379      * @param {Number} col The column index
8380      * @param {String} tooltip The new tooltip
8381      */
8382     setColumnTooltip : function(col, tooltip){
8383             this.config[col].tooltip = tooltip;
8384     },
8385
8386     /**
8387      * Returns the dataIndex for the specified column.
8388      * @param {Number} col The column index
8389      * @return {Number}
8390      */
8391     getDataIndex : function(col){
8392         return this.config[col].dataIndex;
8393     },
8394
8395     /**
8396      * Sets the dataIndex for a column.
8397      * @param {Number} col The column index
8398      * @param {Number} dataIndex The new dataIndex
8399      */
8400     setDataIndex : function(col, dataIndex){
8401         this.config[col].dataIndex = dataIndex;
8402     },
8403
8404     
8405     
8406     /**
8407      * Returns true if the cell is editable.
8408      * @param {Number} colIndex The column index
8409      * @param {Number} rowIndex The row index - this is nto actually used..?
8410      * @return {Boolean}
8411      */
8412     isCellEditable : function(colIndex, rowIndex){
8413         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8414     },
8415
8416     /**
8417      * Returns the editor defined for the cell/column.
8418      * return false or null to disable editing.
8419      * @param {Number} colIndex The column index
8420      * @param {Number} rowIndex The row index
8421      * @return {Object}
8422      */
8423     getCellEditor : function(colIndex, rowIndex){
8424         return this.config[colIndex].editor;
8425     },
8426
8427     /**
8428      * Sets if a column is editable.
8429      * @param {Number} col The column index
8430      * @param {Boolean} editable True if the column is editable
8431      */
8432     setEditable : function(col, editable){
8433         this.config[col].editable = editable;
8434     },
8435
8436
8437     /**
8438      * Returns true if the column is hidden.
8439      * @param {Number} colIndex The column index
8440      * @return {Boolean}
8441      */
8442     isHidden : function(colIndex){
8443         return this.config[colIndex].hidden;
8444     },
8445
8446
8447     /**
8448      * Returns true if the column width cannot be changed
8449      */
8450     isFixed : function(colIndex){
8451         return this.config[colIndex].fixed;
8452     },
8453
8454     /**
8455      * Returns true if the column can be resized
8456      * @return {Boolean}
8457      */
8458     isResizable : function(colIndex){
8459         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8460     },
8461     /**
8462      * Sets if a column is hidden.
8463      * @param {Number} colIndex The column index
8464      * @param {Boolean} hidden True if the column is hidden
8465      */
8466     setHidden : function(colIndex, hidden){
8467         this.config[colIndex].hidden = hidden;
8468         this.totalWidth = null;
8469         this.fireEvent("hiddenchange", this, colIndex, hidden);
8470     },
8471
8472     /**
8473      * Sets the editor for a column.
8474      * @param {Number} col The column index
8475      * @param {Object} editor The editor object
8476      */
8477     setEditor : function(col, editor){
8478         this.config[col].editor = editor;
8479     },
8480     /**
8481      * Add a column (experimental...) - defaults to adding to the end..
8482      * @param {Object} config 
8483     */
8484     addColumn : function(c)
8485     {
8486     
8487         var i = this.config.length;
8488         this.config[i] = c;
8489         
8490         if(typeof c.dataIndex == "undefined"){
8491             c.dataIndex = i;
8492         }
8493         if(typeof c.renderer == "string"){
8494             c.renderer = Roo.util.Format[c.renderer];
8495         }
8496         if(typeof c.id == "undefined"){
8497             c.id = Roo.id();
8498         }
8499         if(c.editor && c.editor.xtype){
8500             c.editor  = Roo.factory(c.editor, Roo.grid);
8501         }
8502         if(c.editor && c.editor.isFormField){
8503             c.editor = new Roo.grid.GridEditor(c.editor);
8504         }
8505         this.lookup[c.id] = c;
8506     }
8507     
8508 });
8509
8510 Roo.grid.ColumnModel.defaultRenderer = function(value)
8511 {
8512     if(typeof value == "object") {
8513         return value;
8514     }
8515         if(typeof value == "string" && value.length < 1){
8516             return "&#160;";
8517         }
8518     
8519         return String.format("{0}", value);
8520 };
8521
8522 // Alias for backwards compatibility
8523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8524 /*
8525  * Based on:
8526  * Ext JS Library 1.1.1
8527  * Copyright(c) 2006-2007, Ext JS, LLC.
8528  *
8529  * Originally Released Under LGPL - original licence link has changed is not relivant.
8530  *
8531  * Fork - LGPL
8532  * <script type="text/javascript">
8533  */
8534  
8535 /**
8536  * @class Roo.LoadMask
8537  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8538  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8539  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8540  * element's UpdateManager load indicator and will be destroyed after the initial load.
8541  * @constructor
8542  * Create a new LoadMask
8543  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8544  * @param {Object} config The config object
8545  */
8546 Roo.LoadMask = function(el, config){
8547     this.el = Roo.get(el);
8548     Roo.apply(this, config);
8549     if(this.store){
8550         this.store.on('beforeload', this.onBeforeLoad, this);
8551         this.store.on('load', this.onLoad, this);
8552         this.store.on('loadexception', this.onLoadException, this);
8553         this.removeMask = false;
8554     }else{
8555         var um = this.el.getUpdateManager();
8556         um.showLoadIndicator = false; // disable the default indicator
8557         um.on('beforeupdate', this.onBeforeLoad, this);
8558         um.on('update', this.onLoad, this);
8559         um.on('failure', this.onLoad, this);
8560         this.removeMask = true;
8561     }
8562 };
8563
8564 Roo.LoadMask.prototype = {
8565     /**
8566      * @cfg {Boolean} removeMask
8567      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8568      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8569      */
8570     removeMask : false,
8571     /**
8572      * @cfg {String} msg
8573      * The text to display in a centered loading message box (defaults to 'Loading...')
8574      */
8575     msg : 'Loading...',
8576     /**
8577      * @cfg {String} msgCls
8578      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8579      */
8580     msgCls : 'x-mask-loading',
8581
8582     /**
8583      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8584      * @type Boolean
8585      */
8586     disabled: false,
8587
8588     /**
8589      * Disables the mask to prevent it from being displayed
8590      */
8591     disable : function(){
8592        this.disabled = true;
8593     },
8594
8595     /**
8596      * Enables the mask so that it can be displayed
8597      */
8598     enable : function(){
8599         this.disabled = false;
8600     },
8601     
8602     onLoadException : function()
8603     {
8604         Roo.log(arguments);
8605         
8606         if (typeof(arguments[3]) != 'undefined') {
8607             Roo.MessageBox.alert("Error loading",arguments[3]);
8608         } 
8609         /*
8610         try {
8611             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8612                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8613             }   
8614         } catch(e) {
8615             
8616         }
8617         */
8618     
8619         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8620     },
8621     // private
8622     onLoad : function()
8623     {
8624         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8625     },
8626
8627     // private
8628     onBeforeLoad : function(){
8629         if(!this.disabled){
8630             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8631         }
8632     },
8633
8634     // private
8635     destroy : function(){
8636         if(this.store){
8637             this.store.un('beforeload', this.onBeforeLoad, this);
8638             this.store.un('load', this.onLoad, this);
8639             this.store.un('loadexception', this.onLoadException, this);
8640         }else{
8641             var um = this.el.getUpdateManager();
8642             um.un('beforeupdate', this.onBeforeLoad, this);
8643             um.un('update', this.onLoad, this);
8644             um.un('failure', this.onLoad, this);
8645         }
8646     }
8647 };/**
8648  * @class Roo.bootstrap.Table
8649  * @licence LGBL
8650  * @extends Roo.bootstrap.Component
8651  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8652  * Similar to Roo.grid.Grid
8653  * <pre><code>
8654  var table = Roo.factory({
8655     xtype : 'Table',
8656     xns : Roo.bootstrap,
8657     autoSizeColumns: true,
8658     
8659     
8660     store : {
8661         xtype : 'Store',
8662         xns : Roo.data,
8663         remoteSort : true,
8664         sortInfo : { direction : 'ASC', field: 'name' },
8665         proxy : {
8666            xtype : 'HttpProxy',
8667            xns : Roo.data,
8668            method : 'GET',
8669            url : 'https://example.com/some.data.url.json'
8670         },
8671         reader : {
8672            xtype : 'JsonReader',
8673            xns : Roo.data,
8674            fields : [ 'id', 'name', whatever' ],
8675            id : 'id',
8676            root : 'data'
8677         }
8678     },
8679     cm : [
8680         {
8681             xtype : 'ColumnModel',
8682             xns : Roo.grid,
8683             align : 'center',
8684             cursor : 'pointer',
8685             dataIndex : 'is_in_group',
8686             header : "Name",
8687             sortable : true,
8688             renderer : function(v, x , r) {  
8689             
8690                 return String.format("{0}", v)
8691             }
8692             width : 3
8693         } // more columns..
8694     ],
8695     selModel : {
8696         xtype : 'RowSelectionModel',
8697         xns : Roo.bootstrap.Table
8698         // you can add listeners to catch selection change here....
8699     }
8700      
8701
8702  });
8703  // set any options
8704  grid.render(Roo.get("some-div"));
8705 </code></pre>
8706
8707 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8708
8709
8710
8711  *
8712  * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8713  * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8714  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8715  * 
8716  * @cfg {String} cls table class
8717  *
8718  * 
8719  * @cfg {boolean} striped Should the rows be alternative striped
8720  * @cfg {boolean} bordered Add borders to the table
8721  * @cfg {boolean} hover Add hover highlighting
8722  * @cfg {boolean} condensed Format condensed
8723  * @cfg {boolean} responsive Format condensed
8724  * @cfg {Boolean} loadMask (true|false) default false
8725  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8726  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8727  * @cfg {Boolean} rowSelection (true|false) default false
8728  * @cfg {Boolean} cellSelection (true|false) default false
8729  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8730  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8731  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8732  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8733  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8734  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8735  * 
8736  * @constructor
8737  * Create a new Table
8738  * @param {Object} config The config object
8739  */
8740
8741 Roo.bootstrap.Table = function(config)
8742 {
8743     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8744      
8745     // BC...
8746     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8747     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8748     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8749     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8750     
8751     this.view = this; // compat with grid.
8752     
8753     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8754     if (this.sm) {
8755         this.sm.grid = this;
8756         this.selModel = Roo.factory(this.sm, Roo.grid);
8757         this.sm = this.selModel;
8758         this.sm.xmodule = this.xmodule || false;
8759     }
8760     
8761     if (this.cm && typeof(this.cm.config) == 'undefined') {
8762         this.colModel = new Roo.grid.ColumnModel(this.cm);
8763         this.cm = this.colModel;
8764         this.cm.xmodule = this.xmodule || false;
8765     }
8766     if (this.store) {
8767         this.store= Roo.factory(this.store, Roo.data);
8768         this.ds = this.store;
8769         this.ds.xmodule = this.xmodule || false;
8770          
8771     }
8772     if (this.footer && this.store) {
8773         this.footer.dataSource = this.ds;
8774         this.footer = Roo.factory(this.footer);
8775     }
8776     
8777     /** @private */
8778     this.addEvents({
8779         /**
8780          * @event cellclick
8781          * Fires when a cell is clicked
8782          * @param {Roo.bootstrap.Table} this
8783          * @param {Roo.Element} el
8784          * @param {Number} rowIndex
8785          * @param {Number} columnIndex
8786          * @param {Roo.EventObject} e
8787          */
8788         "cellclick" : true,
8789         /**
8790          * @event celldblclick
8791          * Fires when a cell is double clicked
8792          * @param {Roo.bootstrap.Table} this
8793          * @param {Roo.Element} el
8794          * @param {Number} rowIndex
8795          * @param {Number} columnIndex
8796          * @param {Roo.EventObject} e
8797          */
8798         "celldblclick" : true,
8799         /**
8800          * @event rowclick
8801          * Fires when a row is clicked
8802          * @param {Roo.bootstrap.Table} this
8803          * @param {Roo.Element} el
8804          * @param {Number} rowIndex
8805          * @param {Roo.EventObject} e
8806          */
8807         "rowclick" : true,
8808         /**
8809          * @event rowdblclick
8810          * Fires when a row is double clicked
8811          * @param {Roo.bootstrap.Table} this
8812          * @param {Roo.Element} el
8813          * @param {Number} rowIndex
8814          * @param {Roo.EventObject} e
8815          */
8816         "rowdblclick" : true,
8817         /**
8818          * @event mouseover
8819          * Fires when a mouseover occur
8820          * @param {Roo.bootstrap.Table} this
8821          * @param {Roo.Element} el
8822          * @param {Number} rowIndex
8823          * @param {Number} columnIndex
8824          * @param {Roo.EventObject} e
8825          */
8826         "mouseover" : true,
8827         /**
8828          * @event mouseout
8829          * Fires when a mouseout occur
8830          * @param {Roo.bootstrap.Table} this
8831          * @param {Roo.Element} el
8832          * @param {Number} rowIndex
8833          * @param {Number} columnIndex
8834          * @param {Roo.EventObject} e
8835          */
8836         "mouseout" : true,
8837         /**
8838          * @event rowclass
8839          * Fires when a row is rendered, so you can change add a style to it.
8840          * @param {Roo.bootstrap.Table} this
8841          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8842          */
8843         'rowclass' : true,
8844           /**
8845          * @event rowsrendered
8846          * Fires when all the  rows have been rendered
8847          * @param {Roo.bootstrap.Table} this
8848          */
8849         'rowsrendered' : true,
8850         /**
8851          * @event contextmenu
8852          * The raw contextmenu event for the entire grid.
8853          * @param {Roo.EventObject} e
8854          */
8855         "contextmenu" : true,
8856         /**
8857          * @event rowcontextmenu
8858          * Fires when a row is right clicked
8859          * @param {Roo.bootstrap.Table} this
8860          * @param {Number} rowIndex
8861          * @param {Roo.EventObject} e
8862          */
8863         "rowcontextmenu" : true,
8864         /**
8865          * @event cellcontextmenu
8866          * Fires when a cell is right clicked
8867          * @param {Roo.bootstrap.Table} this
8868          * @param {Number} rowIndex
8869          * @param {Number} cellIndex
8870          * @param {Roo.EventObject} e
8871          */
8872          "cellcontextmenu" : true,
8873          /**
8874          * @event headercontextmenu
8875          * Fires when a header is right clicked
8876          * @param {Roo.bootstrap.Table} this
8877          * @param {Number} columnIndex
8878          * @param {Roo.EventObject} e
8879          */
8880         "headercontextmenu" : true,
8881         /**
8882          * @event mousedown
8883          * The raw mousedown event for the entire grid.
8884          * @param {Roo.EventObject} e
8885          */
8886         "mousedown" : true
8887         
8888     });
8889 };
8890
8891 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8892     
8893     cls: false,
8894     
8895     striped : false,
8896     scrollBody : false,
8897     bordered: false,
8898     hover:  false,
8899     condensed : false,
8900     responsive : false,
8901     sm : false,
8902     cm : false,
8903     store : false,
8904     loadMask : false,
8905     footerShow : true,
8906     headerShow : true,
8907     enableColumnResize: true,
8908   
8909     rowSelection : false,
8910     cellSelection : false,
8911     layout : false,
8912
8913     minColumnWidth : 50,
8914     
8915     // Roo.Element - the tbody
8916     bodyEl: false,  // <tbody> Roo.Element - thead element    
8917     headEl: false,  // <thead> Roo.Element - thead element
8918     resizeProxy : false, // proxy element for dragging?
8919
8920
8921     
8922     container: false, // used by gridpanel...
8923     
8924     lazyLoad : false,
8925     
8926     CSS : Roo.util.CSS,
8927     
8928     auto_hide_footer : false,
8929     
8930     view: false, // actually points to this..
8931     
8932     getAutoCreate : function()
8933     {
8934         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8935         
8936         cfg = {
8937             tag: 'table',
8938             cls : 'table', 
8939             cn : []
8940         };
8941         // this get's auto added by panel.Grid
8942         if (this.scrollBody) {
8943             cfg.cls += ' table-body-fixed';
8944         }    
8945         if (this.striped) {
8946             cfg.cls += ' table-striped';
8947         }
8948         
8949         if (this.hover) {
8950             cfg.cls += ' table-hover';
8951         }
8952         if (this.bordered) {
8953             cfg.cls += ' table-bordered';
8954         }
8955         if (this.condensed) {
8956             cfg.cls += ' table-condensed';
8957         }
8958         
8959         if (this.responsive) {
8960             cfg.cls += ' table-responsive';
8961         }
8962         
8963         if (this.cls) {
8964             cfg.cls+=  ' ' +this.cls;
8965         }
8966         
8967         
8968         
8969         if (this.layout) {
8970             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8971         }
8972         
8973         if(this.store || this.cm){
8974             if(this.headerShow){
8975                 cfg.cn.push(this.renderHeader());
8976             }
8977             
8978             cfg.cn.push(this.renderBody());
8979             
8980             if(this.footerShow){
8981                 cfg.cn.push(this.renderFooter());
8982             }
8983             // where does this come from?
8984             //cfg.cls+=  ' TableGrid';
8985         }
8986         
8987         return { cn : [ cfg ] };
8988     },
8989     
8990     initEvents : function()
8991     {   
8992         if(!this.store || !this.cm){
8993             return;
8994         }
8995         if (this.selModel) {
8996             this.selModel.initEvents();
8997         }
8998         
8999         
9000         //Roo.log('initEvents with ds!!!!');
9001         
9002         this.bodyEl = this.el.select('tbody', true).first();
9003         this.headEl = this.el.select('thead', true).first();
9004         this.mainFoot = this.el.select('tfoot', true).first();
9005         
9006         
9007         
9008         
9009         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9010             e.on('click', this.sort, this);
9011         }, this);
9012         
9013         
9014         // why is this done????? = it breaks dialogs??
9015         //this.parent().el.setStyle('position', 'relative');
9016         
9017         
9018         if (this.footer) {
9019             this.footer.parentId = this.id;
9020             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9021             
9022             if(this.lazyLoad){
9023                 this.el.select('tfoot tr td').first().addClass('hide');
9024             }
9025         } 
9026         
9027         if(this.loadMask) {
9028             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9029         }
9030         
9031         this.store.on('load', this.onLoad, this);
9032         this.store.on('beforeload', this.onBeforeLoad, this);
9033         this.store.on('update', this.onUpdate, this);
9034         this.store.on('add', this.onAdd, this);
9035         this.store.on("clear", this.clear, this);
9036         
9037         this.el.on("contextmenu", this.onContextMenu, this);
9038         
9039         
9040         this.cm.on("headerchange", this.onHeaderChange, this);
9041         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9042
9043  //?? does bodyEl get replaced on render?
9044         this.bodyEl.on("click", this.onClick, this);
9045         this.bodyEl.on("dblclick", this.onDblClick, this);        
9046         this.bodyEl.on('scroll', this.onBodyScroll, this);
9047
9048         // guessing mainbody will work - this relays usually caught by selmodel at present.
9049         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9050   
9051   
9052         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9053         
9054   
9055         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9056             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9057         }
9058         
9059         this.initCSS();
9060     },
9061     // Compatibility with grid - we implement all the view features at present.
9062     getView : function()
9063     {
9064         return this;
9065     },
9066     
9067     initCSS : function()
9068     {
9069         
9070         
9071         var cm = this.cm, styles = [];
9072         this.CSS.removeStyleSheet(this.id + '-cssrules');
9073         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9074         // we can honour xs/sm/md/xl  as widths...
9075         // we first have to decide what widht we are currently at...
9076         var sz = Roo.getGridSize();
9077         
9078         var total = 0;
9079         var last = -1;
9080         var cols = []; // visable cols.
9081         var total_abs = 0;
9082         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9083             var w = cm.getColumnWidth(i, false);
9084             if(cm.isHidden(i)){
9085                 cols.push( { rel : false, abs : 0 });
9086                 continue;
9087             }
9088             if (w !== false) {
9089                 cols.push( { rel : false, abs : w });
9090                 total_abs += w;
9091                 last = i; // not really..
9092                 continue;
9093             }
9094             var w = cm.getColumnWidth(i, sz);
9095             if (w > 0) {
9096                 last = i
9097             }
9098             total += w;
9099             cols.push( { rel : w, abs : false });
9100         }
9101         
9102         var avail = this.bodyEl.dom.clientWidth - total_abs;
9103         
9104         var unitWidth = Math.floor(avail / total);
9105         var rem = avail - (unitWidth * total);
9106         
9107         var hidden, width, pos = 0 , splithide , left;
9108         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9109             
9110             hidden = 'display:none;';
9111             left = '';
9112             width  = 'width:0px;';
9113             splithide = '';
9114             if(!cm.isHidden(i)){
9115                 hidden = '';
9116                 
9117                 
9118                 // we can honour xs/sm/md/xl ?
9119                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9120                 if (w===0) {
9121                     hidden = 'display:none;';
9122                 }
9123                 // width should return a small number...
9124                 if (i == last) {
9125                     w+=rem; // add the remaining with..
9126                 }
9127                 pos += w;
9128                 left = "left:" + (pos -4) + "px;";
9129                 width = "width:" + w+ "px;";
9130                 
9131             }
9132             if (this.responsive) {
9133                 width = '';
9134                 left = '';
9135                 hidden = cm.isHidden(i) ? 'display:none' : '';
9136                 splithide = 'display: none';
9137             }
9138             
9139             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9140             if (this.headEl) {
9141                 if (i == last) {
9142                     splithide = 'display:none;';
9143                 }
9144                 
9145                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9146                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9147                 );
9148             }
9149             
9150         }
9151         //Roo.log(styles.join(''));
9152         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9153         
9154     },
9155     
9156     
9157     
9158     onContextMenu : function(e, t)
9159     {
9160         this.processEvent("contextmenu", e);
9161     },
9162     
9163     processEvent : function(name, e)
9164     {
9165         if (name != 'touchstart' ) {
9166             this.fireEvent(name, e);    
9167         }
9168         
9169         var t = e.getTarget();
9170         
9171         var cell = Roo.get(t);
9172         
9173         if(!cell){
9174             return;
9175         }
9176         
9177         if(cell.findParent('tfoot', false, true)){
9178             return;
9179         }
9180         
9181         if(cell.findParent('thead', false, true)){
9182             
9183             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9184                 cell = Roo.get(t).findParent('th', false, true);
9185                 if (!cell) {
9186                     Roo.log("failed to find th in thead?");
9187                     Roo.log(e.getTarget());
9188                     return;
9189                 }
9190             }
9191             
9192             var cellIndex = cell.dom.cellIndex;
9193             
9194             var ename = name == 'touchstart' ? 'click' : name;
9195             this.fireEvent("header" + ename, this, cellIndex, e);
9196             
9197             return;
9198         }
9199         
9200         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9201             cell = Roo.get(t).findParent('td', false, true);
9202             if (!cell) {
9203                 Roo.log("failed to find th in tbody?");
9204                 Roo.log(e.getTarget());
9205                 return;
9206             }
9207         }
9208         
9209         var row = cell.findParent('tr', false, true);
9210         var cellIndex = cell.dom.cellIndex;
9211         var rowIndex = row.dom.rowIndex - 1;
9212         
9213         if(row !== false){
9214             
9215             this.fireEvent("row" + name, this, rowIndex, e);
9216             
9217             if(cell !== false){
9218             
9219                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9220             }
9221         }
9222         
9223     },
9224     
9225     onMouseover : function(e, el)
9226     {
9227         var cell = Roo.get(el);
9228         
9229         if(!cell){
9230             return;
9231         }
9232         
9233         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9234             cell = cell.findParent('td', false, true);
9235         }
9236         
9237         var row = cell.findParent('tr', false, true);
9238         var cellIndex = cell.dom.cellIndex;
9239         var rowIndex = row.dom.rowIndex - 1; // start from 0
9240         
9241         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9242         
9243     },
9244     
9245     onMouseout : function(e, el)
9246     {
9247         var cell = Roo.get(el);
9248         
9249         if(!cell){
9250             return;
9251         }
9252         
9253         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9254             cell = cell.findParent('td', false, true);
9255         }
9256         
9257         var row = cell.findParent('tr', false, true);
9258         var cellIndex = cell.dom.cellIndex;
9259         var rowIndex = row.dom.rowIndex - 1; // start from 0
9260         
9261         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9262         
9263     },
9264     
9265     onClick : function(e, el)
9266     {
9267         var cell = Roo.get(el);
9268         
9269         if(!cell || (!this.cellSelection && !this.rowSelection)){
9270             return;
9271         }
9272         
9273         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9274             cell = cell.findParent('td', false, true);
9275         }
9276         
9277         if(!cell || typeof(cell) == 'undefined'){
9278             return;
9279         }
9280         
9281         var row = cell.findParent('tr', false, true);
9282         
9283         if(!row || typeof(row) == 'undefined'){
9284             return;
9285         }
9286         
9287         var cellIndex = cell.dom.cellIndex;
9288         var rowIndex = this.getRowIndex(row);
9289         
9290         // why??? - should these not be based on SelectionModel?
9291         //if(this.cellSelection){
9292             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9293         //}
9294         
9295         //if(this.rowSelection){
9296             this.fireEvent('rowclick', this, row, rowIndex, e);
9297         //}
9298          
9299     },
9300         
9301     onDblClick : function(e,el)
9302     {
9303         var cell = Roo.get(el);
9304         
9305         if(!cell || (!this.cellSelection && !this.rowSelection)){
9306             return;
9307         }
9308         
9309         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9310             cell = cell.findParent('td', false, true);
9311         }
9312         
9313         if(!cell || typeof(cell) == 'undefined'){
9314             return;
9315         }
9316         
9317         var row = cell.findParent('tr', false, true);
9318         
9319         if(!row || typeof(row) == 'undefined'){
9320             return;
9321         }
9322         
9323         var cellIndex = cell.dom.cellIndex;
9324         var rowIndex = this.getRowIndex(row);
9325         
9326         if(this.cellSelection){
9327             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9328         }
9329         
9330         if(this.rowSelection){
9331             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9332         }
9333     },
9334     findRowIndex : function(el)
9335     {
9336         var cell = Roo.get(el);
9337         if(!cell) {
9338             return false;
9339         }
9340         var row = cell.findParent('tr', false, true);
9341         
9342         if(!row || typeof(row) == 'undefined'){
9343             return false;
9344         }
9345         return this.getRowIndex(row);
9346     },
9347     sort : function(e,el)
9348     {
9349         var col = Roo.get(el);
9350         
9351         if(!col.hasClass('sortable')){
9352             return;
9353         }
9354         
9355         var sort = col.attr('sort');
9356         var dir = 'ASC';
9357         
9358         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9359             dir = 'DESC';
9360         }
9361         
9362         this.store.sortInfo = {field : sort, direction : dir};
9363         
9364         if (this.footer) {
9365             Roo.log("calling footer first");
9366             this.footer.onClick('first');
9367         } else {
9368         
9369             this.store.load({ params : { start : 0 } });
9370         }
9371     },
9372     
9373     renderHeader : function()
9374     {
9375         var header = {
9376             tag: 'thead',
9377             cn : []
9378         };
9379         
9380         var cm = this.cm;
9381         this.totalWidth = 0;
9382         
9383         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9384             
9385             var config = cm.config[i];
9386             
9387             var c = {
9388                 tag: 'th',
9389                 cls : 'x-hcol-' + i,
9390                 style : '',
9391                 
9392                 html: cm.getColumnHeader(i)
9393             };
9394             
9395             var tooltip = cm.getColumnTooltip(i);
9396             if (tooltip) {
9397                 c.tooltip = tooltip;
9398             }
9399             
9400             
9401             var hh = '';
9402             
9403             if(typeof(config.sortable) != 'undefined' && config.sortable){
9404                 c.cls += ' sortable';
9405                 c.html = '<i class="fa"></i>' + c.html;
9406             }
9407             
9408             // could use BS4 hidden-..-down 
9409             
9410             if(typeof(config.lgHeader) != 'undefined'){
9411                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9412             }
9413             
9414             if(typeof(config.mdHeader) != 'undefined'){
9415                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9416             }
9417             
9418             if(typeof(config.smHeader) != 'undefined'){
9419                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9420             }
9421             
9422             if(typeof(config.xsHeader) != 'undefined'){
9423                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9424             }
9425             
9426             if(hh.length){
9427                 c.html = hh;
9428             }
9429             
9430             if(typeof(config.tooltip) != 'undefined'){
9431                 c.tooltip = config.tooltip;
9432             }
9433             
9434             if(typeof(config.colspan) != 'undefined'){
9435                 c.colspan = config.colspan;
9436             }
9437             
9438             // hidden is handled by CSS now
9439             
9440             if(typeof(config.dataIndex) != 'undefined'){
9441                 c.sort = config.dataIndex;
9442             }
9443             
9444            
9445             
9446             if(typeof(config.align) != 'undefined' && config.align.length){
9447                 c.style += ' text-align:' + config.align + ';';
9448             }
9449             
9450             /* width is done in CSS
9451              *if(typeof(config.width) != 'undefined'){
9452                 c.style += ' width:' + config.width + 'px;';
9453                 this.totalWidth += config.width;
9454             } else {
9455                 this.totalWidth += 100; // assume minimum of 100 per column?
9456             }
9457             */
9458             
9459             if(typeof(config.cls) != 'undefined'){
9460                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9461             }
9462             // this is the bit that doesnt reall work at all...
9463             
9464             if (this.responsive) {
9465                  
9466             
9467                 ['xs','sm','md','lg'].map(function(size){
9468                     
9469                     if(typeof(config[size]) == 'undefined'){
9470                         return;
9471                     }
9472                      
9473                     if (!config[size]) { // 0 = hidden
9474                         // BS 4 '0' is treated as hide that column and below.
9475                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9476                         return;
9477                     }
9478                     
9479                     c.cls += ' col-' + size + '-' + config[size] + (
9480                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9481                     );
9482                     
9483                     
9484                 });
9485             }
9486             // at the end?
9487             
9488             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9489             
9490             
9491             
9492             
9493             header.cn.push(c)
9494         }
9495         
9496         return header;
9497     },
9498     
9499     renderBody : function()
9500     {
9501         var body = {
9502             tag: 'tbody',
9503             cn : [
9504                 {
9505                     tag: 'tr',
9506                     cn : [
9507                         {
9508                             tag : 'td',
9509                             colspan :  this.cm.getColumnCount()
9510                         }
9511                     ]
9512                 }
9513             ]
9514         };
9515         
9516         return body;
9517     },
9518     
9519     renderFooter : function()
9520     {
9521         var footer = {
9522             tag: 'tfoot',
9523             cn : [
9524                 {
9525                     tag: 'tr',
9526                     cn : [
9527                         {
9528                             tag : 'td',
9529                             colspan :  this.cm.getColumnCount()
9530                         }
9531                     ]
9532                 }
9533             ]
9534         };
9535         
9536         return footer;
9537     },
9538     
9539     
9540     
9541     onLoad : function()
9542     {
9543 //        Roo.log('ds onload');
9544         this.clear();
9545         
9546         var _this = this;
9547         var cm = this.cm;
9548         var ds = this.store;
9549         
9550         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9551             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9552             if (_this.store.sortInfo) {
9553                     
9554                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9555                     e.select('i', true).addClass(['fa-arrow-up']);
9556                 }
9557                 
9558                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9559                     e.select('i', true).addClass(['fa-arrow-down']);
9560                 }
9561             }
9562         });
9563         
9564         var tbody =  this.bodyEl;
9565               
9566         if(ds.getCount() > 0){
9567             ds.data.each(function(d,rowIndex){
9568                 var row =  this.renderRow(cm, ds, rowIndex);
9569                 
9570                 tbody.createChild(row);
9571                 
9572                 var _this = this;
9573                 
9574                 if(row.cellObjects.length){
9575                     Roo.each(row.cellObjects, function(r){
9576                         _this.renderCellObject(r);
9577                     })
9578                 }
9579                 
9580             }, this);
9581         }
9582         
9583         var tfoot = this.el.select('tfoot', true).first();
9584         
9585         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9586             
9587             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9588             
9589             var total = this.ds.getTotalCount();
9590             
9591             if(this.footer.pageSize < total){
9592                 this.mainFoot.show();
9593             }
9594         }
9595         
9596         Roo.each(this.el.select('tbody td', true).elements, function(e){
9597             e.on('mouseover', _this.onMouseover, _this);
9598         });
9599         
9600         Roo.each(this.el.select('tbody td', true).elements, function(e){
9601             e.on('mouseout', _this.onMouseout, _this);
9602         });
9603         this.fireEvent('rowsrendered', this);
9604         
9605         this.autoSize();
9606         
9607         this.initCSS(); /// resize cols
9608
9609         
9610     },
9611     
9612     
9613     onUpdate : function(ds,record)
9614     {
9615         this.refreshRow(record);
9616         this.autoSize();
9617     },
9618     
9619     onRemove : function(ds, record, index, isUpdate){
9620         if(isUpdate !== true){
9621             this.fireEvent("beforerowremoved", this, index, record);
9622         }
9623         var bt = this.bodyEl.dom;
9624         
9625         var rows = this.el.select('tbody > tr', true).elements;
9626         
9627         if(typeof(rows[index]) != 'undefined'){
9628             bt.removeChild(rows[index].dom);
9629         }
9630         
9631 //        if(bt.rows[index]){
9632 //            bt.removeChild(bt.rows[index]);
9633 //        }
9634         
9635         if(isUpdate !== true){
9636             //this.stripeRows(index);
9637             //this.syncRowHeights(index, index);
9638             //this.layout();
9639             this.fireEvent("rowremoved", this, index, record);
9640         }
9641     },
9642     
9643     onAdd : function(ds, records, rowIndex)
9644     {
9645         //Roo.log('on Add called');
9646         // - note this does not handle multiple adding very well..
9647         var bt = this.bodyEl.dom;
9648         for (var i =0 ; i < records.length;i++) {
9649             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9650             //Roo.log(records[i]);
9651             //Roo.log(this.store.getAt(rowIndex+i));
9652             this.insertRow(this.store, rowIndex + i, false);
9653             return;
9654         }
9655         
9656     },
9657     
9658     
9659     refreshRow : function(record){
9660         var ds = this.store, index;
9661         if(typeof record == 'number'){
9662             index = record;
9663             record = ds.getAt(index);
9664         }else{
9665             index = ds.indexOf(record);
9666             if (index < 0) {
9667                 return; // should not happen - but seems to 
9668             }
9669         }
9670         this.insertRow(ds, index, true);
9671         this.autoSize();
9672         this.onRemove(ds, record, index+1, true);
9673         this.autoSize();
9674         //this.syncRowHeights(index, index);
9675         //this.layout();
9676         this.fireEvent("rowupdated", this, index, record);
9677     },
9678     // private - called by RowSelection
9679     onRowSelect : function(rowIndex){
9680         var row = this.getRowDom(rowIndex);
9681         row.addClass(['bg-info','info']);
9682     },
9683     // private - called by RowSelection
9684     onRowDeselect : function(rowIndex)
9685     {
9686         if (rowIndex < 0) {
9687             return;
9688         }
9689         var row = this.getRowDom(rowIndex);
9690         row.removeClass(['bg-info','info']);
9691     },
9692       /**
9693      * Focuses the specified row.
9694      * @param {Number} row The row index
9695      */
9696     focusRow : function(row)
9697     {
9698         //Roo.log('GridView.focusRow');
9699         var x = this.bodyEl.dom.scrollLeft;
9700         this.focusCell(row, 0, false);
9701         this.bodyEl.dom.scrollLeft = x;
9702
9703     },
9704      /**
9705      * Focuses the specified cell.
9706      * @param {Number} row The row index
9707      * @param {Number} col The column index
9708      * @param {Boolean} hscroll false to disable horizontal scrolling
9709      */
9710     focusCell : function(row, col, hscroll)
9711     {
9712         //Roo.log('GridView.focusCell');
9713         var el = this.ensureVisible(row, col, hscroll);
9714         // not sure what focusEL achives = it's a <a> pos relative 
9715         //this.focusEl.alignTo(el, "tl-tl");
9716         //if(Roo.isGecko){
9717         //    this.focusEl.focus();
9718         //}else{
9719         //    this.focusEl.focus.defer(1, this.focusEl);
9720         //}
9721     },
9722     
9723      /**
9724      * Scrolls the specified cell into view
9725      * @param {Number} row The row index
9726      * @param {Number} col The column index
9727      * @param {Boolean} hscroll false to disable horizontal scrolling
9728      */
9729     ensureVisible : function(row, col, hscroll)
9730     {
9731         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9732         //return null; //disable for testing.
9733         if(typeof row != "number"){
9734             row = row.rowIndex;
9735         }
9736         if(row < 0 && row >= this.ds.getCount()){
9737             return  null;
9738         }
9739         col = (col !== undefined ? col : 0);
9740         var cm = this.cm;
9741         while(cm.isHidden(col)){
9742             col++;
9743         }
9744
9745         var el = this.getCellDom(row, col);
9746         if(!el){
9747             return null;
9748         }
9749         var c = this.bodyEl.dom;
9750
9751         var ctop = parseInt(el.offsetTop, 10);
9752         var cleft = parseInt(el.offsetLeft, 10);
9753         var cbot = ctop + el.offsetHeight;
9754         var cright = cleft + el.offsetWidth;
9755
9756         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9757         var ch = 0; //?? header is not withing the area?
9758         var stop = parseInt(c.scrollTop, 10);
9759         var sleft = parseInt(c.scrollLeft, 10);
9760         var sbot = stop + ch;
9761         var sright = sleft + c.clientWidth;
9762         /*
9763         Roo.log('GridView.ensureVisible:' +
9764                 ' ctop:' + ctop +
9765                 ' c.clientHeight:' + c.clientHeight +
9766                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9767                 ' stop:' + stop +
9768                 ' cbot:' + cbot +
9769                 ' sbot:' + sbot +
9770                 ' ch:' + ch  
9771                 );
9772         */
9773         if(ctop < stop){
9774             c.scrollTop = ctop;
9775             //Roo.log("set scrolltop to ctop DISABLE?");
9776         }else if(cbot > sbot){
9777             //Roo.log("set scrolltop to cbot-ch");
9778             c.scrollTop = cbot-ch;
9779         }
9780
9781         if(hscroll !== false){
9782             if(cleft < sleft){
9783                 c.scrollLeft = cleft;
9784             }else if(cright > sright){
9785                 c.scrollLeft = cright-c.clientWidth;
9786             }
9787         }
9788
9789         return el;
9790     },
9791     
9792     
9793     insertRow : function(dm, rowIndex, isUpdate){
9794         
9795         if(!isUpdate){
9796             this.fireEvent("beforerowsinserted", this, rowIndex);
9797         }
9798             //var s = this.getScrollState();
9799         var row = this.renderRow(this.cm, this.store, rowIndex);
9800         // insert before rowIndex..
9801         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9802         
9803         var _this = this;
9804                 
9805         if(row.cellObjects.length){
9806             Roo.each(row.cellObjects, function(r){
9807                 _this.renderCellObject(r);
9808             })
9809         }
9810             
9811         if(!isUpdate){
9812             this.fireEvent("rowsinserted", this, rowIndex);
9813             //this.syncRowHeights(firstRow, lastRow);
9814             //this.stripeRows(firstRow);
9815             //this.layout();
9816         }
9817         
9818     },
9819     
9820     
9821     getRowDom : function(rowIndex)
9822     {
9823         var rows = this.el.select('tbody > tr', true).elements;
9824         
9825         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9826         
9827     },
9828     getCellDom : function(rowIndex, colIndex)
9829     {
9830         var row = this.getRowDom(rowIndex);
9831         if (row === false) {
9832             return false;
9833         }
9834         var cols = row.select('td', true).elements;
9835         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9836         
9837     },
9838     
9839     // returns the object tree for a tr..
9840   
9841     
9842     renderRow : function(cm, ds, rowIndex) 
9843     {
9844         var d = ds.getAt(rowIndex);
9845         
9846         var row = {
9847             tag : 'tr',
9848             cls : 'x-row-' + rowIndex,
9849             cn : []
9850         };
9851             
9852         var cellObjects = [];
9853         
9854         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9855             var config = cm.config[i];
9856             
9857             var renderer = cm.getRenderer(i);
9858             var value = '';
9859             var id = false;
9860             
9861             if(typeof(renderer) !== 'undefined'){
9862                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9863             }
9864             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9865             // and are rendered into the cells after the row is rendered - using the id for the element.
9866             
9867             if(typeof(value) === 'object'){
9868                 id = Roo.id();
9869                 cellObjects.push({
9870                     container : id,
9871                     cfg : value 
9872                 })
9873             }
9874             
9875             var rowcfg = {
9876                 record: d,
9877                 rowIndex : rowIndex,
9878                 colIndex : i,
9879                 rowClass : ''
9880             };
9881
9882             this.fireEvent('rowclass', this, rowcfg);
9883             
9884             var td = {
9885                 tag: 'td',
9886                 // this might end up displaying HTML?
9887                 // this is too messy... - better to only do it on columsn you know are going to be too long
9888                 //tooltip : (typeof(value) === 'object') ? '' : value,
9889                 cls : rowcfg.rowClass + ' x-col-' + i,
9890                 style: '',
9891                 html: (typeof(value) === 'object') ? '' : value
9892             };
9893             
9894             if (id) {
9895                 td.id = id;
9896             }
9897             
9898             if(typeof(config.colspan) != 'undefined'){
9899                 td.colspan = config.colspan;
9900             }
9901             
9902             
9903             
9904             if(typeof(config.align) != 'undefined' && config.align.length){
9905                 td.style += ' text-align:' + config.align + ';';
9906             }
9907             if(typeof(config.valign) != 'undefined' && config.valign.length){
9908                 td.style += ' vertical-align:' + config.valign + ';';
9909             }
9910             /*
9911             if(typeof(config.width) != 'undefined'){
9912                 td.style += ' width:' +  config.width + 'px;';
9913             }
9914             */
9915             
9916             if(typeof(config.cursor) != 'undefined'){
9917                 td.style += ' cursor:' +  config.cursor + ';';
9918             }
9919             
9920             if(typeof(config.cls) != 'undefined'){
9921                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9922             }
9923             if (this.responsive) {
9924                 ['xs','sm','md','lg'].map(function(size){
9925                     
9926                     if(typeof(config[size]) == 'undefined'){
9927                         return;
9928                     }
9929                     
9930                     
9931                       
9932                     if (!config[size]) { // 0 = hidden
9933                         // BS 4 '0' is treated as hide that column and below.
9934                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9935                         return;
9936                     }
9937                     
9938                     td.cls += ' col-' + size + '-' + config[size] + (
9939                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9940                     );
9941                      
9942     
9943                 });
9944             }
9945             row.cn.push(td);
9946            
9947         }
9948         
9949         row.cellObjects = cellObjects;
9950         
9951         return row;
9952           
9953     },
9954     
9955     
9956     
9957     onBeforeLoad : function()
9958     {
9959         
9960     },
9961      /**
9962      * Remove all rows
9963      */
9964     clear : function()
9965     {
9966         this.el.select('tbody', true).first().dom.innerHTML = '';
9967     },
9968     /**
9969      * Show or hide a row.
9970      * @param {Number} rowIndex to show or hide
9971      * @param {Boolean} state hide
9972      */
9973     setRowVisibility : function(rowIndex, state)
9974     {
9975         var bt = this.bodyEl.dom;
9976         
9977         var rows = this.el.select('tbody > tr', true).elements;
9978         
9979         if(typeof(rows[rowIndex]) == 'undefined'){
9980             return;
9981         }
9982         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9983         
9984     },
9985     
9986     
9987     getSelectionModel : function(){
9988         if(!this.selModel){
9989             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9990         }
9991         return this.selModel;
9992     },
9993     /*
9994      * Render the Roo.bootstrap object from renderder
9995      */
9996     renderCellObject : function(r)
9997     {
9998         var _this = this;
9999         
10000         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10001         
10002         var t = r.cfg.render(r.container);
10003         
10004         if(r.cfg.cn){
10005             Roo.each(r.cfg.cn, function(c){
10006                 var child = {
10007                     container: t.getChildContainer(),
10008                     cfg: c
10009                 };
10010                 _this.renderCellObject(child);
10011             })
10012         }
10013     },
10014     /**
10015      * get the Row Index from a dom element.
10016      * @param {Roo.Element} row The row to look for
10017      * @returns {Number} the row
10018      */
10019     getRowIndex : function(row)
10020     {
10021         var rowIndex = -1;
10022         
10023         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10024             if(el != row){
10025                 return;
10026             }
10027             
10028             rowIndex = index;
10029         });
10030         
10031         return rowIndex;
10032     },
10033     /**
10034      * get the header TH element for columnIndex
10035      * @param {Number} columnIndex
10036      * @returns {Roo.Element}
10037      */
10038     getHeaderIndex: function(colIndex)
10039     {
10040         var cols = this.headEl.select('th', true).elements;
10041         return cols[colIndex]; 
10042     },
10043     /**
10044      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10045      * @param {domElement} cell to look for
10046      * @returns {Number} the column
10047      */
10048     getCellIndex : function(cell)
10049     {
10050         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10051         if(id){
10052             return parseInt(id[1], 10);
10053         }
10054         return 0;
10055     },
10056      /**
10057      * Returns the grid's underlying element = used by panel.Grid
10058      * @return {Element} The element
10059      */
10060     getGridEl : function(){
10061         return this.el;
10062     },
10063      /**
10064      * Forces a resize - used by panel.Grid
10065      * @return {Element} The element
10066      */
10067     autoSize : function()
10068     {
10069         //var ctr = Roo.get(this.container.dom.parentElement);
10070         var ctr = Roo.get(this.el.dom);
10071         
10072         var thd = this.getGridEl().select('thead',true).first();
10073         var tbd = this.getGridEl().select('tbody', true).first();
10074         var tfd = this.getGridEl().select('tfoot', true).first();
10075         
10076         var cw = ctr.getWidth();
10077         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10078         
10079         if (tbd) {
10080             
10081             tbd.setWidth(ctr.getWidth());
10082             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10083             // this needs fixing for various usage - currently only hydra job advers I think..
10084             //tdb.setHeight(
10085             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10086             //); 
10087             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10088             cw -= barsize;
10089         }
10090         cw = Math.max(cw, this.totalWidth);
10091         this.getGridEl().select('tbody tr',true).setWidth(cw);
10092         this.initCSS();
10093         
10094         // resize 'expandable coloumn?
10095         
10096         return; // we doe not have a view in this design..
10097         
10098     },
10099     onBodyScroll: function()
10100     {
10101         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10102         if(this.headEl){
10103             this.headEl.setStyle({
10104                 'position' : 'relative',
10105                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10106             });
10107         }
10108         
10109         if(this.lazyLoad){
10110             
10111             var scrollHeight = this.bodyEl.dom.scrollHeight;
10112             
10113             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10114             
10115             var height = this.bodyEl.getHeight();
10116             
10117             if(scrollHeight - height == scrollTop) {
10118                 
10119                 var total = this.ds.getTotalCount();
10120                 
10121                 if(this.footer.cursor + this.footer.pageSize < total){
10122                     
10123                     this.footer.ds.load({
10124                         params : {
10125                             start : this.footer.cursor + this.footer.pageSize,
10126                             limit : this.footer.pageSize
10127                         },
10128                         add : true
10129                     });
10130                 }
10131             }
10132             
10133         }
10134     },
10135     onColumnSplitterMoved : function(i, diff)
10136     {
10137         this.userResized = true;
10138         
10139         var cm = this.colModel;
10140         
10141         var w = this.getHeaderIndex(i).getWidth() + diff;
10142         
10143         
10144         cm.setColumnWidth(i, w, true);
10145         this.initCSS();
10146         //var cid = cm.getColumnId(i); << not used in this version?
10147        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10148         
10149         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10150         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10151         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10152 */
10153         //this.updateSplitters();
10154         //this.layout(); << ??
10155         this.fireEvent("columnresize", i, w);
10156     },
10157     onHeaderChange : function()
10158     {
10159         var header = this.renderHeader();
10160         var table = this.el.select('table', true).first();
10161         
10162         this.headEl.remove();
10163         this.headEl = table.createChild(header, this.bodyEl, false);
10164         
10165         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10166             e.on('click', this.sort, this);
10167         }, this);
10168         
10169         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10170             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10171         }
10172         
10173     },
10174     
10175     onHiddenChange : function(colModel, colIndex, hidden)
10176     {
10177         /*
10178         this.cm.setHidden()
10179         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10180         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10181         
10182         this.CSS.updateRule(thSelector, "display", "");
10183         this.CSS.updateRule(tdSelector, "display", "");
10184         
10185         if(hidden){
10186             this.CSS.updateRule(thSelector, "display", "none");
10187             this.CSS.updateRule(tdSelector, "display", "none");
10188         }
10189         */
10190         // onload calls initCSS()
10191         this.onHeaderChange();
10192         this.onLoad();
10193     },
10194     
10195     setColumnWidth: function(col_index, width)
10196     {
10197         // width = "md-2 xs-2..."
10198         if(!this.colModel.config[col_index]) {
10199             return;
10200         }
10201         
10202         var w = width.split(" ");
10203         
10204         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10205         
10206         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10207         
10208         
10209         for(var j = 0; j < w.length; j++) {
10210             
10211             if(!w[j]) {
10212                 continue;
10213             }
10214             
10215             var size_cls = w[j].split("-");
10216             
10217             if(!Number.isInteger(size_cls[1] * 1)) {
10218                 continue;
10219             }
10220             
10221             if(!this.colModel.config[col_index][size_cls[0]]) {
10222                 continue;
10223             }
10224             
10225             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10226                 continue;
10227             }
10228             
10229             h_row[0].classList.replace(
10230                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10231                 "col-"+size_cls[0]+"-"+size_cls[1]
10232             );
10233             
10234             for(var i = 0; i < rows.length; i++) {
10235                 
10236                 var size_cls = w[j].split("-");
10237                 
10238                 if(!Number.isInteger(size_cls[1] * 1)) {
10239                     continue;
10240                 }
10241                 
10242                 if(!this.colModel.config[col_index][size_cls[0]]) {
10243                     continue;
10244                 }
10245                 
10246                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10247                     continue;
10248                 }
10249                 
10250                 rows[i].classList.replace(
10251                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10252                     "col-"+size_cls[0]+"-"+size_cls[1]
10253                 );
10254             }
10255             
10256             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10257         }
10258     }
10259 });
10260
10261 // currently only used to find the split on drag.. 
10262 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10263
10264 /**
10265  * @depricated
10266 */
10267 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10268 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10269 /*
10270  * - LGPL
10271  *
10272  * table cell
10273  * 
10274  */
10275
10276 /**
10277  * @class Roo.bootstrap.TableCell
10278  * @extends Roo.bootstrap.Component
10279  * Bootstrap TableCell class
10280  * @cfg {String} html cell contain text
10281  * @cfg {String} cls cell class
10282  * @cfg {String} tag cell tag (td|th) default td
10283  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10284  * @cfg {String} align Aligns the content in a cell
10285  * @cfg {String} axis Categorizes cells
10286  * @cfg {String} bgcolor Specifies the background color of a cell
10287  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10288  * @cfg {Number} colspan Specifies the number of columns a cell should span
10289  * @cfg {String} headers Specifies one or more header cells a cell is related to
10290  * @cfg {Number} height Sets the height of a cell
10291  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10292  * @cfg {Number} rowspan Sets the number of rows a cell should span
10293  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10294  * @cfg {String} valign Vertical aligns the content in a cell
10295  * @cfg {Number} width Specifies the width of a cell
10296  * 
10297  * @constructor
10298  * Create a new TableCell
10299  * @param {Object} config The config object
10300  */
10301
10302 Roo.bootstrap.TableCell = function(config){
10303     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10304 };
10305
10306 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10307     
10308     html: false,
10309     cls: false,
10310     tag: false,
10311     abbr: false,
10312     align: false,
10313     axis: false,
10314     bgcolor: false,
10315     charoff: false,
10316     colspan: false,
10317     headers: false,
10318     height: false,
10319     nowrap: false,
10320     rowspan: false,
10321     scope: false,
10322     valign: false,
10323     width: false,
10324     
10325     
10326     getAutoCreate : function(){
10327         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10328         
10329         cfg = {
10330             tag: 'td'
10331         };
10332         
10333         if(this.tag){
10334             cfg.tag = this.tag;
10335         }
10336         
10337         if (this.html) {
10338             cfg.html=this.html
10339         }
10340         if (this.cls) {
10341             cfg.cls=this.cls
10342         }
10343         if (this.abbr) {
10344             cfg.abbr=this.abbr
10345         }
10346         if (this.align) {
10347             cfg.align=this.align
10348         }
10349         if (this.axis) {
10350             cfg.axis=this.axis
10351         }
10352         if (this.bgcolor) {
10353             cfg.bgcolor=this.bgcolor
10354         }
10355         if (this.charoff) {
10356             cfg.charoff=this.charoff
10357         }
10358         if (this.colspan) {
10359             cfg.colspan=this.colspan
10360         }
10361         if (this.headers) {
10362             cfg.headers=this.headers
10363         }
10364         if (this.height) {
10365             cfg.height=this.height
10366         }
10367         if (this.nowrap) {
10368             cfg.nowrap=this.nowrap
10369         }
10370         if (this.rowspan) {
10371             cfg.rowspan=this.rowspan
10372         }
10373         if (this.scope) {
10374             cfg.scope=this.scope
10375         }
10376         if (this.valign) {
10377             cfg.valign=this.valign
10378         }
10379         if (this.width) {
10380             cfg.width=this.width
10381         }
10382         
10383         
10384         return cfg;
10385     }
10386    
10387 });
10388
10389  
10390
10391  /*
10392  * - LGPL
10393  *
10394  * table row
10395  * 
10396  */
10397
10398 /**
10399  * @class Roo.bootstrap.TableRow
10400  * @extends Roo.bootstrap.Component
10401  * Bootstrap TableRow class
10402  * @cfg {String} cls row class
10403  * @cfg {String} align Aligns the content in a table row
10404  * @cfg {String} bgcolor Specifies a background color for a table row
10405  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10406  * @cfg {String} valign Vertical aligns the content in a table row
10407  * 
10408  * @constructor
10409  * Create a new TableRow
10410  * @param {Object} config The config object
10411  */
10412
10413 Roo.bootstrap.TableRow = function(config){
10414     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10415 };
10416
10417 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10418     
10419     cls: false,
10420     align: false,
10421     bgcolor: false,
10422     charoff: false,
10423     valign: false,
10424     
10425     getAutoCreate : function(){
10426         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10427         
10428         cfg = {
10429             tag: 'tr'
10430         };
10431             
10432         if(this.cls){
10433             cfg.cls = this.cls;
10434         }
10435         if(this.align){
10436             cfg.align = this.align;
10437         }
10438         if(this.bgcolor){
10439             cfg.bgcolor = this.bgcolor;
10440         }
10441         if(this.charoff){
10442             cfg.charoff = this.charoff;
10443         }
10444         if(this.valign){
10445             cfg.valign = this.valign;
10446         }
10447         
10448         return cfg;
10449     }
10450    
10451 });
10452
10453  
10454
10455  /*
10456  * - LGPL
10457  *
10458  * table body
10459  * 
10460  */
10461
10462 /**
10463  * @class Roo.bootstrap.TableBody
10464  * @extends Roo.bootstrap.Component
10465  * Bootstrap TableBody class
10466  * @cfg {String} cls element class
10467  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10468  * @cfg {String} align Aligns the content inside the element
10469  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10470  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10471  * 
10472  * @constructor
10473  * Create a new TableBody
10474  * @param {Object} config The config object
10475  */
10476
10477 Roo.bootstrap.TableBody = function(config){
10478     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10479 };
10480
10481 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10482     
10483     cls: false,
10484     tag: false,
10485     align: false,
10486     charoff: false,
10487     valign: false,
10488     
10489     getAutoCreate : function(){
10490         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10491         
10492         cfg = {
10493             tag: 'tbody'
10494         };
10495             
10496         if (this.cls) {
10497             cfg.cls=this.cls
10498         }
10499         if(this.tag){
10500             cfg.tag = this.tag;
10501         }
10502         
10503         if(this.align){
10504             cfg.align = this.align;
10505         }
10506         if(this.charoff){
10507             cfg.charoff = this.charoff;
10508         }
10509         if(this.valign){
10510             cfg.valign = this.valign;
10511         }
10512         
10513         return cfg;
10514     }
10515     
10516     
10517 //    initEvents : function()
10518 //    {
10519 //        
10520 //        if(!this.store){
10521 //            return;
10522 //        }
10523 //        
10524 //        this.store = Roo.factory(this.store, Roo.data);
10525 //        this.store.on('load', this.onLoad, this);
10526 //        
10527 //        this.store.load();
10528 //        
10529 //    },
10530 //    
10531 //    onLoad: function () 
10532 //    {   
10533 //        this.fireEvent('load', this);
10534 //    }
10535 //    
10536 //   
10537 });
10538
10539  
10540
10541  /*
10542  * Based on:
10543  * Ext JS Library 1.1.1
10544  * Copyright(c) 2006-2007, Ext JS, LLC.
10545  *
10546  * Originally Released Under LGPL - original licence link has changed is not relivant.
10547  *
10548  * Fork - LGPL
10549  * <script type="text/javascript">
10550  */
10551
10552 // as we use this in bootstrap.
10553 Roo.namespace('Roo.form');
10554  /**
10555  * @class Roo.form.Action
10556  * Internal Class used to handle form actions
10557  * @constructor
10558  * @param {Roo.form.BasicForm} el The form element or its id
10559  * @param {Object} config Configuration options
10560  */
10561
10562  
10563  
10564 // define the action interface
10565 Roo.form.Action = function(form, options){
10566     this.form = form;
10567     this.options = options || {};
10568 };
10569 /**
10570  * Client Validation Failed
10571  * @const 
10572  */
10573 Roo.form.Action.CLIENT_INVALID = 'client';
10574 /**
10575  * Server Validation Failed
10576  * @const 
10577  */
10578 Roo.form.Action.SERVER_INVALID = 'server';
10579  /**
10580  * Connect to Server Failed
10581  * @const 
10582  */
10583 Roo.form.Action.CONNECT_FAILURE = 'connect';
10584 /**
10585  * Reading Data from Server Failed
10586  * @const 
10587  */
10588 Roo.form.Action.LOAD_FAILURE = 'load';
10589
10590 Roo.form.Action.prototype = {
10591     type : 'default',
10592     failureType : undefined,
10593     response : undefined,
10594     result : undefined,
10595
10596     // interface method
10597     run : function(options){
10598
10599     },
10600
10601     // interface method
10602     success : function(response){
10603
10604     },
10605
10606     // interface method
10607     handleResponse : function(response){
10608
10609     },
10610
10611     // default connection failure
10612     failure : function(response){
10613         
10614         this.response = response;
10615         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10616         this.form.afterAction(this, false);
10617     },
10618
10619     processResponse : function(response){
10620         this.response = response;
10621         if(!response.responseText){
10622             return true;
10623         }
10624         this.result = this.handleResponse(response);
10625         return this.result;
10626     },
10627
10628     // utility functions used internally
10629     getUrl : function(appendParams){
10630         var url = this.options.url || this.form.url || this.form.el.dom.action;
10631         if(appendParams){
10632             var p = this.getParams();
10633             if(p){
10634                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10635             }
10636         }
10637         return url;
10638     },
10639
10640     getMethod : function(){
10641         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10642     },
10643
10644     getParams : function(){
10645         var bp = this.form.baseParams;
10646         var p = this.options.params;
10647         if(p){
10648             if(typeof p == "object"){
10649                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10650             }else if(typeof p == 'string' && bp){
10651                 p += '&' + Roo.urlEncode(bp);
10652             }
10653         }else if(bp){
10654             p = Roo.urlEncode(bp);
10655         }
10656         return p;
10657     },
10658
10659     createCallback : function(){
10660         return {
10661             success: this.success,
10662             failure: this.failure,
10663             scope: this,
10664             timeout: (this.form.timeout*1000),
10665             upload: this.form.fileUpload ? this.success : undefined
10666         };
10667     }
10668 };
10669
10670 Roo.form.Action.Submit = function(form, options){
10671     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10672 };
10673
10674 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10675     type : 'submit',
10676
10677     haveProgress : false,
10678     uploadComplete : false,
10679     
10680     // uploadProgress indicator.
10681     uploadProgress : function()
10682     {
10683         if (!this.form.progressUrl) {
10684             return;
10685         }
10686         
10687         if (!this.haveProgress) {
10688             Roo.MessageBox.progress("Uploading", "Uploading");
10689         }
10690         if (this.uploadComplete) {
10691            Roo.MessageBox.hide();
10692            return;
10693         }
10694         
10695         this.haveProgress = true;
10696    
10697         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10698         
10699         var c = new Roo.data.Connection();
10700         c.request({
10701             url : this.form.progressUrl,
10702             params: {
10703                 id : uid
10704             },
10705             method: 'GET',
10706             success : function(req){
10707                //console.log(data);
10708                 var rdata = false;
10709                 var edata;
10710                 try  {
10711                    rdata = Roo.decode(req.responseText)
10712                 } catch (e) {
10713                     Roo.log("Invalid data from server..");
10714                     Roo.log(edata);
10715                     return;
10716                 }
10717                 if (!rdata || !rdata.success) {
10718                     Roo.log(rdata);
10719                     Roo.MessageBox.alert(Roo.encode(rdata));
10720                     return;
10721                 }
10722                 var data = rdata.data;
10723                 
10724                 if (this.uploadComplete) {
10725                    Roo.MessageBox.hide();
10726                    return;
10727                 }
10728                    
10729                 if (data){
10730                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10731                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10732                     );
10733                 }
10734                 this.uploadProgress.defer(2000,this);
10735             },
10736        
10737             failure: function(data) {
10738                 Roo.log('progress url failed ');
10739                 Roo.log(data);
10740             },
10741             scope : this
10742         });
10743            
10744     },
10745     
10746     
10747     run : function()
10748     {
10749         // run get Values on the form, so it syncs any secondary forms.
10750         this.form.getValues();
10751         
10752         var o = this.options;
10753         var method = this.getMethod();
10754         var isPost = method == 'POST';
10755         if(o.clientValidation === false || this.form.isValid()){
10756             
10757             if (this.form.progressUrl) {
10758                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10759                     (new Date() * 1) + '' + Math.random());
10760                     
10761             } 
10762             
10763             
10764             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10765                 form:this.form.el.dom,
10766                 url:this.getUrl(!isPost),
10767                 method: method,
10768                 params:isPost ? this.getParams() : null,
10769                 isUpload: this.form.fileUpload,
10770                 formData : this.form.formData
10771             }));
10772             
10773             this.uploadProgress();
10774
10775         }else if (o.clientValidation !== false){ // client validation failed
10776             this.failureType = Roo.form.Action.CLIENT_INVALID;
10777             this.form.afterAction(this, false);
10778         }
10779     },
10780
10781     success : function(response)
10782     {
10783         this.uploadComplete= true;
10784         if (this.haveProgress) {
10785             Roo.MessageBox.hide();
10786         }
10787         
10788         
10789         var result = this.processResponse(response);
10790         if(result === true || result.success){
10791             this.form.afterAction(this, true);
10792             return;
10793         }
10794         if(result.errors){
10795             this.form.markInvalid(result.errors);
10796             this.failureType = Roo.form.Action.SERVER_INVALID;
10797         }
10798         this.form.afterAction(this, false);
10799     },
10800     failure : function(response)
10801     {
10802         this.uploadComplete= true;
10803         if (this.haveProgress) {
10804             Roo.MessageBox.hide();
10805         }
10806         
10807         this.response = response;
10808         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10809         this.form.afterAction(this, false);
10810     },
10811     
10812     handleResponse : function(response){
10813         if(this.form.errorReader){
10814             var rs = this.form.errorReader.read(response);
10815             var errors = [];
10816             if(rs.records){
10817                 for(var i = 0, len = rs.records.length; i < len; i++) {
10818                     var r = rs.records[i];
10819                     errors[i] = r.data;
10820                 }
10821             }
10822             if(errors.length < 1){
10823                 errors = null;
10824             }
10825             return {
10826                 success : rs.success,
10827                 errors : errors
10828             };
10829         }
10830         var ret = false;
10831         try {
10832             ret = Roo.decode(response.responseText);
10833         } catch (e) {
10834             ret = {
10835                 success: false,
10836                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10837                 errors : []
10838             };
10839         }
10840         return ret;
10841         
10842     }
10843 });
10844
10845
10846 Roo.form.Action.Load = function(form, options){
10847     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10848     this.reader = this.form.reader;
10849 };
10850
10851 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10852     type : 'load',
10853
10854     run : function(){
10855         
10856         Roo.Ajax.request(Roo.apply(
10857                 this.createCallback(), {
10858                     method:this.getMethod(),
10859                     url:this.getUrl(false),
10860                     params:this.getParams()
10861         }));
10862     },
10863
10864     success : function(response){
10865         
10866         var result = this.processResponse(response);
10867         if(result === true || !result.success || !result.data){
10868             this.failureType = Roo.form.Action.LOAD_FAILURE;
10869             this.form.afterAction(this, false);
10870             return;
10871         }
10872         this.form.clearInvalid();
10873         this.form.setValues(result.data);
10874         this.form.afterAction(this, true);
10875     },
10876
10877     handleResponse : function(response){
10878         if(this.form.reader){
10879             var rs = this.form.reader.read(response);
10880             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10881             return {
10882                 success : rs.success,
10883                 data : data
10884             };
10885         }
10886         return Roo.decode(response.responseText);
10887     }
10888 });
10889
10890 Roo.form.Action.ACTION_TYPES = {
10891     'load' : Roo.form.Action.Load,
10892     'submit' : Roo.form.Action.Submit
10893 };/*
10894  * - LGPL
10895  *
10896  * form
10897  *
10898  */
10899
10900 /**
10901  * @class Roo.bootstrap.Form
10902  * @extends Roo.bootstrap.Component
10903  * Bootstrap Form class
10904  * @cfg {String} method  GET | POST (default POST)
10905  * @cfg {String} labelAlign top | left (default top)
10906  * @cfg {String} align left  | right - for navbars
10907  * @cfg {Boolean} loadMask load mask when submit (default true)
10908
10909  *
10910  * @constructor
10911  * Create a new Form
10912  * @param {Object} config The config object
10913  */
10914
10915
10916 Roo.bootstrap.Form = function(config){
10917     
10918     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10919     
10920     Roo.bootstrap.Form.popover.apply();
10921     
10922     this.addEvents({
10923         /**
10924          * @event clientvalidation
10925          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10926          * @param {Form} this
10927          * @param {Boolean} valid true if the form has passed client-side validation
10928          */
10929         clientvalidation: true,
10930         /**
10931          * @event beforeaction
10932          * Fires before any action is performed. Return false to cancel the action.
10933          * @param {Form} this
10934          * @param {Action} action The action to be performed
10935          */
10936         beforeaction: true,
10937         /**
10938          * @event actionfailed
10939          * Fires when an action fails.
10940          * @param {Form} this
10941          * @param {Action} action The action that failed
10942          */
10943         actionfailed : true,
10944         /**
10945          * @event actioncomplete
10946          * Fires when an action is completed.
10947          * @param {Form} this
10948          * @param {Action} action The action that completed
10949          */
10950         actioncomplete : true
10951     });
10952 };
10953
10954 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10955
10956      /**
10957      * @cfg {String} method
10958      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10959      */
10960     method : 'POST',
10961     /**
10962      * @cfg {String} url
10963      * The URL to use for form actions if one isn't supplied in the action options.
10964      */
10965     /**
10966      * @cfg {Boolean} fileUpload
10967      * Set to true if this form is a file upload.
10968      */
10969
10970     /**
10971      * @cfg {Object} baseParams
10972      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10973      */
10974
10975     /**
10976      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10977      */
10978     timeout: 30,
10979     /**
10980      * @cfg {Sting} align (left|right) for navbar forms
10981      */
10982     align : 'left',
10983
10984     // private
10985     activeAction : null,
10986
10987     /**
10988      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10989      * element by passing it or its id or mask the form itself by passing in true.
10990      * @type Mixed
10991      */
10992     waitMsgTarget : false,
10993
10994     loadMask : true,
10995     
10996     /**
10997      * @cfg {Boolean} errorMask (true|false) default false
10998      */
10999     errorMask : false,
11000     
11001     /**
11002      * @cfg {Number} maskOffset Default 100
11003      */
11004     maskOffset : 100,
11005     
11006     /**
11007      * @cfg {Boolean} maskBody
11008      */
11009     maskBody : false,
11010
11011     getAutoCreate : function(){
11012
11013         var cfg = {
11014             tag: 'form',
11015             method : this.method || 'POST',
11016             id : this.id || Roo.id(),
11017             cls : ''
11018         };
11019         if (this.parent().xtype.match(/^Nav/)) {
11020             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11021
11022         }
11023
11024         if (this.labelAlign == 'left' ) {
11025             cfg.cls += ' form-horizontal';
11026         }
11027
11028
11029         return cfg;
11030     },
11031     initEvents : function()
11032     {
11033         this.el.on('submit', this.onSubmit, this);
11034         // this was added as random key presses on the form where triggering form submit.
11035         this.el.on('keypress', function(e) {
11036             if (e.getCharCode() != 13) {
11037                 return true;
11038             }
11039             // we might need to allow it for textareas.. and some other items.
11040             // check e.getTarget().
11041
11042             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11043                 return true;
11044             }
11045
11046             Roo.log("keypress blocked");
11047
11048             e.preventDefault();
11049             return false;
11050         });
11051         
11052     },
11053     // private
11054     onSubmit : function(e){
11055         e.stopEvent();
11056     },
11057
11058      /**
11059      * Returns true if client-side validation on the form is successful.
11060      * @return Boolean
11061      */
11062     isValid : function(){
11063         var items = this.getItems();
11064         var valid = true;
11065         var target = false;
11066         
11067         items.each(function(f){
11068             
11069             if(f.validate()){
11070                 return;
11071             }
11072             
11073             Roo.log('invalid field: ' + f.name);
11074             
11075             valid = false;
11076
11077             if(!target && f.el.isVisible(true)){
11078                 target = f;
11079             }
11080            
11081         });
11082         
11083         if(this.errorMask && !valid){
11084             Roo.bootstrap.Form.popover.mask(this, target);
11085         }
11086         
11087         return valid;
11088     },
11089     
11090     /**
11091      * Returns true if any fields in this form have changed since their original load.
11092      * @return Boolean
11093      */
11094     isDirty : function(){
11095         var dirty = false;
11096         var items = this.getItems();
11097         items.each(function(f){
11098            if(f.isDirty()){
11099                dirty = true;
11100                return false;
11101            }
11102            return true;
11103         });
11104         return dirty;
11105     },
11106      /**
11107      * Performs a predefined action (submit or load) or custom actions you define on this form.
11108      * @param {String} actionName The name of the action type
11109      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11110      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11111      * accept other config options):
11112      * <pre>
11113 Property          Type             Description
11114 ----------------  ---------------  ----------------------------------------------------------------------------------
11115 url               String           The url for the action (defaults to the form's url)
11116 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11117 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11118 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11119                                    validate the form on the client (defaults to false)
11120      * </pre>
11121      * @return {BasicForm} this
11122      */
11123     doAction : function(action, options){
11124         if(typeof action == 'string'){
11125             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11126         }
11127         if(this.fireEvent('beforeaction', this, action) !== false){
11128             this.beforeAction(action);
11129             action.run.defer(100, action);
11130         }
11131         return this;
11132     },
11133
11134     // private
11135     beforeAction : function(action){
11136         var o = action.options;
11137         
11138         if(this.loadMask){
11139             
11140             if(this.maskBody){
11141                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11142             } else {
11143                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11144             }
11145         }
11146         // not really supported yet.. ??
11147
11148         //if(this.waitMsgTarget === true){
11149         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11150         //}else if(this.waitMsgTarget){
11151         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11152         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11153         //}else {
11154         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11155        // }
11156
11157     },
11158
11159     // private
11160     afterAction : function(action, success){
11161         this.activeAction = null;
11162         var o = action.options;
11163
11164         if(this.loadMask){
11165             
11166             if(this.maskBody){
11167                 Roo.get(document.body).unmask();
11168             } else {
11169                 this.el.unmask();
11170             }
11171         }
11172         
11173         //if(this.waitMsgTarget === true){
11174 //            this.el.unmask();
11175         //}else if(this.waitMsgTarget){
11176         //    this.waitMsgTarget.unmask();
11177         //}else{
11178         //    Roo.MessageBox.updateProgress(1);
11179         //    Roo.MessageBox.hide();
11180        // }
11181         //
11182         if(success){
11183             if(o.reset){
11184                 this.reset();
11185             }
11186             Roo.callback(o.success, o.scope, [this, action]);
11187             this.fireEvent('actioncomplete', this, action);
11188
11189         }else{
11190
11191             // failure condition..
11192             // we have a scenario where updates need confirming.
11193             // eg. if a locking scenario exists..
11194             // we look for { errors : { needs_confirm : true }} in the response.
11195             if (
11196                 (typeof(action.result) != 'undefined')  &&
11197                 (typeof(action.result.errors) != 'undefined')  &&
11198                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11199            ){
11200                 var _t = this;
11201                 Roo.log("not supported yet");
11202                  /*
11203
11204                 Roo.MessageBox.confirm(
11205                     "Change requires confirmation",
11206                     action.result.errorMsg,
11207                     function(r) {
11208                         if (r != 'yes') {
11209                             return;
11210                         }
11211                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11212                     }
11213
11214                 );
11215                 */
11216
11217
11218                 return;
11219             }
11220
11221             Roo.callback(o.failure, o.scope, [this, action]);
11222             // show an error message if no failed handler is set..
11223             if (!this.hasListener('actionfailed')) {
11224                 Roo.log("need to add dialog support");
11225                 /*
11226                 Roo.MessageBox.alert("Error",
11227                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11228                         action.result.errorMsg :
11229                         "Saving Failed, please check your entries or try again"
11230                 );
11231                 */
11232             }
11233
11234             this.fireEvent('actionfailed', this, action);
11235         }
11236
11237     },
11238     /**
11239      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11240      * @param {String} id The value to search for
11241      * @return Field
11242      */
11243     findField : function(id){
11244         var items = this.getItems();
11245         var field = items.get(id);
11246         if(!field){
11247              items.each(function(f){
11248                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11249                     field = f;
11250                     return false;
11251                 }
11252                 return true;
11253             });
11254         }
11255         return field || null;
11256     },
11257      /**
11258      * Mark fields in this form invalid in bulk.
11259      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11260      * @return {BasicForm} this
11261      */
11262     markInvalid : function(errors){
11263         if(errors instanceof Array){
11264             for(var i = 0, len = errors.length; i < len; i++){
11265                 var fieldError = errors[i];
11266                 var f = this.findField(fieldError.id);
11267                 if(f){
11268                     f.markInvalid(fieldError.msg);
11269                 }
11270             }
11271         }else{
11272             var field, id;
11273             for(id in errors){
11274                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11275                     field.markInvalid(errors[id]);
11276                 }
11277             }
11278         }
11279         //Roo.each(this.childForms || [], function (f) {
11280         //    f.markInvalid(errors);
11281         //});
11282
11283         return this;
11284     },
11285
11286     /**
11287      * Set values for fields in this form in bulk.
11288      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11289      * @return {BasicForm} this
11290      */
11291     setValues : function(values){
11292         if(values instanceof Array){ // array of objects
11293             for(var i = 0, len = values.length; i < len; i++){
11294                 var v = values[i];
11295                 var f = this.findField(v.id);
11296                 if(f){
11297                     f.setValue(v.value);
11298                     if(this.trackResetOnLoad){
11299                         f.originalValue = f.getValue();
11300                     }
11301                 }
11302             }
11303         }else{ // object hash
11304             var field, id;
11305             for(id in values){
11306                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11307
11308                     if (field.setFromData &&
11309                         field.valueField &&
11310                         field.displayField &&
11311                         // combos' with local stores can
11312                         // be queried via setValue()
11313                         // to set their value..
11314                         (field.store && !field.store.isLocal)
11315                         ) {
11316                         // it's a combo
11317                         var sd = { };
11318                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11319                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11320                         field.setFromData(sd);
11321
11322                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11323                         
11324                         field.setFromData(values);
11325                         
11326                     } else {
11327                         field.setValue(values[id]);
11328                     }
11329
11330
11331                     if(this.trackResetOnLoad){
11332                         field.originalValue = field.getValue();
11333                     }
11334                 }
11335             }
11336         }
11337
11338         //Roo.each(this.childForms || [], function (f) {
11339         //    f.setValues(values);
11340         //});
11341
11342         return this;
11343     },
11344
11345     /**
11346      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11347      * they are returned as an array.
11348      * @param {Boolean} asString
11349      * @return {Object}
11350      */
11351     getValues : function(asString){
11352         //if (this.childForms) {
11353             // copy values from the child forms
11354         //    Roo.each(this.childForms, function (f) {
11355         //        this.setValues(f.getValues());
11356         //    }, this);
11357         //}
11358
11359
11360
11361         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11362         if(asString === true){
11363             return fs;
11364         }
11365         return Roo.urlDecode(fs);
11366     },
11367
11368     /**
11369      * Returns the fields in this form as an object with key/value pairs.
11370      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11371      * @return {Object}
11372      */
11373     getFieldValues : function(with_hidden)
11374     {
11375         var items = this.getItems();
11376         var ret = {};
11377         items.each(function(f){
11378             
11379             if (!f.getName()) {
11380                 return;
11381             }
11382             
11383             var v = f.getValue();
11384             
11385             if (f.inputType =='radio') {
11386                 if (typeof(ret[f.getName()]) == 'undefined') {
11387                     ret[f.getName()] = ''; // empty..
11388                 }
11389
11390                 if (!f.el.dom.checked) {
11391                     return;
11392
11393                 }
11394                 v = f.el.dom.value;
11395
11396             }
11397             
11398             if(f.xtype == 'MoneyField'){
11399                 ret[f.currencyName] = f.getCurrency();
11400             }
11401
11402             // not sure if this supported any more..
11403             if ((typeof(v) == 'object') && f.getRawValue) {
11404                 v = f.getRawValue() ; // dates..
11405             }
11406             // combo boxes where name != hiddenName...
11407             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11408                 ret[f.name] = f.getRawValue();
11409             }
11410             ret[f.getName()] = v;
11411         });
11412
11413         return ret;
11414     },
11415
11416     /**
11417      * Clears all invalid messages in this form.
11418      * @return {BasicForm} this
11419      */
11420     clearInvalid : function(){
11421         var items = this.getItems();
11422
11423         items.each(function(f){
11424            f.clearInvalid();
11425         });
11426
11427         return this;
11428     },
11429
11430     /**
11431      * Resets this form.
11432      * @return {BasicForm} this
11433      */
11434     reset : function(){
11435         var items = this.getItems();
11436         items.each(function(f){
11437             f.reset();
11438         });
11439
11440         Roo.each(this.childForms || [], function (f) {
11441             f.reset();
11442         });
11443
11444
11445         return this;
11446     },
11447     
11448     getItems : function()
11449     {
11450         var r=new Roo.util.MixedCollection(false, function(o){
11451             return o.id || (o.id = Roo.id());
11452         });
11453         var iter = function(el) {
11454             if (el.inputEl) {
11455                 r.add(el);
11456             }
11457             if (!el.items) {
11458                 return;
11459             }
11460             Roo.each(el.items,function(e) {
11461                 iter(e);
11462             });
11463         };
11464
11465         iter(this);
11466         return r;
11467     },
11468     
11469     hideFields : function(items)
11470     {
11471         Roo.each(items, function(i){
11472             
11473             var f = this.findField(i);
11474             
11475             if(!f){
11476                 return;
11477             }
11478             
11479             f.hide();
11480             
11481         }, this);
11482     },
11483     
11484     showFields : function(items)
11485     {
11486         Roo.each(items, function(i){
11487             
11488             var f = this.findField(i);
11489             
11490             if(!f){
11491                 return;
11492             }
11493             
11494             f.show();
11495             
11496         }, this);
11497     }
11498
11499 });
11500
11501 Roo.apply(Roo.bootstrap.Form, {
11502     
11503     popover : {
11504         
11505         padding : 5,
11506         
11507         isApplied : false,
11508         
11509         isMasked : false,
11510         
11511         form : false,
11512         
11513         target : false,
11514         
11515         toolTip : false,
11516         
11517         intervalID : false,
11518         
11519         maskEl : false,
11520         
11521         apply : function()
11522         {
11523             if(this.isApplied){
11524                 return;
11525             }
11526             
11527             this.maskEl = {
11528                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11529                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11530                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11531                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11532             };
11533             
11534             this.maskEl.top.enableDisplayMode("block");
11535             this.maskEl.left.enableDisplayMode("block");
11536             this.maskEl.bottom.enableDisplayMode("block");
11537             this.maskEl.right.enableDisplayMode("block");
11538             
11539             this.toolTip = new Roo.bootstrap.Tooltip({
11540                 cls : 'roo-form-error-popover',
11541                 alignment : {
11542                     'left' : ['r-l', [-2,0], 'right'],
11543                     'right' : ['l-r', [2,0], 'left'],
11544                     'bottom' : ['tl-bl', [0,2], 'top'],
11545                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11546                 }
11547             });
11548             
11549             this.toolTip.render(Roo.get(document.body));
11550
11551             this.toolTip.el.enableDisplayMode("block");
11552             
11553             Roo.get(document.body).on('click', function(){
11554                 this.unmask();
11555             }, this);
11556             
11557             Roo.get(document.body).on('touchstart', function(){
11558                 this.unmask();
11559             }, this);
11560             
11561             this.isApplied = true
11562         },
11563         
11564         mask : function(form, target)
11565         {
11566             this.form = form;
11567             
11568             this.target = target;
11569             
11570             if(!this.form.errorMask || !target.el){
11571                 return;
11572             }
11573             
11574             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11575             
11576             Roo.log(scrollable);
11577             
11578             var ot = this.target.el.calcOffsetsTo(scrollable);
11579             
11580             var scrollTo = ot[1] - this.form.maskOffset;
11581             
11582             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11583             
11584             scrollable.scrollTo('top', scrollTo);
11585             
11586             var box = this.target.el.getBox();
11587             Roo.log(box);
11588             var zIndex = Roo.bootstrap.Modal.zIndex++;
11589
11590             
11591             this.maskEl.top.setStyle('position', 'absolute');
11592             this.maskEl.top.setStyle('z-index', zIndex);
11593             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11594             this.maskEl.top.setLeft(0);
11595             this.maskEl.top.setTop(0);
11596             this.maskEl.top.show();
11597             
11598             this.maskEl.left.setStyle('position', 'absolute');
11599             this.maskEl.left.setStyle('z-index', zIndex);
11600             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11601             this.maskEl.left.setLeft(0);
11602             this.maskEl.left.setTop(box.y - this.padding);
11603             this.maskEl.left.show();
11604
11605             this.maskEl.bottom.setStyle('position', 'absolute');
11606             this.maskEl.bottom.setStyle('z-index', zIndex);
11607             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11608             this.maskEl.bottom.setLeft(0);
11609             this.maskEl.bottom.setTop(box.bottom + this.padding);
11610             this.maskEl.bottom.show();
11611
11612             this.maskEl.right.setStyle('position', 'absolute');
11613             this.maskEl.right.setStyle('z-index', zIndex);
11614             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11615             this.maskEl.right.setLeft(box.right + this.padding);
11616             this.maskEl.right.setTop(box.y - this.padding);
11617             this.maskEl.right.show();
11618
11619             this.toolTip.bindEl = this.target.el;
11620
11621             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11622
11623             var tip = this.target.blankText;
11624
11625             if(this.target.getValue() !== '' ) {
11626                 
11627                 if (this.target.invalidText.length) {
11628                     tip = this.target.invalidText;
11629                 } else if (this.target.regexText.length){
11630                     tip = this.target.regexText;
11631                 }
11632             }
11633
11634             this.toolTip.show(tip);
11635
11636             this.intervalID = window.setInterval(function() {
11637                 Roo.bootstrap.Form.popover.unmask();
11638             }, 10000);
11639
11640             window.onwheel = function(){ return false;};
11641             
11642             (function(){ this.isMasked = true; }).defer(500, this);
11643             
11644         },
11645         
11646         unmask : function()
11647         {
11648             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11649                 return;
11650             }
11651             
11652             this.maskEl.top.setStyle('position', 'absolute');
11653             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11654             this.maskEl.top.hide();
11655
11656             this.maskEl.left.setStyle('position', 'absolute');
11657             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11658             this.maskEl.left.hide();
11659
11660             this.maskEl.bottom.setStyle('position', 'absolute');
11661             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11662             this.maskEl.bottom.hide();
11663
11664             this.maskEl.right.setStyle('position', 'absolute');
11665             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11666             this.maskEl.right.hide();
11667             
11668             this.toolTip.hide();
11669             
11670             this.toolTip.el.hide();
11671             
11672             window.onwheel = function(){ return true;};
11673             
11674             if(this.intervalID){
11675                 window.clearInterval(this.intervalID);
11676                 this.intervalID = false;
11677             }
11678             
11679             this.isMasked = false;
11680             
11681         }
11682         
11683     }
11684     
11685 });
11686
11687 /*
11688  * Based on:
11689  * Ext JS Library 1.1.1
11690  * Copyright(c) 2006-2007, Ext JS, LLC.
11691  *
11692  * Originally Released Under LGPL - original licence link has changed is not relivant.
11693  *
11694  * Fork - LGPL
11695  * <script type="text/javascript">
11696  */
11697 /**
11698  * @class Roo.form.VTypes
11699  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11700  * @singleton
11701  */
11702 Roo.form.VTypes = function(){
11703     // closure these in so they are only created once.
11704     var alpha = /^[a-zA-Z_]+$/;
11705     var alphanum = /^[a-zA-Z0-9_]+$/;
11706     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11707     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11708
11709     // All these messages and functions are configurable
11710     return {
11711         /**
11712          * The function used to validate email addresses
11713          * @param {String} value The email address
11714          */
11715         'email' : function(v){
11716             return email.test(v);
11717         },
11718         /**
11719          * The error text to display when the email validation function returns false
11720          * @type String
11721          */
11722         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11723         /**
11724          * The keystroke filter mask to be applied on email input
11725          * @type RegExp
11726          */
11727         'emailMask' : /[a-z0-9_\.\-@]/i,
11728
11729         /**
11730          * The function used to validate URLs
11731          * @param {String} value The URL
11732          */
11733         'url' : function(v){
11734             return url.test(v);
11735         },
11736         /**
11737          * The error text to display when the url validation function returns false
11738          * @type String
11739          */
11740         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11741         
11742         /**
11743          * The function used to validate alpha values
11744          * @param {String} value The value
11745          */
11746         'alpha' : function(v){
11747             return alpha.test(v);
11748         },
11749         /**
11750          * The error text to display when the alpha validation function returns false
11751          * @type String
11752          */
11753         'alphaText' : 'This field should only contain letters and _',
11754         /**
11755          * The keystroke filter mask to be applied on alpha input
11756          * @type RegExp
11757          */
11758         'alphaMask' : /[a-z_]/i,
11759
11760         /**
11761          * The function used to validate alphanumeric values
11762          * @param {String} value The value
11763          */
11764         'alphanum' : function(v){
11765             return alphanum.test(v);
11766         },
11767         /**
11768          * The error text to display when the alphanumeric validation function returns false
11769          * @type String
11770          */
11771         'alphanumText' : 'This field should only contain letters, numbers and _',
11772         /**
11773          * The keystroke filter mask to be applied on alphanumeric input
11774          * @type RegExp
11775          */
11776         'alphanumMask' : /[a-z0-9_]/i
11777     };
11778 }();/*
11779  * - LGPL
11780  *
11781  * Input
11782  * 
11783  */
11784
11785 /**
11786  * @class Roo.bootstrap.Input
11787  * @extends Roo.bootstrap.Component
11788  * Bootstrap Input class
11789  * @cfg {Boolean} disabled is it disabled
11790  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11791  * @cfg {String} name name of the input
11792  * @cfg {string} fieldLabel - the label associated
11793  * @cfg {string} placeholder - placeholder to put in text.
11794  * @cfg {string}  before - input group add on before
11795  * @cfg {string} after - input group add on after
11796  * @cfg {string} size - (lg|sm) or leave empty..
11797  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11798  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11799  * @cfg {Number} md colspan out of 12 for computer-sized screens
11800  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11801  * @cfg {string} value default value of the input
11802  * @cfg {Number} labelWidth set the width of label 
11803  * @cfg {Number} labellg set the width of label (1-12)
11804  * @cfg {Number} labelmd set the width of label (1-12)
11805  * @cfg {Number} labelsm set the width of label (1-12)
11806  * @cfg {Number} labelxs set the width of label (1-12)
11807  * @cfg {String} labelAlign (top|left)
11808  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11809  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11810  * @cfg {String} indicatorpos (left|right) default left
11811  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11812  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11813  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11814
11815  * @cfg {String} align (left|center|right) Default left
11816  * @cfg {Boolean} forceFeedback (true|false) Default false
11817  * 
11818  * @constructor
11819  * Create a new Input
11820  * @param {Object} config The config object
11821  */
11822
11823 Roo.bootstrap.Input = function(config){
11824     
11825     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11826     
11827     this.addEvents({
11828         /**
11829          * @event focus
11830          * Fires when this field receives input focus.
11831          * @param {Roo.form.Field} this
11832          */
11833         focus : true,
11834         /**
11835          * @event blur
11836          * Fires when this field loses input focus.
11837          * @param {Roo.form.Field} this
11838          */
11839         blur : true,
11840         /**
11841          * @event specialkey
11842          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11843          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11844          * @param {Roo.form.Field} this
11845          * @param {Roo.EventObject} e The event object
11846          */
11847         specialkey : true,
11848         /**
11849          * @event change
11850          * Fires just before the field blurs if the field value has changed.
11851          * @param {Roo.form.Field} this
11852          * @param {Mixed} newValue The new value
11853          * @param {Mixed} oldValue The original value
11854          */
11855         change : true,
11856         /**
11857          * @event invalid
11858          * Fires after the field has been marked as invalid.
11859          * @param {Roo.form.Field} this
11860          * @param {String} msg The validation message
11861          */
11862         invalid : true,
11863         /**
11864          * @event valid
11865          * Fires after the field has been validated with no errors.
11866          * @param {Roo.form.Field} this
11867          */
11868         valid : true,
11869          /**
11870          * @event keyup
11871          * Fires after the key up
11872          * @param {Roo.form.Field} this
11873          * @param {Roo.EventObject}  e The event Object
11874          */
11875         keyup : true,
11876         /**
11877          * @event paste
11878          * Fires after the user pastes into input
11879          * @param {Roo.form.Field} this
11880          * @param {Roo.EventObject}  e The event Object
11881          */
11882         paste : true
11883     });
11884 };
11885
11886 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11887      /**
11888      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11889       automatic validation (defaults to "keyup").
11890      */
11891     validationEvent : "keyup",
11892      /**
11893      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11894      */
11895     validateOnBlur : true,
11896     /**
11897      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11898      */
11899     validationDelay : 250,
11900      /**
11901      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11902      */
11903     focusClass : "x-form-focus",  // not needed???
11904     
11905        
11906     /**
11907      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11908      */
11909     invalidClass : "has-warning",
11910     
11911     /**
11912      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11913      */
11914     validClass : "has-success",
11915     
11916     /**
11917      * @cfg {Boolean} hasFeedback (true|false) default true
11918      */
11919     hasFeedback : true,
11920     
11921     /**
11922      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11923      */
11924     invalidFeedbackClass : "glyphicon-warning-sign",
11925     
11926     /**
11927      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11928      */
11929     validFeedbackClass : "glyphicon-ok",
11930     
11931     /**
11932      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11933      */
11934     selectOnFocus : false,
11935     
11936      /**
11937      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11938      */
11939     maskRe : null,
11940        /**
11941      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11942      */
11943     vtype : null,
11944     
11945       /**
11946      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11947      */
11948     disableKeyFilter : false,
11949     
11950        /**
11951      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11952      */
11953     disabled : false,
11954      /**
11955      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11956      */
11957     allowBlank : true,
11958     /**
11959      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11960      */
11961     blankText : "Please complete this mandatory field",
11962     
11963      /**
11964      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11965      */
11966     minLength : 0,
11967     /**
11968      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11969      */
11970     maxLength : Number.MAX_VALUE,
11971     /**
11972      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11973      */
11974     minLengthText : "The minimum length for this field is {0}",
11975     /**
11976      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11977      */
11978     maxLengthText : "The maximum length for this field is {0}",
11979   
11980     
11981     /**
11982      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11983      * If available, this function will be called only after the basic validators all return true, and will be passed the
11984      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11985      */
11986     validator : null,
11987     /**
11988      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11989      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11990      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11991      */
11992     regex : null,
11993     /**
11994      * @cfg {String} regexText -- Depricated - use Invalid Text
11995      */
11996     regexText : "",
11997     
11998     /**
11999      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12000      */
12001     invalidText : "",
12002     
12003     
12004     
12005     autocomplete: false,
12006     
12007     
12008     fieldLabel : '',
12009     inputType : 'text',
12010     
12011     name : false,
12012     placeholder: false,
12013     before : false,
12014     after : false,
12015     size : false,
12016     hasFocus : false,
12017     preventMark: false,
12018     isFormField : true,
12019     value : '',
12020     labelWidth : 2,
12021     labelAlign : false,
12022     readOnly : false,
12023     align : false,
12024     formatedValue : false,
12025     forceFeedback : false,
12026     
12027     indicatorpos : 'left',
12028     
12029     labellg : 0,
12030     labelmd : 0,
12031     labelsm : 0,
12032     labelxs : 0,
12033     
12034     capture : '',
12035     accept : '',
12036     
12037     parentLabelAlign : function()
12038     {
12039         var parent = this;
12040         while (parent.parent()) {
12041             parent = parent.parent();
12042             if (typeof(parent.labelAlign) !='undefined') {
12043                 return parent.labelAlign;
12044             }
12045         }
12046         return 'left';
12047         
12048     },
12049     
12050     getAutoCreate : function()
12051     {
12052         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12053         
12054         var id = Roo.id();
12055         
12056         var cfg = {};
12057         
12058         if(this.inputType != 'hidden'){
12059             cfg.cls = 'form-group' //input-group
12060         }
12061         
12062         var input =  {
12063             tag: 'input',
12064             id : id,
12065             type : this.inputType,
12066             value : this.value,
12067             cls : 'form-control',
12068             placeholder : this.placeholder || '',
12069             autocomplete : this.autocomplete || 'new-password'
12070         };
12071         if (this.inputType == 'file') {
12072             input.style = 'overflow:hidden'; // why not in CSS?
12073         }
12074         
12075         if(this.capture.length){
12076             input.capture = this.capture;
12077         }
12078         
12079         if(this.accept.length){
12080             input.accept = this.accept + "/*";
12081         }
12082         
12083         if(this.align){
12084             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12085         }
12086         
12087         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12088             input.maxLength = this.maxLength;
12089         }
12090         
12091         if (this.disabled) {
12092             input.disabled=true;
12093         }
12094         
12095         if (this.readOnly) {
12096             input.readonly=true;
12097         }
12098         
12099         if (this.name) {
12100             input.name = this.name;
12101         }
12102         
12103         if (this.size) {
12104             input.cls += ' input-' + this.size;
12105         }
12106         
12107         var settings=this;
12108         ['xs','sm','md','lg'].map(function(size){
12109             if (settings[size]) {
12110                 cfg.cls += ' col-' + size + '-' + settings[size];
12111             }
12112         });
12113         
12114         var inputblock = input;
12115         
12116         var feedback = {
12117             tag: 'span',
12118             cls: 'glyphicon form-control-feedback'
12119         };
12120             
12121         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12122             
12123             inputblock = {
12124                 cls : 'has-feedback',
12125                 cn :  [
12126                     input,
12127                     feedback
12128                 ] 
12129             };  
12130         }
12131         
12132         if (this.before || this.after) {
12133             
12134             inputblock = {
12135                 cls : 'input-group',
12136                 cn :  [] 
12137             };
12138             
12139             if (this.before && typeof(this.before) == 'string') {
12140                 
12141                 inputblock.cn.push({
12142                     tag :'span',
12143                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12144                     html : this.before
12145                 });
12146             }
12147             if (this.before && typeof(this.before) == 'object') {
12148                 this.before = Roo.factory(this.before);
12149                 
12150                 inputblock.cn.push({
12151                     tag :'span',
12152                     cls : 'roo-input-before input-group-prepend   input-group-' +
12153                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12154                 });
12155             }
12156             
12157             inputblock.cn.push(input);
12158             
12159             if (this.after && typeof(this.after) == 'string') {
12160                 inputblock.cn.push({
12161                     tag :'span',
12162                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12163                     html : this.after
12164                 });
12165             }
12166             if (this.after && typeof(this.after) == 'object') {
12167                 this.after = Roo.factory(this.after);
12168                 
12169                 inputblock.cn.push({
12170                     tag :'span',
12171                     cls : 'roo-input-after input-group-append  input-group-' +
12172                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12173                 });
12174             }
12175             
12176             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12177                 inputblock.cls += ' has-feedback';
12178                 inputblock.cn.push(feedback);
12179             }
12180         };
12181         var indicator = {
12182             tag : 'i',
12183             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12184             tooltip : 'This field is required'
12185         };
12186         if (this.allowBlank ) {
12187             indicator.style = this.allowBlank ? ' display:none' : '';
12188         }
12189         if (align ==='left' && this.fieldLabel.length) {
12190             
12191             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12192             
12193             cfg.cn = [
12194                 indicator,
12195                 {
12196                     tag: 'label',
12197                     'for' :  id,
12198                     cls : 'control-label col-form-label',
12199                     html : this.fieldLabel
12200
12201                 },
12202                 {
12203                     cls : "", 
12204                     cn: [
12205                         inputblock
12206                     ]
12207                 }
12208             ];
12209             
12210             var labelCfg = cfg.cn[1];
12211             var contentCfg = cfg.cn[2];
12212             
12213             if(this.indicatorpos == 'right'){
12214                 cfg.cn = [
12215                     {
12216                         tag: 'label',
12217                         'for' :  id,
12218                         cls : 'control-label col-form-label',
12219                         cn : [
12220                             {
12221                                 tag : 'span',
12222                                 html : this.fieldLabel
12223                             },
12224                             indicator
12225                         ]
12226                     },
12227                     {
12228                         cls : "",
12229                         cn: [
12230                             inputblock
12231                         ]
12232                     }
12233
12234                 ];
12235                 
12236                 labelCfg = cfg.cn[0];
12237                 contentCfg = cfg.cn[1];
12238             
12239             }
12240             
12241             if(this.labelWidth > 12){
12242                 labelCfg.style = "width: " + this.labelWidth + 'px';
12243             }
12244             
12245             if(this.labelWidth < 13 && this.labelmd == 0){
12246                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12247             }
12248             
12249             if(this.labellg > 0){
12250                 labelCfg.cls += ' col-lg-' + this.labellg;
12251                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12252             }
12253             
12254             if(this.labelmd > 0){
12255                 labelCfg.cls += ' col-md-' + this.labelmd;
12256                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12257             }
12258             
12259             if(this.labelsm > 0){
12260                 labelCfg.cls += ' col-sm-' + this.labelsm;
12261                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12262             }
12263             
12264             if(this.labelxs > 0){
12265                 labelCfg.cls += ' col-xs-' + this.labelxs;
12266                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12267             }
12268             
12269             
12270         } else if ( this.fieldLabel.length) {
12271                 
12272             
12273             
12274             cfg.cn = [
12275                 {
12276                     tag : 'i',
12277                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12278                     tooltip : 'This field is required',
12279                     style : this.allowBlank ? ' display:none' : '' 
12280                 },
12281                 {
12282                     tag: 'label',
12283                    //cls : 'input-group-addon',
12284                     html : this.fieldLabel
12285
12286                 },
12287
12288                inputblock
12289
12290            ];
12291            
12292            if(this.indicatorpos == 'right'){
12293        
12294                 cfg.cn = [
12295                     {
12296                         tag: 'label',
12297                        //cls : 'input-group-addon',
12298                         html : this.fieldLabel
12299
12300                     },
12301                     {
12302                         tag : 'i',
12303                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12304                         tooltip : 'This field is required',
12305                         style : this.allowBlank ? ' display:none' : '' 
12306                     },
12307
12308                    inputblock
12309
12310                ];
12311
12312             }
12313
12314         } else {
12315             
12316             cfg.cn = [
12317
12318                     inputblock
12319
12320             ];
12321                 
12322                 
12323         };
12324         
12325         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12326            cfg.cls += ' navbar-form';
12327         }
12328         
12329         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12330             // on BS4 we do this only if not form 
12331             cfg.cls += ' navbar-form';
12332             cfg.tag = 'li';
12333         }
12334         
12335         return cfg;
12336         
12337     },
12338     /**
12339      * return the real input element.
12340      */
12341     inputEl: function ()
12342     {
12343         return this.el.select('input.form-control',true).first();
12344     },
12345     
12346     tooltipEl : function()
12347     {
12348         return this.inputEl();
12349     },
12350     
12351     indicatorEl : function()
12352     {
12353         if (Roo.bootstrap.version == 4) {
12354             return false; // not enabled in v4 yet.
12355         }
12356         
12357         var indicator = this.el.select('i.roo-required-indicator',true).first();
12358         
12359         if(!indicator){
12360             return false;
12361         }
12362         
12363         return indicator;
12364         
12365     },
12366     
12367     setDisabled : function(v)
12368     {
12369         var i  = this.inputEl().dom;
12370         if (!v) {
12371             i.removeAttribute('disabled');
12372             return;
12373             
12374         }
12375         i.setAttribute('disabled','true');
12376     },
12377     initEvents : function()
12378     {
12379           
12380         this.inputEl().on("keydown" , this.fireKey,  this);
12381         this.inputEl().on("focus", this.onFocus,  this);
12382         this.inputEl().on("blur", this.onBlur,  this);
12383         
12384         this.inputEl().relayEvent('keyup', this);
12385         this.inputEl().relayEvent('paste', this);
12386         
12387         this.indicator = this.indicatorEl();
12388         
12389         if(this.indicator){
12390             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12391         }
12392  
12393         // reference to original value for reset
12394         this.originalValue = this.getValue();
12395         //Roo.form.TextField.superclass.initEvents.call(this);
12396         if(this.validationEvent == 'keyup'){
12397             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12398             this.inputEl().on('keyup', this.filterValidation, this);
12399         }
12400         else if(this.validationEvent !== false){
12401             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12402         }
12403         
12404         if(this.selectOnFocus){
12405             this.on("focus", this.preFocus, this);
12406             
12407         }
12408         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12409             this.inputEl().on("keypress", this.filterKeys, this);
12410         } else {
12411             this.inputEl().relayEvent('keypress', this);
12412         }
12413        /* if(this.grow){
12414             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12415             this.el.on("click", this.autoSize,  this);
12416         }
12417         */
12418         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12419             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12420         }
12421         
12422         if (typeof(this.before) == 'object') {
12423             this.before.render(this.el.select('.roo-input-before',true).first());
12424         }
12425         if (typeof(this.after) == 'object') {
12426             this.after.render(this.el.select('.roo-input-after',true).first());
12427         }
12428         
12429         this.inputEl().on('change', this.onChange, this);
12430         
12431     },
12432     filterValidation : function(e){
12433         if(!e.isNavKeyPress()){
12434             this.validationTask.delay(this.validationDelay);
12435         }
12436     },
12437      /**
12438      * Validates the field value
12439      * @return {Boolean} True if the value is valid, else false
12440      */
12441     validate : function(){
12442         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12443         if(this.disabled || this.validateValue(this.getRawValue())){
12444             this.markValid();
12445             return true;
12446         }
12447         
12448         this.markInvalid();
12449         return false;
12450     },
12451     
12452     
12453     /**
12454      * Validates a value according to the field's validation rules and marks the field as invalid
12455      * if the validation fails
12456      * @param {Mixed} value The value to validate
12457      * @return {Boolean} True if the value is valid, else false
12458      */
12459     validateValue : function(value)
12460     {
12461         if(this.getVisibilityEl().hasClass('hidden')){
12462             return true;
12463         }
12464         
12465         if(value.length < 1)  { // if it's blank
12466             if(this.allowBlank){
12467                 return true;
12468             }
12469             return false;
12470         }
12471         
12472         if(value.length < this.minLength){
12473             return false;
12474         }
12475         if(value.length > this.maxLength){
12476             return false;
12477         }
12478         if(this.vtype){
12479             var vt = Roo.form.VTypes;
12480             if(!vt[this.vtype](value, this)){
12481                 return false;
12482             }
12483         }
12484         if(typeof this.validator == "function"){
12485             var msg = this.validator(value);
12486             if(msg !== true){
12487                 return false;
12488             }
12489             if (typeof(msg) == 'string') {
12490                 this.invalidText = msg;
12491             }
12492         }
12493         
12494         if(this.regex && !this.regex.test(value)){
12495             return false;
12496         }
12497         
12498         return true;
12499     },
12500     
12501      // private
12502     fireKey : function(e){
12503         //Roo.log('field ' + e.getKey());
12504         if(e.isNavKeyPress()){
12505             this.fireEvent("specialkey", this, e);
12506         }
12507     },
12508     focus : function (selectText){
12509         if(this.rendered){
12510             this.inputEl().focus();
12511             if(selectText === true){
12512                 this.inputEl().dom.select();
12513             }
12514         }
12515         return this;
12516     } ,
12517     
12518     onFocus : function(){
12519         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12520            // this.el.addClass(this.focusClass);
12521         }
12522         if(!this.hasFocus){
12523             this.hasFocus = true;
12524             this.startValue = this.getValue();
12525             this.fireEvent("focus", this);
12526         }
12527     },
12528     
12529     beforeBlur : Roo.emptyFn,
12530
12531     
12532     // private
12533     onBlur : function(){
12534         this.beforeBlur();
12535         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12536             //this.el.removeClass(this.focusClass);
12537         }
12538         this.hasFocus = false;
12539         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12540             this.validate();
12541         }
12542         var v = this.getValue();
12543         if(String(v) !== String(this.startValue)){
12544             this.fireEvent('change', this, v, this.startValue);
12545         }
12546         this.fireEvent("blur", this);
12547     },
12548     
12549     onChange : function(e)
12550     {
12551         var v = this.getValue();
12552         if(String(v) !== String(this.startValue)){
12553             this.fireEvent('change', this, v, this.startValue);
12554         }
12555         
12556     },
12557     
12558     /**
12559      * Resets the current field value to the originally loaded value and clears any validation messages
12560      */
12561     reset : function(){
12562         this.setValue(this.originalValue);
12563         this.validate();
12564     },
12565      /**
12566      * Returns the name of the field
12567      * @return {Mixed} name The name field
12568      */
12569     getName: function(){
12570         return this.name;
12571     },
12572      /**
12573      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12574      * @return {Mixed} value The field value
12575      */
12576     getValue : function(){
12577         
12578         var v = this.inputEl().getValue();
12579         
12580         return v;
12581     },
12582     /**
12583      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12584      * @return {Mixed} value The field value
12585      */
12586     getRawValue : function(){
12587         var v = this.inputEl().getValue();
12588         
12589         return v;
12590     },
12591     
12592     /**
12593      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12594      * @param {Mixed} value The value to set
12595      */
12596     setRawValue : function(v){
12597         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12598     },
12599     
12600     selectText : function(start, end){
12601         var v = this.getRawValue();
12602         if(v.length > 0){
12603             start = start === undefined ? 0 : start;
12604             end = end === undefined ? v.length : end;
12605             var d = this.inputEl().dom;
12606             if(d.setSelectionRange){
12607                 d.setSelectionRange(start, end);
12608             }else if(d.createTextRange){
12609                 var range = d.createTextRange();
12610                 range.moveStart("character", start);
12611                 range.moveEnd("character", v.length-end);
12612                 range.select();
12613             }
12614         }
12615     },
12616     
12617     /**
12618      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12619      * @param {Mixed} value The value to set
12620      */
12621     setValue : function(v){
12622         this.value = v;
12623         if(this.rendered){
12624             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12625             this.validate();
12626         }
12627     },
12628     
12629     /*
12630     processValue : function(value){
12631         if(this.stripCharsRe){
12632             var newValue = value.replace(this.stripCharsRe, '');
12633             if(newValue !== value){
12634                 this.setRawValue(newValue);
12635                 return newValue;
12636             }
12637         }
12638         return value;
12639     },
12640   */
12641     preFocus : function(){
12642         
12643         if(this.selectOnFocus){
12644             this.inputEl().dom.select();
12645         }
12646     },
12647     filterKeys : function(e){
12648         var k = e.getKey();
12649         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12650             return;
12651         }
12652         var c = e.getCharCode(), cc = String.fromCharCode(c);
12653         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12654             return;
12655         }
12656         if(!this.maskRe.test(cc)){
12657             e.stopEvent();
12658         }
12659     },
12660      /**
12661      * Clear any invalid styles/messages for this field
12662      */
12663     clearInvalid : function(){
12664         
12665         if(!this.el || this.preventMark){ // not rendered
12666             return;
12667         }
12668         
12669         
12670         this.el.removeClass([this.invalidClass, 'is-invalid']);
12671         
12672         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12673             
12674             var feedback = this.el.select('.form-control-feedback', true).first();
12675             
12676             if(feedback){
12677                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12678             }
12679             
12680         }
12681         
12682         if(this.indicator){
12683             this.indicator.removeClass('visible');
12684             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12685         }
12686         
12687         this.fireEvent('valid', this);
12688     },
12689     
12690      /**
12691      * Mark this field as valid
12692      */
12693     markValid : function()
12694     {
12695         if(!this.el  || this.preventMark){ // not rendered...
12696             return;
12697         }
12698         
12699         this.el.removeClass([this.invalidClass, this.validClass]);
12700         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12701
12702         var feedback = this.el.select('.form-control-feedback', true).first();
12703             
12704         if(feedback){
12705             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12706         }
12707         
12708         if(this.indicator){
12709             this.indicator.removeClass('visible');
12710             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12711         }
12712         
12713         if(this.disabled){
12714             return;
12715         }
12716         
12717            
12718         if(this.allowBlank && !this.getRawValue().length){
12719             return;
12720         }
12721         if (Roo.bootstrap.version == 3) {
12722             this.el.addClass(this.validClass);
12723         } else {
12724             this.inputEl().addClass('is-valid');
12725         }
12726
12727         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12728             
12729             var feedback = this.el.select('.form-control-feedback', true).first();
12730             
12731             if(feedback){
12732                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12733                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12734             }
12735             
12736         }
12737         
12738         this.fireEvent('valid', this);
12739     },
12740     
12741      /**
12742      * Mark this field as invalid
12743      * @param {String} msg The validation message
12744      */
12745     markInvalid : function(msg)
12746     {
12747         if(!this.el  || this.preventMark){ // not rendered
12748             return;
12749         }
12750         
12751         this.el.removeClass([this.invalidClass, this.validClass]);
12752         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12753         
12754         var feedback = this.el.select('.form-control-feedback', true).first();
12755             
12756         if(feedback){
12757             this.el.select('.form-control-feedback', true).first().removeClass(
12758                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12759         }
12760
12761         if(this.disabled){
12762             return;
12763         }
12764         
12765         if(this.allowBlank && !this.getRawValue().length){
12766             return;
12767         }
12768         
12769         if(this.indicator){
12770             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12771             this.indicator.addClass('visible');
12772         }
12773         if (Roo.bootstrap.version == 3) {
12774             this.el.addClass(this.invalidClass);
12775         } else {
12776             this.inputEl().addClass('is-invalid');
12777         }
12778         
12779         
12780         
12781         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12782             
12783             var feedback = this.el.select('.form-control-feedback', true).first();
12784             
12785             if(feedback){
12786                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12787                 
12788                 if(this.getValue().length || this.forceFeedback){
12789                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12790                 }
12791                 
12792             }
12793             
12794         }
12795         
12796         this.fireEvent('invalid', this, msg);
12797     },
12798     // private
12799     SafariOnKeyDown : function(event)
12800     {
12801         // this is a workaround for a password hang bug on chrome/ webkit.
12802         if (this.inputEl().dom.type != 'password') {
12803             return;
12804         }
12805         
12806         var isSelectAll = false;
12807         
12808         if(this.inputEl().dom.selectionEnd > 0){
12809             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12810         }
12811         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12812             event.preventDefault();
12813             this.setValue('');
12814             return;
12815         }
12816         
12817         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12818             
12819             event.preventDefault();
12820             // this is very hacky as keydown always get's upper case.
12821             //
12822             var cc = String.fromCharCode(event.getCharCode());
12823             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12824             
12825         }
12826     },
12827     adjustWidth : function(tag, w){
12828         tag = tag.toLowerCase();
12829         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12830             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12831                 if(tag == 'input'){
12832                     return w + 2;
12833                 }
12834                 if(tag == 'textarea'){
12835                     return w-2;
12836                 }
12837             }else if(Roo.isOpera){
12838                 if(tag == 'input'){
12839                     return w + 2;
12840                 }
12841                 if(tag == 'textarea'){
12842                     return w-2;
12843                 }
12844             }
12845         }
12846         return w;
12847     },
12848     
12849     setFieldLabel : function(v)
12850     {
12851         if(!this.rendered){
12852             return;
12853         }
12854         
12855         if(this.indicatorEl()){
12856             var ar = this.el.select('label > span',true);
12857             
12858             if (ar.elements.length) {
12859                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12860                 this.fieldLabel = v;
12861                 return;
12862             }
12863             
12864             var br = this.el.select('label',true);
12865             
12866             if(br.elements.length) {
12867                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12868                 this.fieldLabel = v;
12869                 return;
12870             }
12871             
12872             Roo.log('Cannot Found any of label > span || label in input');
12873             return;
12874         }
12875         
12876         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12877         this.fieldLabel = v;
12878         
12879         
12880     }
12881 });
12882
12883  
12884 /*
12885  * - LGPL
12886  *
12887  * Input
12888  * 
12889  */
12890
12891 /**
12892  * @class Roo.bootstrap.TextArea
12893  * @extends Roo.bootstrap.Input
12894  * Bootstrap TextArea class
12895  * @cfg {Number} cols Specifies the visible width of a text area
12896  * @cfg {Number} rows Specifies the visible number of lines in a text area
12897  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12898  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12899  * @cfg {string} html text
12900  * 
12901  * @constructor
12902  * Create a new TextArea
12903  * @param {Object} config The config object
12904  */
12905
12906 Roo.bootstrap.TextArea = function(config){
12907     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12908    
12909 };
12910
12911 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12912      
12913     cols : false,
12914     rows : 5,
12915     readOnly : false,
12916     warp : 'soft',
12917     resize : false,
12918     value: false,
12919     html: false,
12920     
12921     getAutoCreate : function(){
12922         
12923         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12924         
12925         var id = Roo.id();
12926         
12927         var cfg = {};
12928         
12929         if(this.inputType != 'hidden'){
12930             cfg.cls = 'form-group' //input-group
12931         }
12932         
12933         var input =  {
12934             tag: 'textarea',
12935             id : id,
12936             warp : this.warp,
12937             rows : this.rows,
12938             value : this.value || '',
12939             html: this.html || '',
12940             cls : 'form-control',
12941             placeholder : this.placeholder || '' 
12942             
12943         };
12944         
12945         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12946             input.maxLength = this.maxLength;
12947         }
12948         
12949         if(this.resize){
12950             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12951         }
12952         
12953         if(this.cols){
12954             input.cols = this.cols;
12955         }
12956         
12957         if (this.readOnly) {
12958             input.readonly = true;
12959         }
12960         
12961         if (this.name) {
12962             input.name = this.name;
12963         }
12964         
12965         if (this.size) {
12966             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12967         }
12968         
12969         var settings=this;
12970         ['xs','sm','md','lg'].map(function(size){
12971             if (settings[size]) {
12972                 cfg.cls += ' col-' + size + '-' + settings[size];
12973             }
12974         });
12975         
12976         var inputblock = input;
12977         
12978         if(this.hasFeedback && !this.allowBlank){
12979             
12980             var feedback = {
12981                 tag: 'span',
12982                 cls: 'glyphicon form-control-feedback'
12983             };
12984
12985             inputblock = {
12986                 cls : 'has-feedback',
12987                 cn :  [
12988                     input,
12989                     feedback
12990                 ] 
12991             };  
12992         }
12993         
12994         
12995         if (this.before || this.after) {
12996             
12997             inputblock = {
12998                 cls : 'input-group',
12999                 cn :  [] 
13000             };
13001             if (this.before) {
13002                 inputblock.cn.push({
13003                     tag :'span',
13004                     cls : 'input-group-addon',
13005                     html : this.before
13006                 });
13007             }
13008             
13009             inputblock.cn.push(input);
13010             
13011             if(this.hasFeedback && !this.allowBlank){
13012                 inputblock.cls += ' has-feedback';
13013                 inputblock.cn.push(feedback);
13014             }
13015             
13016             if (this.after) {
13017                 inputblock.cn.push({
13018                     tag :'span',
13019                     cls : 'input-group-addon',
13020                     html : this.after
13021                 });
13022             }
13023             
13024         }
13025         
13026         if (align ==='left' && this.fieldLabel.length) {
13027             cfg.cn = [
13028                 {
13029                     tag: 'label',
13030                     'for' :  id,
13031                     cls : 'control-label',
13032                     html : this.fieldLabel
13033                 },
13034                 {
13035                     cls : "",
13036                     cn: [
13037                         inputblock
13038                     ]
13039                 }
13040
13041             ];
13042             
13043             if(this.labelWidth > 12){
13044                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13045             }
13046
13047             if(this.labelWidth < 13 && this.labelmd == 0){
13048                 this.labelmd = this.labelWidth;
13049             }
13050
13051             if(this.labellg > 0){
13052                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13053                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13054             }
13055
13056             if(this.labelmd > 0){
13057                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13058                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13059             }
13060
13061             if(this.labelsm > 0){
13062                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13063                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13064             }
13065
13066             if(this.labelxs > 0){
13067                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13068                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13069             }
13070             
13071         } else if ( this.fieldLabel.length) {
13072             cfg.cn = [
13073
13074                {
13075                    tag: 'label',
13076                    //cls : 'input-group-addon',
13077                    html : this.fieldLabel
13078
13079                },
13080
13081                inputblock
13082
13083            ];
13084
13085         } else {
13086
13087             cfg.cn = [
13088
13089                 inputblock
13090
13091             ];
13092                 
13093         }
13094         
13095         if (this.disabled) {
13096             input.disabled=true;
13097         }
13098         
13099         return cfg;
13100         
13101     },
13102     /**
13103      * return the real textarea element.
13104      */
13105     inputEl: function ()
13106     {
13107         return this.el.select('textarea.form-control',true).first();
13108     },
13109     
13110     /**
13111      * Clear any invalid styles/messages for this field
13112      */
13113     clearInvalid : function()
13114     {
13115         
13116         if(!this.el || this.preventMark){ // not rendered
13117             return;
13118         }
13119         
13120         var label = this.el.select('label', true).first();
13121         var icon = this.el.select('i.fa-star', true).first();
13122         
13123         if(label && icon){
13124             icon.remove();
13125         }
13126         this.el.removeClass( this.validClass);
13127         this.inputEl().removeClass('is-invalid');
13128          
13129         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13130             
13131             var feedback = this.el.select('.form-control-feedback', true).first();
13132             
13133             if(feedback){
13134                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13135             }
13136             
13137         }
13138         
13139         this.fireEvent('valid', this);
13140     },
13141     
13142      /**
13143      * Mark this field as valid
13144      */
13145     markValid : function()
13146     {
13147         if(!this.el  || this.preventMark){ // not rendered
13148             return;
13149         }
13150         
13151         this.el.removeClass([this.invalidClass, this.validClass]);
13152         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13153         
13154         var feedback = this.el.select('.form-control-feedback', true).first();
13155             
13156         if(feedback){
13157             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13158         }
13159
13160         if(this.disabled || this.allowBlank){
13161             return;
13162         }
13163         
13164         var label = this.el.select('label', true).first();
13165         var icon = this.el.select('i.fa-star', true).first();
13166         
13167         if(label && icon){
13168             icon.remove();
13169         }
13170         if (Roo.bootstrap.version == 3) {
13171             this.el.addClass(this.validClass);
13172         } else {
13173             this.inputEl().addClass('is-valid');
13174         }
13175         
13176         
13177         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13178             
13179             var feedback = this.el.select('.form-control-feedback', true).first();
13180             
13181             if(feedback){
13182                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13183                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13184             }
13185             
13186         }
13187         
13188         this.fireEvent('valid', this);
13189     },
13190     
13191      /**
13192      * Mark this field as invalid
13193      * @param {String} msg The validation message
13194      */
13195     markInvalid : function(msg)
13196     {
13197         if(!this.el  || this.preventMark){ // not rendered
13198             return;
13199         }
13200         
13201         this.el.removeClass([this.invalidClass, this.validClass]);
13202         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13203         
13204         var feedback = this.el.select('.form-control-feedback', true).first();
13205             
13206         if(feedback){
13207             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13208         }
13209
13210         if(this.disabled || this.allowBlank){
13211             return;
13212         }
13213         
13214         var label = this.el.select('label', true).first();
13215         var icon = this.el.select('i.fa-star', true).first();
13216         
13217         if(!this.getValue().length && label && !icon){
13218             this.el.createChild({
13219                 tag : 'i',
13220                 cls : 'text-danger fa fa-lg fa-star',
13221                 tooltip : 'This field is required',
13222                 style : 'margin-right:5px;'
13223             }, label, true);
13224         }
13225         
13226         if (Roo.bootstrap.version == 3) {
13227             this.el.addClass(this.invalidClass);
13228         } else {
13229             this.inputEl().addClass('is-invalid');
13230         }
13231         
13232         // fixme ... this may be depricated need to test..
13233         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13234             
13235             var feedback = this.el.select('.form-control-feedback', true).first();
13236             
13237             if(feedback){
13238                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13239                 
13240                 if(this.getValue().length || this.forceFeedback){
13241                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13242                 }
13243                 
13244             }
13245             
13246         }
13247         
13248         this.fireEvent('invalid', this, msg);
13249     }
13250 });
13251
13252  
13253 /*
13254  * - LGPL
13255  *
13256  * trigger field - base class for combo..
13257  * 
13258  */
13259  
13260 /**
13261  * @class Roo.bootstrap.TriggerField
13262  * @extends Roo.bootstrap.Input
13263  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13264  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13265  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13266  * for which you can provide a custom implementation.  For example:
13267  * <pre><code>
13268 var trigger = new Roo.bootstrap.TriggerField();
13269 trigger.onTriggerClick = myTriggerFn;
13270 trigger.applyTo('my-field');
13271 </code></pre>
13272  *
13273  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13274  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13275  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13276  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13277  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13278
13279  * @constructor
13280  * Create a new TriggerField.
13281  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13282  * to the base TextField)
13283  */
13284 Roo.bootstrap.TriggerField = function(config){
13285     this.mimicing = false;
13286     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13287 };
13288
13289 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13290     /**
13291      * @cfg {String} triggerClass A CSS class to apply to the trigger
13292      */
13293      /**
13294      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13295      */
13296     hideTrigger:false,
13297
13298     /**
13299      * @cfg {Boolean} removable (true|false) special filter default false
13300      */
13301     removable : false,
13302     
13303     /** @cfg {Boolean} grow @hide */
13304     /** @cfg {Number} growMin @hide */
13305     /** @cfg {Number} growMax @hide */
13306
13307     /**
13308      * @hide 
13309      * @method
13310      */
13311     autoSize: Roo.emptyFn,
13312     // private
13313     monitorTab : true,
13314     // private
13315     deferHeight : true,
13316
13317     
13318     actionMode : 'wrap',
13319     
13320     caret : false,
13321     
13322     
13323     getAutoCreate : function(){
13324        
13325         var align = this.labelAlign || this.parentLabelAlign();
13326         
13327         var id = Roo.id();
13328         
13329         var cfg = {
13330             cls: 'form-group' //input-group
13331         };
13332         
13333         
13334         var input =  {
13335             tag: 'input',
13336             id : id,
13337             type : this.inputType,
13338             cls : 'form-control',
13339             autocomplete: 'new-password',
13340             placeholder : this.placeholder || '' 
13341             
13342         };
13343         if (this.name) {
13344             input.name = this.name;
13345         }
13346         if (this.size) {
13347             input.cls += ' input-' + this.size;
13348         }
13349         
13350         if (this.disabled) {
13351             input.disabled=true;
13352         }
13353         
13354         var inputblock = input;
13355         
13356         if(this.hasFeedback && !this.allowBlank){
13357             
13358             var feedback = {
13359                 tag: 'span',
13360                 cls: 'glyphicon form-control-feedback'
13361             };
13362             
13363             if(this.removable && !this.editable  ){
13364                 inputblock = {
13365                     cls : 'has-feedback',
13366                     cn :  [
13367                         inputblock,
13368                         {
13369                             tag: 'button',
13370                             html : 'x',
13371                             cls : 'roo-combo-removable-btn close'
13372                         },
13373                         feedback
13374                     ] 
13375                 };
13376             } else {
13377                 inputblock = {
13378                     cls : 'has-feedback',
13379                     cn :  [
13380                         inputblock,
13381                         feedback
13382                     ] 
13383                 };
13384             }
13385
13386         } else {
13387             if(this.removable && !this.editable ){
13388                 inputblock = {
13389                     cls : 'roo-removable',
13390                     cn :  [
13391                         inputblock,
13392                         {
13393                             tag: 'button',
13394                             html : 'x',
13395                             cls : 'roo-combo-removable-btn close'
13396                         }
13397                     ] 
13398                 };
13399             }
13400         }
13401         
13402         if (this.before || this.after) {
13403             
13404             inputblock = {
13405                 cls : 'input-group',
13406                 cn :  [] 
13407             };
13408             if (this.before) {
13409                 inputblock.cn.push({
13410                     tag :'span',
13411                     cls : 'input-group-addon input-group-prepend input-group-text',
13412                     html : this.before
13413                 });
13414             }
13415             
13416             inputblock.cn.push(input);
13417             
13418             if(this.hasFeedback && !this.allowBlank){
13419                 inputblock.cls += ' has-feedback';
13420                 inputblock.cn.push(feedback);
13421             }
13422             
13423             if (this.after) {
13424                 inputblock.cn.push({
13425                     tag :'span',
13426                     cls : 'input-group-addon input-group-append input-group-text',
13427                     html : this.after
13428                 });
13429             }
13430             
13431         };
13432         
13433       
13434         
13435         var ibwrap = inputblock;
13436         
13437         if(this.multiple){
13438             ibwrap = {
13439                 tag: 'ul',
13440                 cls: 'roo-select2-choices',
13441                 cn:[
13442                     {
13443                         tag: 'li',
13444                         cls: 'roo-select2-search-field',
13445                         cn: [
13446
13447                             inputblock
13448                         ]
13449                     }
13450                 ]
13451             };
13452                 
13453         }
13454         
13455         var combobox = {
13456             cls: 'roo-select2-container input-group',
13457             cn: [
13458                  {
13459                     tag: 'input',
13460                     type : 'hidden',
13461                     cls: 'form-hidden-field'
13462                 },
13463                 ibwrap
13464             ]
13465         };
13466         
13467         if(!this.multiple && this.showToggleBtn){
13468             
13469             var caret = {
13470                         tag: 'span',
13471                         cls: 'caret'
13472              };
13473             if (this.caret != false) {
13474                 caret = {
13475                      tag: 'i',
13476                      cls: 'fa fa-' + this.caret
13477                 };
13478                 
13479             }
13480             
13481             combobox.cn.push({
13482                 tag :'span',
13483                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13484                 cn : [
13485                     Roo.bootstrap.version == 3 ? caret : '',
13486                     {
13487                         tag: 'span',
13488                         cls: 'combobox-clear',
13489                         cn  : [
13490                             {
13491                                 tag : 'i',
13492                                 cls: 'icon-remove'
13493                             }
13494                         ]
13495                     }
13496                 ]
13497
13498             })
13499         }
13500         
13501         if(this.multiple){
13502             combobox.cls += ' roo-select2-container-multi';
13503         }
13504          var indicator = {
13505             tag : 'i',
13506             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13507             tooltip : 'This field is required'
13508         };
13509         if (Roo.bootstrap.version == 4) {
13510             indicator = {
13511                 tag : 'i',
13512                 style : 'display:none'
13513             };
13514         }
13515         
13516         
13517         if (align ==='left' && this.fieldLabel.length) {
13518             
13519             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13520
13521             cfg.cn = [
13522                 indicator,
13523                 {
13524                     tag: 'label',
13525                     'for' :  id,
13526                     cls : 'control-label',
13527                     html : this.fieldLabel
13528
13529                 },
13530                 {
13531                     cls : "", 
13532                     cn: [
13533                         combobox
13534                     ]
13535                 }
13536
13537             ];
13538             
13539             var labelCfg = cfg.cn[1];
13540             var contentCfg = cfg.cn[2];
13541             
13542             if(this.indicatorpos == 'right'){
13543                 cfg.cn = [
13544                     {
13545                         tag: 'label',
13546                         'for' :  id,
13547                         cls : 'control-label',
13548                         cn : [
13549                             {
13550                                 tag : 'span',
13551                                 html : this.fieldLabel
13552                             },
13553                             indicator
13554                         ]
13555                     },
13556                     {
13557                         cls : "", 
13558                         cn: [
13559                             combobox
13560                         ]
13561                     }
13562
13563                 ];
13564                 
13565                 labelCfg = cfg.cn[0];
13566                 contentCfg = cfg.cn[1];
13567             }
13568             
13569             if(this.labelWidth > 12){
13570                 labelCfg.style = "width: " + this.labelWidth + 'px';
13571             }
13572             
13573             if(this.labelWidth < 13 && this.labelmd == 0){
13574                 this.labelmd = this.labelWidth;
13575             }
13576             
13577             if(this.labellg > 0){
13578                 labelCfg.cls += ' col-lg-' + this.labellg;
13579                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13580             }
13581             
13582             if(this.labelmd > 0){
13583                 labelCfg.cls += ' col-md-' + this.labelmd;
13584                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13585             }
13586             
13587             if(this.labelsm > 0){
13588                 labelCfg.cls += ' col-sm-' + this.labelsm;
13589                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13590             }
13591             
13592             if(this.labelxs > 0){
13593                 labelCfg.cls += ' col-xs-' + this.labelxs;
13594                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13595             }
13596             
13597         } else if ( this.fieldLabel.length) {
13598 //                Roo.log(" label");
13599             cfg.cn = [
13600                 indicator,
13601                {
13602                    tag: 'label',
13603                    //cls : 'input-group-addon',
13604                    html : this.fieldLabel
13605
13606                },
13607
13608                combobox
13609
13610             ];
13611             
13612             if(this.indicatorpos == 'right'){
13613                 
13614                 cfg.cn = [
13615                     {
13616                        tag: 'label',
13617                        cn : [
13618                            {
13619                                tag : 'span',
13620                                html : this.fieldLabel
13621                            },
13622                            indicator
13623                        ]
13624
13625                     },
13626                     combobox
13627
13628                 ];
13629
13630             }
13631
13632         } else {
13633             
13634 //                Roo.log(" no label && no align");
13635                 cfg = combobox
13636                      
13637                 
13638         }
13639         
13640         var settings=this;
13641         ['xs','sm','md','lg'].map(function(size){
13642             if (settings[size]) {
13643                 cfg.cls += ' col-' + size + '-' + settings[size];
13644             }
13645         });
13646         
13647         return cfg;
13648         
13649     },
13650     
13651     
13652     
13653     // private
13654     onResize : function(w, h){
13655 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13656 //        if(typeof w == 'number'){
13657 //            var x = w - this.trigger.getWidth();
13658 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13659 //            this.trigger.setStyle('left', x+'px');
13660 //        }
13661     },
13662
13663     // private
13664     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13665
13666     // private
13667     getResizeEl : function(){
13668         return this.inputEl();
13669     },
13670
13671     // private
13672     getPositionEl : function(){
13673         return this.inputEl();
13674     },
13675
13676     // private
13677     alignErrorIcon : function(){
13678         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13679     },
13680
13681     // private
13682     initEvents : function(){
13683         
13684         this.createList();
13685         
13686         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13687         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13688         if(!this.multiple && this.showToggleBtn){
13689             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13690             if(this.hideTrigger){
13691                 this.trigger.setDisplayed(false);
13692             }
13693             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13694         }
13695         
13696         if(this.multiple){
13697             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13698         }
13699         
13700         if(this.removable && !this.editable && !this.tickable){
13701             var close = this.closeTriggerEl();
13702             
13703             if(close){
13704                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13705                 close.on('click', this.removeBtnClick, this, close);
13706             }
13707         }
13708         
13709         //this.trigger.addClassOnOver('x-form-trigger-over');
13710         //this.trigger.addClassOnClick('x-form-trigger-click');
13711         
13712         //if(!this.width){
13713         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13714         //}
13715     },
13716     
13717     closeTriggerEl : function()
13718     {
13719         var close = this.el.select('.roo-combo-removable-btn', true).first();
13720         return close ? close : false;
13721     },
13722     
13723     removeBtnClick : function(e, h, el)
13724     {
13725         e.preventDefault();
13726         
13727         if(this.fireEvent("remove", this) !== false){
13728             this.reset();
13729             this.fireEvent("afterremove", this)
13730         }
13731     },
13732     
13733     createList : function()
13734     {
13735         this.list = Roo.get(document.body).createChild({
13736             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13737             cls: 'typeahead typeahead-long dropdown-menu shadow',
13738             style: 'display:none'
13739         });
13740         
13741         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13742         
13743     },
13744
13745     // private
13746     initTrigger : function(){
13747        
13748     },
13749
13750     // private
13751     onDestroy : function(){
13752         if(this.trigger){
13753             this.trigger.removeAllListeners();
13754           //  this.trigger.remove();
13755         }
13756         //if(this.wrap){
13757         //    this.wrap.remove();
13758         //}
13759         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13760     },
13761
13762     // private
13763     onFocus : function(){
13764         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13765         /*
13766         if(!this.mimicing){
13767             this.wrap.addClass('x-trigger-wrap-focus');
13768             this.mimicing = true;
13769             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13770             if(this.monitorTab){
13771                 this.el.on("keydown", this.checkTab, this);
13772             }
13773         }
13774         */
13775     },
13776
13777     // private
13778     checkTab : function(e){
13779         if(e.getKey() == e.TAB){
13780             this.triggerBlur();
13781         }
13782     },
13783
13784     // private
13785     onBlur : function(){
13786         // do nothing
13787     },
13788
13789     // private
13790     mimicBlur : function(e, t){
13791         /*
13792         if(!this.wrap.contains(t) && this.validateBlur()){
13793             this.triggerBlur();
13794         }
13795         */
13796     },
13797
13798     // private
13799     triggerBlur : function(){
13800         this.mimicing = false;
13801         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13802         if(this.monitorTab){
13803             this.el.un("keydown", this.checkTab, this);
13804         }
13805         //this.wrap.removeClass('x-trigger-wrap-focus');
13806         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13807     },
13808
13809     // private
13810     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13811     validateBlur : function(e, t){
13812         return true;
13813     },
13814
13815     // private
13816     onDisable : function(){
13817         this.inputEl().dom.disabled = true;
13818         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13819         //if(this.wrap){
13820         //    this.wrap.addClass('x-item-disabled');
13821         //}
13822     },
13823
13824     // private
13825     onEnable : function(){
13826         this.inputEl().dom.disabled = false;
13827         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13828         //if(this.wrap){
13829         //    this.el.removeClass('x-item-disabled');
13830         //}
13831     },
13832
13833     // private
13834     onShow : function(){
13835         var ae = this.getActionEl();
13836         
13837         if(ae){
13838             ae.dom.style.display = '';
13839             ae.dom.style.visibility = 'visible';
13840         }
13841     },
13842
13843     // private
13844     
13845     onHide : function(){
13846         var ae = this.getActionEl();
13847         ae.dom.style.display = 'none';
13848     },
13849
13850     /**
13851      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13852      * by an implementing function.
13853      * @method
13854      * @param {EventObject} e
13855      */
13856     onTriggerClick : Roo.emptyFn
13857 });
13858  
13859 /*
13860 * Licence: LGPL
13861 */
13862
13863 /**
13864  * @class Roo.bootstrap.CardUploader
13865  * @extends Roo.bootstrap.Button
13866  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13867  * @cfg {Number} errorTimeout default 3000
13868  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13869  * @cfg {Array}  html The button text.
13870
13871  *
13872  * @constructor
13873  * Create a new CardUploader
13874  * @param {Object} config The config object
13875  */
13876
13877 Roo.bootstrap.CardUploader = function(config){
13878     
13879  
13880     
13881     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13882     
13883     
13884     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13885         return r.data.id
13886      });
13887     
13888      this.addEvents({
13889          // raw events
13890         /**
13891          * @event preview
13892          * When a image is clicked on - and needs to display a slideshow or similar..
13893          * @param {Roo.bootstrap.Card} this
13894          * @param {Object} The image information data 
13895          *
13896          */
13897         'preview' : true,
13898          /**
13899          * @event download
13900          * When a the download link is clicked
13901          * @param {Roo.bootstrap.Card} this
13902          * @param {Object} The image information data  contains 
13903          */
13904         'download' : true
13905         
13906     });
13907 };
13908  
13909 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13910     
13911      
13912     errorTimeout : 3000,
13913      
13914     images : false,
13915    
13916     fileCollection : false,
13917     allowBlank : true,
13918     
13919     getAutoCreate : function()
13920     {
13921         
13922         var cfg =  {
13923             cls :'form-group' ,
13924             cn : [
13925                
13926                 {
13927                     tag: 'label',
13928                    //cls : 'input-group-addon',
13929                     html : this.fieldLabel
13930
13931                 },
13932
13933                 {
13934                     tag: 'input',
13935                     type : 'hidden',
13936                     name : this.name,
13937                     value : this.value,
13938                     cls : 'd-none  form-control'
13939                 },
13940                 
13941                 {
13942                     tag: 'input',
13943                     multiple : 'multiple',
13944                     type : 'file',
13945                     cls : 'd-none  roo-card-upload-selector'
13946                 },
13947                 
13948                 {
13949                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13950                 },
13951                 {
13952                     cls : 'card-columns roo-card-uploader-container'
13953                 }
13954
13955             ]
13956         };
13957            
13958          
13959         return cfg;
13960     },
13961     
13962     getChildContainer : function() /// what children are added to.
13963     {
13964         return this.containerEl;
13965     },
13966    
13967     getButtonContainer : function() /// what children are added to.
13968     {
13969         return this.el.select(".roo-card-uploader-button-container").first();
13970     },
13971    
13972     initEvents : function()
13973     {
13974         
13975         Roo.bootstrap.Input.prototype.initEvents.call(this);
13976         
13977         var t = this;
13978         this.addxtype({
13979             xns: Roo.bootstrap,
13980
13981             xtype : 'Button',
13982             container_method : 'getButtonContainer' ,            
13983             html :  this.html, // fix changable?
13984             cls : 'w-100 ',
13985             listeners : {
13986                 'click' : function(btn, e) {
13987                     t.onClick(e);
13988                 }
13989             }
13990         });
13991         
13992         
13993         
13994         
13995         this.urlAPI = (window.createObjectURL && window) || 
13996                                 (window.URL && URL.revokeObjectURL && URL) || 
13997                                 (window.webkitURL && webkitURL);
13998                         
13999          
14000          
14001          
14002         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14003         
14004         this.selectorEl.on('change', this.onFileSelected, this);
14005         if (this.images) {
14006             var t = this;
14007             this.images.forEach(function(img) {
14008                 t.addCard(img)
14009             });
14010             this.images = false;
14011         }
14012         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14013          
14014        
14015     },
14016     
14017    
14018     onClick : function(e)
14019     {
14020         e.preventDefault();
14021          
14022         this.selectorEl.dom.click();
14023          
14024     },
14025     
14026     onFileSelected : function(e)
14027     {
14028         e.preventDefault();
14029         
14030         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14031             return;
14032         }
14033         
14034         Roo.each(this.selectorEl.dom.files, function(file){    
14035             this.addFile(file);
14036         }, this);
14037          
14038     },
14039     
14040       
14041     
14042       
14043     
14044     addFile : function(file)
14045     {
14046            
14047         if(typeof(file) === 'string'){
14048             throw "Add file by name?"; // should not happen
14049             return;
14050         }
14051         
14052         if(!file || !this.urlAPI){
14053             return;
14054         }
14055         
14056         // file;
14057         // file.type;
14058         
14059         var _this = this;
14060         
14061         
14062         var url = _this.urlAPI.createObjectURL( file);
14063            
14064         this.addCard({
14065             id : Roo.bootstrap.CardUploader.ID--,
14066             is_uploaded : false,
14067             src : url,
14068             srcfile : file,
14069             title : file.name,
14070             mimetype : file.type,
14071             preview : false,
14072             is_deleted : 0
14073         });
14074         
14075     },
14076     
14077     /**
14078      * addCard - add an Attachment to the uploader
14079      * @param data - the data about the image to upload
14080      *
14081      * {
14082           id : 123
14083           title : "Title of file",
14084           is_uploaded : false,
14085           src : "http://.....",
14086           srcfile : { the File upload object },
14087           mimetype : file.type,
14088           preview : false,
14089           is_deleted : 0
14090           .. any other data...
14091         }
14092      *
14093      * 
14094     */
14095     
14096     addCard : function (data)
14097     {
14098         // hidden input element?
14099         // if the file is not an image...
14100         //then we need to use something other that and header_image
14101         var t = this;
14102         //   remove.....
14103         var footer = [
14104             {
14105                 xns : Roo.bootstrap,
14106                 xtype : 'CardFooter',
14107                  items: [
14108                     {
14109                         xns : Roo.bootstrap,
14110                         xtype : 'Element',
14111                         cls : 'd-flex',
14112                         items : [
14113                             
14114                             {
14115                                 xns : Roo.bootstrap,
14116                                 xtype : 'Button',
14117                                 html : String.format("<small>{0}</small>", data.title),
14118                                 cls : 'col-10 text-left',
14119                                 size: 'sm',
14120                                 weight: 'link',
14121                                 fa : 'download',
14122                                 listeners : {
14123                                     click : function() {
14124                                      
14125                                         t.fireEvent( "download", t, data );
14126                                     }
14127                                 }
14128                             },
14129                           
14130                             {
14131                                 xns : Roo.bootstrap,
14132                                 xtype : 'Button',
14133                                 style: 'max-height: 28px; ',
14134                                 size : 'sm',
14135                                 weight: 'danger',
14136                                 cls : 'col-2',
14137                                 fa : 'times',
14138                                 listeners : {
14139                                     click : function() {
14140                                         t.removeCard(data.id)
14141                                     }
14142                                 }
14143                             }
14144                         ]
14145                     }
14146                     
14147                 ] 
14148             }
14149             
14150         ];
14151         
14152         var cn = this.addxtype(
14153             {
14154                  
14155                 xns : Roo.bootstrap,
14156                 xtype : 'Card',
14157                 closeable : true,
14158                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14159                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14160                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14161                 data : data,
14162                 html : false,
14163                  
14164                 items : footer,
14165                 initEvents : function() {
14166                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14167                     var card = this;
14168                     this.imgEl = this.el.select('.card-img-top').first();
14169                     if (this.imgEl) {
14170                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14171                         this.imgEl.set({ 'pointer' : 'cursor' });
14172                                   
14173                     }
14174                     this.getCardFooter().addClass('p-1');
14175                     
14176                   
14177                 }
14178                 
14179             }
14180         );
14181         // dont' really need ot update items.
14182         // this.items.push(cn);
14183         this.fileCollection.add(cn);
14184         
14185         if (!data.srcfile) {
14186             this.updateInput();
14187             return;
14188         }
14189             
14190         var _t = this;
14191         var reader = new FileReader();
14192         reader.addEventListener("load", function() {  
14193             data.srcdata =  reader.result;
14194             _t.updateInput();
14195         });
14196         reader.readAsDataURL(data.srcfile);
14197         
14198         
14199         
14200     },
14201     removeCard : function(id)
14202     {
14203         
14204         var card  = this.fileCollection.get(id);
14205         card.data.is_deleted = 1;
14206         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14207         //this.fileCollection.remove(card);
14208         //this.items = this.items.filter(function(e) { return e != card });
14209         // dont' really need ot update items.
14210         card.el.dom.parentNode.removeChild(card.el.dom);
14211         this.updateInput();
14212
14213         
14214     },
14215     reset: function()
14216     {
14217         this.fileCollection.each(function(card) {
14218             if (card.el.dom && card.el.dom.parentNode) {
14219                 card.el.dom.parentNode.removeChild(card.el.dom);
14220             }
14221         });
14222         this.fileCollection.clear();
14223         this.updateInput();
14224     },
14225     
14226     updateInput : function()
14227     {
14228          var data = [];
14229         this.fileCollection.each(function(e) {
14230             data.push(e.data);
14231             
14232         });
14233         this.inputEl().dom.value = JSON.stringify(data);
14234         
14235         
14236         
14237     }
14238     
14239     
14240 });
14241
14242
14243 Roo.bootstrap.CardUploader.ID = -1;/*
14244  * Based on:
14245  * Ext JS Library 1.1.1
14246  * Copyright(c) 2006-2007, Ext JS, LLC.
14247  *
14248  * Originally Released Under LGPL - original licence link has changed is not relivant.
14249  *
14250  * Fork - LGPL
14251  * <script type="text/javascript">
14252  */
14253
14254
14255 /**
14256  * @class Roo.data.SortTypes
14257  * @singleton
14258  * Defines the default sorting (casting?) comparison functions used when sorting data.
14259  */
14260 Roo.data.SortTypes = {
14261     /**
14262      * Default sort that does nothing
14263      * @param {Mixed} s The value being converted
14264      * @return {Mixed} The comparison value
14265      */
14266     none : function(s){
14267         return s;
14268     },
14269     
14270     /**
14271      * The regular expression used to strip tags
14272      * @type {RegExp}
14273      * @property
14274      */
14275     stripTagsRE : /<\/?[^>]+>/gi,
14276     
14277     /**
14278      * Strips all HTML tags to sort on text only
14279      * @param {Mixed} s The value being converted
14280      * @return {String} The comparison value
14281      */
14282     asText : function(s){
14283         return String(s).replace(this.stripTagsRE, "");
14284     },
14285     
14286     /**
14287      * Strips all HTML tags to sort on text only - Case insensitive
14288      * @param {Mixed} s The value being converted
14289      * @return {String} The comparison value
14290      */
14291     asUCText : function(s){
14292         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14293     },
14294     
14295     /**
14296      * Case insensitive string
14297      * @param {Mixed} s The value being converted
14298      * @return {String} The comparison value
14299      */
14300     asUCString : function(s) {
14301         return String(s).toUpperCase();
14302     },
14303     
14304     /**
14305      * Date sorting
14306      * @param {Mixed} s The value being converted
14307      * @return {Number} The comparison value
14308      */
14309     asDate : function(s) {
14310         if(!s){
14311             return 0;
14312         }
14313         if(s instanceof Date){
14314             return s.getTime();
14315         }
14316         return Date.parse(String(s));
14317     },
14318     
14319     /**
14320      * Float sorting
14321      * @param {Mixed} s The value being converted
14322      * @return {Float} The comparison value
14323      */
14324     asFloat : function(s) {
14325         var val = parseFloat(String(s).replace(/,/g, ""));
14326         if(isNaN(val)) {
14327             val = 0;
14328         }
14329         return val;
14330     },
14331     
14332     /**
14333      * Integer sorting
14334      * @param {Mixed} s The value being converted
14335      * @return {Number} The comparison value
14336      */
14337     asInt : function(s) {
14338         var val = parseInt(String(s).replace(/,/g, ""));
14339         if(isNaN(val)) {
14340             val = 0;
14341         }
14342         return val;
14343     }
14344 };/*
14345  * Based on:
14346  * Ext JS Library 1.1.1
14347  * Copyright(c) 2006-2007, Ext JS, LLC.
14348  *
14349  * Originally Released Under LGPL - original licence link has changed is not relivant.
14350  *
14351  * Fork - LGPL
14352  * <script type="text/javascript">
14353  */
14354
14355 /**
14356 * @class Roo.data.Record
14357  * Instances of this class encapsulate both record <em>definition</em> information, and record
14358  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14359  * to access Records cached in an {@link Roo.data.Store} object.<br>
14360  * <p>
14361  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14362  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14363  * objects.<br>
14364  * <p>
14365  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14366  * @constructor
14367  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14368  * {@link #create}. The parameters are the same.
14369  * @param {Array} data An associative Array of data values keyed by the field name.
14370  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14371  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14372  * not specified an integer id is generated.
14373  */
14374 Roo.data.Record = function(data, id){
14375     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14376     this.data = data;
14377 };
14378
14379 /**
14380  * Generate a constructor for a specific record layout.
14381  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14382  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14383  * Each field definition object may contain the following properties: <ul>
14384  * <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,
14385  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14386  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14387  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14388  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14389  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14390  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14391  * this may be omitted.</p></li>
14392  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14393  * <ul><li>auto (Default, implies no conversion)</li>
14394  * <li>string</li>
14395  * <li>int</li>
14396  * <li>float</li>
14397  * <li>boolean</li>
14398  * <li>date</li></ul></p></li>
14399  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14400  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14401  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14402  * by the Reader into an object that will be stored in the Record. It is passed the
14403  * following parameters:<ul>
14404  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14405  * </ul></p></li>
14406  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14407  * </ul>
14408  * <br>usage:<br><pre><code>
14409 var TopicRecord = Roo.data.Record.create(
14410     {name: 'title', mapping: 'topic_title'},
14411     {name: 'author', mapping: 'username'},
14412     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14413     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14414     {name: 'lastPoster', mapping: 'user2'},
14415     {name: 'excerpt', mapping: 'post_text'}
14416 );
14417
14418 var myNewRecord = new TopicRecord({
14419     title: 'Do my job please',
14420     author: 'noobie',
14421     totalPosts: 1,
14422     lastPost: new Date(),
14423     lastPoster: 'Animal',
14424     excerpt: 'No way dude!'
14425 });
14426 myStore.add(myNewRecord);
14427 </code></pre>
14428  * @method create
14429  * @static
14430  */
14431 Roo.data.Record.create = function(o){
14432     var f = function(){
14433         f.superclass.constructor.apply(this, arguments);
14434     };
14435     Roo.extend(f, Roo.data.Record);
14436     var p = f.prototype;
14437     p.fields = new Roo.util.MixedCollection(false, function(field){
14438         return field.name;
14439     });
14440     for(var i = 0, len = o.length; i < len; i++){
14441         p.fields.add(new Roo.data.Field(o[i]));
14442     }
14443     f.getField = function(name){
14444         return p.fields.get(name);  
14445     };
14446     return f;
14447 };
14448
14449 Roo.data.Record.AUTO_ID = 1000;
14450 Roo.data.Record.EDIT = 'edit';
14451 Roo.data.Record.REJECT = 'reject';
14452 Roo.data.Record.COMMIT = 'commit';
14453
14454 Roo.data.Record.prototype = {
14455     /**
14456      * Readonly flag - true if this record has been modified.
14457      * @type Boolean
14458      */
14459     dirty : false,
14460     editing : false,
14461     error: null,
14462     modified: null,
14463
14464     // private
14465     join : function(store){
14466         this.store = store;
14467     },
14468
14469     /**
14470      * Set the named field to the specified value.
14471      * @param {String} name The name of the field to set.
14472      * @param {Object} value The value to set the field to.
14473      */
14474     set : function(name, value){
14475         if(this.data[name] == value){
14476             return;
14477         }
14478         this.dirty = true;
14479         if(!this.modified){
14480             this.modified = {};
14481         }
14482         if(typeof this.modified[name] == 'undefined'){
14483             this.modified[name] = this.data[name];
14484         }
14485         this.data[name] = value;
14486         if(!this.editing && this.store){
14487             this.store.afterEdit(this);
14488         }       
14489     },
14490
14491     /**
14492      * Get the value of the named field.
14493      * @param {String} name The name of the field to get the value of.
14494      * @return {Object} The value of the field.
14495      */
14496     get : function(name){
14497         return this.data[name]; 
14498     },
14499
14500     // private
14501     beginEdit : function(){
14502         this.editing = true;
14503         this.modified = {}; 
14504     },
14505
14506     // private
14507     cancelEdit : function(){
14508         this.editing = false;
14509         delete this.modified;
14510     },
14511
14512     // private
14513     endEdit : function(){
14514         this.editing = false;
14515         if(this.dirty && this.store){
14516             this.store.afterEdit(this);
14517         }
14518     },
14519
14520     /**
14521      * Usually called by the {@link Roo.data.Store} which owns the Record.
14522      * Rejects all changes made to the Record since either creation, or the last commit operation.
14523      * Modified fields are reverted to their original values.
14524      * <p>
14525      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14526      * of reject operations.
14527      */
14528     reject : function(){
14529         var m = this.modified;
14530         for(var n in m){
14531             if(typeof m[n] != "function"){
14532                 this.data[n] = m[n];
14533             }
14534         }
14535         this.dirty = false;
14536         delete this.modified;
14537         this.editing = false;
14538         if(this.store){
14539             this.store.afterReject(this);
14540         }
14541     },
14542
14543     /**
14544      * Usually called by the {@link Roo.data.Store} which owns the Record.
14545      * Commits all changes made to the Record since either creation, or the last commit operation.
14546      * <p>
14547      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14548      * of commit operations.
14549      */
14550     commit : function(){
14551         this.dirty = false;
14552         delete this.modified;
14553         this.editing = false;
14554         if(this.store){
14555             this.store.afterCommit(this);
14556         }
14557     },
14558
14559     // private
14560     hasError : function(){
14561         return this.error != null;
14562     },
14563
14564     // private
14565     clearError : function(){
14566         this.error = null;
14567     },
14568
14569     /**
14570      * Creates a copy of this record.
14571      * @param {String} id (optional) A new record id if you don't want to use this record's id
14572      * @return {Record}
14573      */
14574     copy : function(newId) {
14575         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14576     }
14577 };/*
14578  * Based on:
14579  * Ext JS Library 1.1.1
14580  * Copyright(c) 2006-2007, Ext JS, LLC.
14581  *
14582  * Originally Released Under LGPL - original licence link has changed is not relivant.
14583  *
14584  * Fork - LGPL
14585  * <script type="text/javascript">
14586  */
14587
14588
14589
14590 /**
14591  * @class Roo.data.Store
14592  * @extends Roo.util.Observable
14593  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14594  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14595  * <p>
14596  * 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
14597  * has no knowledge of the format of the data returned by the Proxy.<br>
14598  * <p>
14599  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14600  * instances from the data object. These records are cached and made available through accessor functions.
14601  * @constructor
14602  * Creates a new Store.
14603  * @param {Object} config A config object containing the objects needed for the Store to access data,
14604  * and read the data into Records.
14605  */
14606 Roo.data.Store = function(config){
14607     this.data = new Roo.util.MixedCollection(false);
14608     this.data.getKey = function(o){
14609         return o.id;
14610     };
14611     this.baseParams = {};
14612     // private
14613     this.paramNames = {
14614         "start" : "start",
14615         "limit" : "limit",
14616         "sort" : "sort",
14617         "dir" : "dir",
14618         "multisort" : "_multisort"
14619     };
14620
14621     if(config && config.data){
14622         this.inlineData = config.data;
14623         delete config.data;
14624     }
14625
14626     Roo.apply(this, config);
14627     
14628     if(this.reader){ // reader passed
14629         this.reader = Roo.factory(this.reader, Roo.data);
14630         this.reader.xmodule = this.xmodule || false;
14631         if(!this.recordType){
14632             this.recordType = this.reader.recordType;
14633         }
14634         if(this.reader.onMetaChange){
14635             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14636         }
14637     }
14638
14639     if(this.recordType){
14640         this.fields = this.recordType.prototype.fields;
14641     }
14642     this.modified = [];
14643
14644     this.addEvents({
14645         /**
14646          * @event datachanged
14647          * Fires when the data cache has changed, and a widget which is using this Store
14648          * as a Record cache should refresh its view.
14649          * @param {Store} this
14650          */
14651         datachanged : true,
14652         /**
14653          * @event metachange
14654          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14655          * @param {Store} this
14656          * @param {Object} meta The JSON metadata
14657          */
14658         metachange : true,
14659         /**
14660          * @event add
14661          * Fires when Records have been added to the Store
14662          * @param {Store} this
14663          * @param {Roo.data.Record[]} records The array of Records added
14664          * @param {Number} index The index at which the record(s) were added
14665          */
14666         add : true,
14667         /**
14668          * @event remove
14669          * Fires when a Record has been removed from the Store
14670          * @param {Store} this
14671          * @param {Roo.data.Record} record The Record that was removed
14672          * @param {Number} index The index at which the record was removed
14673          */
14674         remove : true,
14675         /**
14676          * @event update
14677          * Fires when a Record has been updated
14678          * @param {Store} this
14679          * @param {Roo.data.Record} record The Record that was updated
14680          * @param {String} operation The update operation being performed.  Value may be one of:
14681          * <pre><code>
14682  Roo.data.Record.EDIT
14683  Roo.data.Record.REJECT
14684  Roo.data.Record.COMMIT
14685          * </code></pre>
14686          */
14687         update : true,
14688         /**
14689          * @event clear
14690          * Fires when the data cache has been cleared.
14691          * @param {Store} this
14692          */
14693         clear : true,
14694         /**
14695          * @event beforeload
14696          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14697          * the load action will be canceled.
14698          * @param {Store} this
14699          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14700          */
14701         beforeload : true,
14702         /**
14703          * @event beforeloadadd
14704          * Fires after a new set of Records has been loaded.
14705          * @param {Store} this
14706          * @param {Roo.data.Record[]} records The Records that were loaded
14707          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14708          */
14709         beforeloadadd : true,
14710         /**
14711          * @event load
14712          * Fires after a new set of Records has been loaded, before they are added to the store.
14713          * @param {Store} this
14714          * @param {Roo.data.Record[]} records The Records that were loaded
14715          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14716          * @params {Object} return from reader
14717          */
14718         load : true,
14719         /**
14720          * @event loadexception
14721          * Fires if an exception occurs in the Proxy during loading.
14722          * Called with the signature of the Proxy's "loadexception" event.
14723          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14724          * 
14725          * @param {Proxy} 
14726          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14727          * @param {Object} load options 
14728          * @param {Object} jsonData from your request (normally this contains the Exception)
14729          */
14730         loadexception : true
14731     });
14732     
14733     if(this.proxy){
14734         this.proxy = Roo.factory(this.proxy, Roo.data);
14735         this.proxy.xmodule = this.xmodule || false;
14736         this.relayEvents(this.proxy,  ["loadexception"]);
14737     }
14738     this.sortToggle = {};
14739     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14740
14741     Roo.data.Store.superclass.constructor.call(this);
14742
14743     if(this.inlineData){
14744         this.loadData(this.inlineData);
14745         delete this.inlineData;
14746     }
14747 };
14748
14749 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14750      /**
14751     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14752     * without a remote query - used by combo/forms at present.
14753     */
14754     
14755     /**
14756     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14757     */
14758     /**
14759     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14760     */
14761     /**
14762     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14763     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14764     */
14765     /**
14766     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14767     * on any HTTP request
14768     */
14769     /**
14770     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14771     */
14772     /**
14773     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14774     */
14775     multiSort: false,
14776     /**
14777     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14778     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14779     */
14780     remoteSort : false,
14781
14782     /**
14783     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14784      * loaded or when a record is removed. (defaults to false).
14785     */
14786     pruneModifiedRecords : false,
14787
14788     // private
14789     lastOptions : null,
14790
14791     /**
14792      * Add Records to the Store and fires the add event.
14793      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14794      */
14795     add : function(records){
14796         records = [].concat(records);
14797         for(var i = 0, len = records.length; i < len; i++){
14798             records[i].join(this);
14799         }
14800         var index = this.data.length;
14801         this.data.addAll(records);
14802         this.fireEvent("add", this, records, index);
14803     },
14804
14805     /**
14806      * Remove a Record from the Store and fires the remove event.
14807      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14808      */
14809     remove : function(record){
14810         var index = this.data.indexOf(record);
14811         this.data.removeAt(index);
14812  
14813         if(this.pruneModifiedRecords){
14814             this.modified.remove(record);
14815         }
14816         this.fireEvent("remove", this, record, index);
14817     },
14818
14819     /**
14820      * Remove all Records from the Store and fires the clear event.
14821      */
14822     removeAll : function(){
14823         this.data.clear();
14824         if(this.pruneModifiedRecords){
14825             this.modified = [];
14826         }
14827         this.fireEvent("clear", this);
14828     },
14829
14830     /**
14831      * Inserts Records to the Store at the given index and fires the add event.
14832      * @param {Number} index The start index at which to insert the passed Records.
14833      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14834      */
14835     insert : function(index, records){
14836         records = [].concat(records);
14837         for(var i = 0, len = records.length; i < len; i++){
14838             this.data.insert(index, records[i]);
14839             records[i].join(this);
14840         }
14841         this.fireEvent("add", this, records, index);
14842     },
14843
14844     /**
14845      * Get the index within the cache of the passed Record.
14846      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14847      * @return {Number} The index of the passed Record. Returns -1 if not found.
14848      */
14849     indexOf : function(record){
14850         return this.data.indexOf(record);
14851     },
14852
14853     /**
14854      * Get the index within the cache of the Record with the passed id.
14855      * @param {String} id The id of the Record to find.
14856      * @return {Number} The index of the Record. Returns -1 if not found.
14857      */
14858     indexOfId : function(id){
14859         return this.data.indexOfKey(id);
14860     },
14861
14862     /**
14863      * Get the Record with the specified id.
14864      * @param {String} id The id of the Record to find.
14865      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14866      */
14867     getById : function(id){
14868         return this.data.key(id);
14869     },
14870
14871     /**
14872      * Get the Record at the specified index.
14873      * @param {Number} index The index of the Record to find.
14874      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14875      */
14876     getAt : function(index){
14877         return this.data.itemAt(index);
14878     },
14879
14880     /**
14881      * Returns a range of Records between specified indices.
14882      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14883      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14884      * @return {Roo.data.Record[]} An array of Records
14885      */
14886     getRange : function(start, end){
14887         return this.data.getRange(start, end);
14888     },
14889
14890     // private
14891     storeOptions : function(o){
14892         o = Roo.apply({}, o);
14893         delete o.callback;
14894         delete o.scope;
14895         this.lastOptions = o;
14896     },
14897
14898     /**
14899      * Loads the Record cache from the configured Proxy using the configured Reader.
14900      * <p>
14901      * If using remote paging, then the first load call must specify the <em>start</em>
14902      * and <em>limit</em> properties in the options.params property to establish the initial
14903      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14904      * <p>
14905      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14906      * and this call will return before the new data has been loaded. Perform any post-processing
14907      * in a callback function, or in a "load" event handler.</strong>
14908      * <p>
14909      * @param {Object} options An object containing properties which control loading options:<ul>
14910      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14911      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14912      * passed the following arguments:<ul>
14913      * <li>r : Roo.data.Record[]</li>
14914      * <li>options: Options object from the load call</li>
14915      * <li>success: Boolean success indicator</li></ul></li>
14916      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14917      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14918      * </ul>
14919      */
14920     load : function(options){
14921         options = options || {};
14922         if(this.fireEvent("beforeload", this, options) !== false){
14923             this.storeOptions(options);
14924             var p = Roo.apply(options.params || {}, this.baseParams);
14925             // if meta was not loaded from remote source.. try requesting it.
14926             if (!this.reader.metaFromRemote) {
14927                 p._requestMeta = 1;
14928             }
14929             if(this.sortInfo && this.remoteSort){
14930                 var pn = this.paramNames;
14931                 p[pn["sort"]] = this.sortInfo.field;
14932                 p[pn["dir"]] = this.sortInfo.direction;
14933             }
14934             if (this.multiSort) {
14935                 var pn = this.paramNames;
14936                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14937             }
14938             
14939             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14940         }
14941     },
14942
14943     /**
14944      * Reloads the Record cache from the configured Proxy using the configured Reader and
14945      * the options from the last load operation performed.
14946      * @param {Object} options (optional) An object containing properties which may override the options
14947      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14948      * the most recently used options are reused).
14949      */
14950     reload : function(options){
14951         this.load(Roo.applyIf(options||{}, this.lastOptions));
14952     },
14953
14954     // private
14955     // Called as a callback by the Reader during a load operation.
14956     loadRecords : function(o, options, success){
14957         if(!o || success === false){
14958             if(success !== false){
14959                 this.fireEvent("load", this, [], options, o);
14960             }
14961             if(options.callback){
14962                 options.callback.call(options.scope || this, [], options, false);
14963             }
14964             return;
14965         }
14966         // if data returned failure - throw an exception.
14967         if (o.success === false) {
14968             // show a message if no listener is registered.
14969             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14970                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14971             }
14972             // loadmask wil be hooked into this..
14973             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14974             return;
14975         }
14976         var r = o.records, t = o.totalRecords || r.length;
14977         
14978         this.fireEvent("beforeloadadd", this, r, options, o);
14979         
14980         if(!options || options.add !== true){
14981             if(this.pruneModifiedRecords){
14982                 this.modified = [];
14983             }
14984             for(var i = 0, len = r.length; i < len; i++){
14985                 r[i].join(this);
14986             }
14987             if(this.snapshot){
14988                 this.data = this.snapshot;
14989                 delete this.snapshot;
14990             }
14991             this.data.clear();
14992             this.data.addAll(r);
14993             this.totalLength = t;
14994             this.applySort();
14995             this.fireEvent("datachanged", this);
14996         }else{
14997             this.totalLength = Math.max(t, this.data.length+r.length);
14998             this.add(r);
14999         }
15000         
15001         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15002                 
15003             var e = new Roo.data.Record({});
15004
15005             e.set(this.parent.displayField, this.parent.emptyTitle);
15006             e.set(this.parent.valueField, '');
15007
15008             this.insert(0, e);
15009         }
15010             
15011         this.fireEvent("load", this, r, options, o);
15012         if(options.callback){
15013             options.callback.call(options.scope || this, r, options, true);
15014         }
15015     },
15016
15017
15018     /**
15019      * Loads data from a passed data block. A Reader which understands the format of the data
15020      * must have been configured in the constructor.
15021      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15022      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15023      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15024      */
15025     loadData : function(o, append){
15026         var r = this.reader.readRecords(o);
15027         this.loadRecords(r, {add: append}, true);
15028     },
15029     
15030      /**
15031      * using 'cn' the nested child reader read the child array into it's child stores.
15032      * @param {Object} rec The record with a 'children array
15033      */
15034     loadDataFromChildren : function(rec)
15035     {
15036         this.loadData(this.reader.toLoadData(rec));
15037     },
15038     
15039
15040     /**
15041      * Gets the number of cached records.
15042      * <p>
15043      * <em>If using paging, this may not be the total size of the dataset. If the data object
15044      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15045      * the data set size</em>
15046      */
15047     getCount : function(){
15048         return this.data.length || 0;
15049     },
15050
15051     /**
15052      * Gets the total number of records in the dataset as returned by the server.
15053      * <p>
15054      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15055      * the dataset size</em>
15056      */
15057     getTotalCount : function(){
15058         return this.totalLength || 0;
15059     },
15060
15061     /**
15062      * Returns the sort state of the Store as an object with two properties:
15063      * <pre><code>
15064  field {String} The name of the field by which the Records are sorted
15065  direction {String} The sort order, "ASC" or "DESC"
15066      * </code></pre>
15067      */
15068     getSortState : function(){
15069         return this.sortInfo;
15070     },
15071
15072     // private
15073     applySort : function(){
15074         if(this.sortInfo && !this.remoteSort){
15075             var s = this.sortInfo, f = s.field;
15076             var st = this.fields.get(f).sortType;
15077             var fn = function(r1, r2){
15078                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15079                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15080             };
15081             this.data.sort(s.direction, fn);
15082             if(this.snapshot && this.snapshot != this.data){
15083                 this.snapshot.sort(s.direction, fn);
15084             }
15085         }
15086     },
15087
15088     /**
15089      * Sets the default sort column and order to be used by the next load operation.
15090      * @param {String} fieldName The name of the field to sort by.
15091      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15092      */
15093     setDefaultSort : function(field, dir){
15094         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15095     },
15096
15097     /**
15098      * Sort the Records.
15099      * If remote sorting is used, the sort is performed on the server, and the cache is
15100      * reloaded. If local sorting is used, the cache is sorted internally.
15101      * @param {String} fieldName The name of the field to sort by.
15102      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15103      */
15104     sort : function(fieldName, dir){
15105         var f = this.fields.get(fieldName);
15106         if(!dir){
15107             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15108             
15109             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15110                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15111             }else{
15112                 dir = f.sortDir;
15113             }
15114         }
15115         this.sortToggle[f.name] = dir;
15116         this.sortInfo = {field: f.name, direction: dir};
15117         if(!this.remoteSort){
15118             this.applySort();
15119             this.fireEvent("datachanged", this);
15120         }else{
15121             this.load(this.lastOptions);
15122         }
15123     },
15124
15125     /**
15126      * Calls the specified function for each of the Records in the cache.
15127      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15128      * Returning <em>false</em> aborts and exits the iteration.
15129      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15130      */
15131     each : function(fn, scope){
15132         this.data.each(fn, scope);
15133     },
15134
15135     /**
15136      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15137      * (e.g., during paging).
15138      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15139      */
15140     getModifiedRecords : function(){
15141         return this.modified;
15142     },
15143
15144     // private
15145     createFilterFn : function(property, value, anyMatch){
15146         if(!value.exec){ // not a regex
15147             value = String(value);
15148             if(value.length == 0){
15149                 return false;
15150             }
15151             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15152         }
15153         return function(r){
15154             return value.test(r.data[property]);
15155         };
15156     },
15157
15158     /**
15159      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15160      * @param {String} property A field on your records
15161      * @param {Number} start The record index to start at (defaults to 0)
15162      * @param {Number} end The last record index to include (defaults to length - 1)
15163      * @return {Number} The sum
15164      */
15165     sum : function(property, start, end){
15166         var rs = this.data.items, v = 0;
15167         start = start || 0;
15168         end = (end || end === 0) ? end : rs.length-1;
15169
15170         for(var i = start; i <= end; i++){
15171             v += (rs[i].data[property] || 0);
15172         }
15173         return v;
15174     },
15175
15176     /**
15177      * Filter the records by a specified property.
15178      * @param {String} field A field on your records
15179      * @param {String/RegExp} value Either a string that the field
15180      * should start with or a RegExp to test against the field
15181      * @param {Boolean} anyMatch True to match any part not just the beginning
15182      */
15183     filter : function(property, value, anyMatch){
15184         var fn = this.createFilterFn(property, value, anyMatch);
15185         return fn ? this.filterBy(fn) : this.clearFilter();
15186     },
15187
15188     /**
15189      * Filter by a function. The specified function will be called with each
15190      * record in this data source. If the function returns true the record is included,
15191      * otherwise it is filtered.
15192      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15193      * @param {Object} scope (optional) The scope of the function (defaults to this)
15194      */
15195     filterBy : function(fn, scope){
15196         this.snapshot = this.snapshot || this.data;
15197         this.data = this.queryBy(fn, scope||this);
15198         this.fireEvent("datachanged", this);
15199     },
15200
15201     /**
15202      * Query the records by a specified property.
15203      * @param {String} field A field on your records
15204      * @param {String/RegExp} value Either a string that the field
15205      * should start with or a RegExp to test against the field
15206      * @param {Boolean} anyMatch True to match any part not just the beginning
15207      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15208      */
15209     query : function(property, value, anyMatch){
15210         var fn = this.createFilterFn(property, value, anyMatch);
15211         return fn ? this.queryBy(fn) : this.data.clone();
15212     },
15213
15214     /**
15215      * Query by a function. The specified function will be called with each
15216      * record in this data source. If the function returns true the record is included
15217      * in the results.
15218      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15219      * @param {Object} scope (optional) The scope of the function (defaults to this)
15220       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15221      **/
15222     queryBy : function(fn, scope){
15223         var data = this.snapshot || this.data;
15224         return data.filterBy(fn, scope||this);
15225     },
15226
15227     /**
15228      * Collects unique values for a particular dataIndex from this store.
15229      * @param {String} dataIndex The property to collect
15230      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15231      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15232      * @return {Array} An array of the unique values
15233      **/
15234     collect : function(dataIndex, allowNull, bypassFilter){
15235         var d = (bypassFilter === true && this.snapshot) ?
15236                 this.snapshot.items : this.data.items;
15237         var v, sv, r = [], l = {};
15238         for(var i = 0, len = d.length; i < len; i++){
15239             v = d[i].data[dataIndex];
15240             sv = String(v);
15241             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15242                 l[sv] = true;
15243                 r[r.length] = v;
15244             }
15245         }
15246         return r;
15247     },
15248
15249     /**
15250      * Revert to a view of the Record cache with no filtering applied.
15251      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15252      */
15253     clearFilter : function(suppressEvent){
15254         if(this.snapshot && this.snapshot != this.data){
15255             this.data = this.snapshot;
15256             delete this.snapshot;
15257             if(suppressEvent !== true){
15258                 this.fireEvent("datachanged", this);
15259             }
15260         }
15261     },
15262
15263     // private
15264     afterEdit : function(record){
15265         if(this.modified.indexOf(record) == -1){
15266             this.modified.push(record);
15267         }
15268         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15269     },
15270     
15271     // private
15272     afterReject : function(record){
15273         this.modified.remove(record);
15274         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15275     },
15276
15277     // private
15278     afterCommit : function(record){
15279         this.modified.remove(record);
15280         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15281     },
15282
15283     /**
15284      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15285      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15286      */
15287     commitChanges : function(){
15288         var m = this.modified.slice(0);
15289         this.modified = [];
15290         for(var i = 0, len = m.length; i < len; i++){
15291             m[i].commit();
15292         }
15293     },
15294
15295     /**
15296      * Cancel outstanding changes on all changed records.
15297      */
15298     rejectChanges : function(){
15299         var m = this.modified.slice(0);
15300         this.modified = [];
15301         for(var i = 0, len = m.length; i < len; i++){
15302             m[i].reject();
15303         }
15304     },
15305
15306     onMetaChange : function(meta, rtype, o){
15307         this.recordType = rtype;
15308         this.fields = rtype.prototype.fields;
15309         delete this.snapshot;
15310         this.sortInfo = meta.sortInfo || this.sortInfo;
15311         this.modified = [];
15312         this.fireEvent('metachange', this, this.reader.meta);
15313     },
15314     
15315     moveIndex : function(data, type)
15316     {
15317         var index = this.indexOf(data);
15318         
15319         var newIndex = index + type;
15320         
15321         this.remove(data);
15322         
15323         this.insert(newIndex, data);
15324         
15325     }
15326 });/*
15327  * Based on:
15328  * Ext JS Library 1.1.1
15329  * Copyright(c) 2006-2007, Ext JS, LLC.
15330  *
15331  * Originally Released Under LGPL - original licence link has changed is not relivant.
15332  *
15333  * Fork - LGPL
15334  * <script type="text/javascript">
15335  */
15336
15337 /**
15338  * @class Roo.data.SimpleStore
15339  * @extends Roo.data.Store
15340  * Small helper class to make creating Stores from Array data easier.
15341  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15342  * @cfg {Array} fields An array of field definition objects, or field name strings.
15343  * @cfg {Object} an existing reader (eg. copied from another store)
15344  * @cfg {Array} data The multi-dimensional array of data
15345  * @constructor
15346  * @param {Object} config
15347  */
15348 Roo.data.SimpleStore = function(config)
15349 {
15350     Roo.data.SimpleStore.superclass.constructor.call(this, {
15351         isLocal : true,
15352         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15353                 id: config.id
15354             },
15355             Roo.data.Record.create(config.fields)
15356         ),
15357         proxy : new Roo.data.MemoryProxy(config.data)
15358     });
15359     this.load();
15360 };
15361 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15362  * Based on:
15363  * Ext JS Library 1.1.1
15364  * Copyright(c) 2006-2007, Ext JS, LLC.
15365  *
15366  * Originally Released Under LGPL - original licence link has changed is not relivant.
15367  *
15368  * Fork - LGPL
15369  * <script type="text/javascript">
15370  */
15371
15372 /**
15373 /**
15374  * @extends Roo.data.Store
15375  * @class Roo.data.JsonStore
15376  * Small helper class to make creating Stores for JSON data easier. <br/>
15377 <pre><code>
15378 var store = new Roo.data.JsonStore({
15379     url: 'get-images.php',
15380     root: 'images',
15381     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15382 });
15383 </code></pre>
15384  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15385  * JsonReader and HttpProxy (unless inline data is provided).</b>
15386  * @cfg {Array} fields An array of field definition objects, or field name strings.
15387  * @constructor
15388  * @param {Object} config
15389  */
15390 Roo.data.JsonStore = function(c){
15391     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15392         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15393         reader: new Roo.data.JsonReader(c, c.fields)
15394     }));
15395 };
15396 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15397  * Based on:
15398  * Ext JS Library 1.1.1
15399  * Copyright(c) 2006-2007, Ext JS, LLC.
15400  *
15401  * Originally Released Under LGPL - original licence link has changed is not relivant.
15402  *
15403  * Fork - LGPL
15404  * <script type="text/javascript">
15405  */
15406
15407  
15408 Roo.data.Field = function(config){
15409     if(typeof config == "string"){
15410         config = {name: config};
15411     }
15412     Roo.apply(this, config);
15413     
15414     if(!this.type){
15415         this.type = "auto";
15416     }
15417     
15418     var st = Roo.data.SortTypes;
15419     // named sortTypes are supported, here we look them up
15420     if(typeof this.sortType == "string"){
15421         this.sortType = st[this.sortType];
15422     }
15423     
15424     // set default sortType for strings and dates
15425     if(!this.sortType){
15426         switch(this.type){
15427             case "string":
15428                 this.sortType = st.asUCString;
15429                 break;
15430             case "date":
15431                 this.sortType = st.asDate;
15432                 break;
15433             default:
15434                 this.sortType = st.none;
15435         }
15436     }
15437
15438     // define once
15439     var stripRe = /[\$,%]/g;
15440
15441     // prebuilt conversion function for this field, instead of
15442     // switching every time we're reading a value
15443     if(!this.convert){
15444         var cv, dateFormat = this.dateFormat;
15445         switch(this.type){
15446             case "":
15447             case "auto":
15448             case undefined:
15449                 cv = function(v){ return v; };
15450                 break;
15451             case "string":
15452                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15453                 break;
15454             case "int":
15455                 cv = function(v){
15456                     return v !== undefined && v !== null && v !== '' ?
15457                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15458                     };
15459                 break;
15460             case "float":
15461                 cv = function(v){
15462                     return v !== undefined && v !== null && v !== '' ?
15463                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15464                     };
15465                 break;
15466             case "bool":
15467             case "boolean":
15468                 cv = function(v){ return v === true || v === "true" || v == 1; };
15469                 break;
15470             case "date":
15471                 cv = function(v){
15472                     if(!v){
15473                         return '';
15474                     }
15475                     if(v instanceof Date){
15476                         return v;
15477                     }
15478                     if(dateFormat){
15479                         if(dateFormat == "timestamp"){
15480                             return new Date(v*1000);
15481                         }
15482                         return Date.parseDate(v, dateFormat);
15483                     }
15484                     var parsed = Date.parse(v);
15485                     return parsed ? new Date(parsed) : null;
15486                 };
15487              break;
15488             
15489         }
15490         this.convert = cv;
15491     }
15492 };
15493
15494 Roo.data.Field.prototype = {
15495     dateFormat: null,
15496     defaultValue: "",
15497     mapping: null,
15498     sortType : null,
15499     sortDir : "ASC"
15500 };/*
15501  * Based on:
15502  * Ext JS Library 1.1.1
15503  * Copyright(c) 2006-2007, Ext JS, LLC.
15504  *
15505  * Originally Released Under LGPL - original licence link has changed is not relivant.
15506  *
15507  * Fork - LGPL
15508  * <script type="text/javascript">
15509  */
15510  
15511 // Base class for reading structured data from a data source.  This class is intended to be
15512 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15513
15514 /**
15515  * @class Roo.data.DataReader
15516  * Base class for reading structured data from a data source.  This class is intended to be
15517  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15518  */
15519
15520 Roo.data.DataReader = function(meta, recordType){
15521     
15522     this.meta = meta;
15523     
15524     this.recordType = recordType instanceof Array ? 
15525         Roo.data.Record.create(recordType) : recordType;
15526 };
15527
15528 Roo.data.DataReader.prototype = {
15529     
15530     
15531     readerType : 'Data',
15532      /**
15533      * Create an empty record
15534      * @param {Object} data (optional) - overlay some values
15535      * @return {Roo.data.Record} record created.
15536      */
15537     newRow :  function(d) {
15538         var da =  {};
15539         this.recordType.prototype.fields.each(function(c) {
15540             switch( c.type) {
15541                 case 'int' : da[c.name] = 0; break;
15542                 case 'date' : da[c.name] = new Date(); break;
15543                 case 'float' : da[c.name] = 0.0; break;
15544                 case 'boolean' : da[c.name] = false; break;
15545                 default : da[c.name] = ""; break;
15546             }
15547             
15548         });
15549         return new this.recordType(Roo.apply(da, d));
15550     }
15551     
15552     
15553 };/*
15554  * Based on:
15555  * Ext JS Library 1.1.1
15556  * Copyright(c) 2006-2007, Ext JS, LLC.
15557  *
15558  * Originally Released Under LGPL - original licence link has changed is not relivant.
15559  *
15560  * Fork - LGPL
15561  * <script type="text/javascript">
15562  */
15563
15564 /**
15565  * @class Roo.data.DataProxy
15566  * @extends Roo.data.Observable
15567  * This class is an abstract base class for implementations which provide retrieval of
15568  * unformatted data objects.<br>
15569  * <p>
15570  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15571  * (of the appropriate type which knows how to parse the data object) to provide a block of
15572  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15573  * <p>
15574  * Custom implementations must implement the load method as described in
15575  * {@link Roo.data.HttpProxy#load}.
15576  */
15577 Roo.data.DataProxy = function(){
15578     this.addEvents({
15579         /**
15580          * @event beforeload
15581          * Fires before a network request is made to retrieve a data object.
15582          * @param {Object} This DataProxy object.
15583          * @param {Object} params The params parameter to the load function.
15584          */
15585         beforeload : true,
15586         /**
15587          * @event load
15588          * Fires before the load method's callback is called.
15589          * @param {Object} This DataProxy object.
15590          * @param {Object} o The data object.
15591          * @param {Object} arg The callback argument object passed to the load function.
15592          */
15593         load : true,
15594         /**
15595          * @event loadexception
15596          * Fires if an Exception occurs during data retrieval.
15597          * @param {Object} This DataProxy object.
15598          * @param {Object} o The data object.
15599          * @param {Object} arg The callback argument object passed to the load function.
15600          * @param {Object} e The Exception.
15601          */
15602         loadexception : true
15603     });
15604     Roo.data.DataProxy.superclass.constructor.call(this);
15605 };
15606
15607 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15608
15609     /**
15610      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15611      */
15612 /*
15613  * Based on:
15614  * Ext JS Library 1.1.1
15615  * Copyright(c) 2006-2007, Ext JS, LLC.
15616  *
15617  * Originally Released Under LGPL - original licence link has changed is not relivant.
15618  *
15619  * Fork - LGPL
15620  * <script type="text/javascript">
15621  */
15622 /**
15623  * @class Roo.data.MemoryProxy
15624  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15625  * to the Reader when its load method is called.
15626  * @constructor
15627  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15628  */
15629 Roo.data.MemoryProxy = function(data){
15630     if (data.data) {
15631         data = data.data;
15632     }
15633     Roo.data.MemoryProxy.superclass.constructor.call(this);
15634     this.data = data;
15635 };
15636
15637 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15638     
15639     /**
15640      * Load data from the requested source (in this case an in-memory
15641      * data object passed to the constructor), read the data object into
15642      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15643      * process that block using the passed callback.
15644      * @param {Object} params This parameter is not used by the MemoryProxy class.
15645      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15646      * object into a block of Roo.data.Records.
15647      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15648      * The function must be passed <ul>
15649      * <li>The Record block object</li>
15650      * <li>The "arg" argument from the load function</li>
15651      * <li>A boolean success indicator</li>
15652      * </ul>
15653      * @param {Object} scope The scope in which to call the callback
15654      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15655      */
15656     load : function(params, reader, callback, scope, arg){
15657         params = params || {};
15658         var result;
15659         try {
15660             result = reader.readRecords(params.data ? params.data :this.data);
15661         }catch(e){
15662             this.fireEvent("loadexception", this, arg, null, e);
15663             callback.call(scope, null, arg, false);
15664             return;
15665         }
15666         callback.call(scope, result, arg, true);
15667     },
15668     
15669     // private
15670     update : function(params, records){
15671         
15672     }
15673 });/*
15674  * Based on:
15675  * Ext JS Library 1.1.1
15676  * Copyright(c) 2006-2007, Ext JS, LLC.
15677  *
15678  * Originally Released Under LGPL - original licence link has changed is not relivant.
15679  *
15680  * Fork - LGPL
15681  * <script type="text/javascript">
15682  */
15683 /**
15684  * @class Roo.data.HttpProxy
15685  * @extends Roo.data.DataProxy
15686  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15687  * configured to reference a certain URL.<br><br>
15688  * <p>
15689  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15690  * from which the running page was served.<br><br>
15691  * <p>
15692  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15693  * <p>
15694  * Be aware that to enable the browser to parse an XML document, the server must set
15695  * the Content-Type header in the HTTP response to "text/xml".
15696  * @constructor
15697  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15698  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15699  * will be used to make the request.
15700  */
15701 Roo.data.HttpProxy = function(conn){
15702     Roo.data.HttpProxy.superclass.constructor.call(this);
15703     // is conn a conn config or a real conn?
15704     this.conn = conn;
15705     this.useAjax = !conn || !conn.events;
15706   
15707 };
15708
15709 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15710     // thse are take from connection...
15711     
15712     /**
15713      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15714      */
15715     /**
15716      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15717      * extra parameters to each request made by this object. (defaults to undefined)
15718      */
15719     /**
15720      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15721      *  to each request made by this object. (defaults to undefined)
15722      */
15723     /**
15724      * @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)
15725      */
15726     /**
15727      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15728      */
15729      /**
15730      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15731      * @type Boolean
15732      */
15733   
15734
15735     /**
15736      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15737      * @type Boolean
15738      */
15739     /**
15740      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15741      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15742      * a finer-grained basis than the DataProxy events.
15743      */
15744     getConnection : function(){
15745         return this.useAjax ? Roo.Ajax : this.conn;
15746     },
15747
15748     /**
15749      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15750      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15751      * process that block using the passed callback.
15752      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15753      * for the request to the remote server.
15754      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15755      * object into a block of Roo.data.Records.
15756      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15757      * The function must be passed <ul>
15758      * <li>The Record block object</li>
15759      * <li>The "arg" argument from the load function</li>
15760      * <li>A boolean success indicator</li>
15761      * </ul>
15762      * @param {Object} scope The scope in which to call the callback
15763      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15764      */
15765     load : function(params, reader, callback, scope, arg){
15766         if(this.fireEvent("beforeload", this, params) !== false){
15767             var  o = {
15768                 params : params || {},
15769                 request: {
15770                     callback : callback,
15771                     scope : scope,
15772                     arg : arg
15773                 },
15774                 reader: reader,
15775                 callback : this.loadResponse,
15776                 scope: this
15777             };
15778             if(this.useAjax){
15779                 Roo.applyIf(o, this.conn);
15780                 if(this.activeRequest){
15781                     Roo.Ajax.abort(this.activeRequest);
15782                 }
15783                 this.activeRequest = Roo.Ajax.request(o);
15784             }else{
15785                 this.conn.request(o);
15786             }
15787         }else{
15788             callback.call(scope||this, null, arg, false);
15789         }
15790     },
15791
15792     // private
15793     loadResponse : function(o, success, response){
15794         delete this.activeRequest;
15795         if(!success){
15796             this.fireEvent("loadexception", this, o, response);
15797             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15798             return;
15799         }
15800         var result;
15801         try {
15802             result = o.reader.read(response);
15803         }catch(e){
15804             this.fireEvent("loadexception", this, o, response, e);
15805             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15806             return;
15807         }
15808         
15809         this.fireEvent("load", this, o, o.request.arg);
15810         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15811     },
15812
15813     // private
15814     update : function(dataSet){
15815
15816     },
15817
15818     // private
15819     updateResponse : function(dataSet){
15820
15821     }
15822 });/*
15823  * Based on:
15824  * Ext JS Library 1.1.1
15825  * Copyright(c) 2006-2007, Ext JS, LLC.
15826  *
15827  * Originally Released Under LGPL - original licence link has changed is not relivant.
15828  *
15829  * Fork - LGPL
15830  * <script type="text/javascript">
15831  */
15832
15833 /**
15834  * @class Roo.data.ScriptTagProxy
15835  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15836  * other than the originating domain of the running page.<br><br>
15837  * <p>
15838  * <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
15839  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15840  * <p>
15841  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15842  * source code that is used as the source inside a &lt;script> tag.<br><br>
15843  * <p>
15844  * In order for the browser to process the returned data, the server must wrap the data object
15845  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15846  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15847  * depending on whether the callback name was passed:
15848  * <p>
15849  * <pre><code>
15850 boolean scriptTag = false;
15851 String cb = request.getParameter("callback");
15852 if (cb != null) {
15853     scriptTag = true;
15854     response.setContentType("text/javascript");
15855 } else {
15856     response.setContentType("application/x-json");
15857 }
15858 Writer out = response.getWriter();
15859 if (scriptTag) {
15860     out.write(cb + "(");
15861 }
15862 out.print(dataBlock.toJsonString());
15863 if (scriptTag) {
15864     out.write(");");
15865 }
15866 </pre></code>
15867  *
15868  * @constructor
15869  * @param {Object} config A configuration object.
15870  */
15871 Roo.data.ScriptTagProxy = function(config){
15872     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15873     Roo.apply(this, config);
15874     this.head = document.getElementsByTagName("head")[0];
15875 };
15876
15877 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15878
15879 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15880     /**
15881      * @cfg {String} url The URL from which to request the data object.
15882      */
15883     /**
15884      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15885      */
15886     timeout : 30000,
15887     /**
15888      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15889      * the server the name of the callback function set up by the load call to process the returned data object.
15890      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15891      * javascript output which calls this named function passing the data object as its only parameter.
15892      */
15893     callbackParam : "callback",
15894     /**
15895      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15896      * name to the request.
15897      */
15898     nocache : true,
15899
15900     /**
15901      * Load data from the configured URL, read the data object into
15902      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15903      * process that block using the passed callback.
15904      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15905      * for the request to the remote server.
15906      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15907      * object into a block of Roo.data.Records.
15908      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15909      * The function must be passed <ul>
15910      * <li>The Record block object</li>
15911      * <li>The "arg" argument from the load function</li>
15912      * <li>A boolean success indicator</li>
15913      * </ul>
15914      * @param {Object} scope The scope in which to call the callback
15915      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15916      */
15917     load : function(params, reader, callback, scope, arg){
15918         if(this.fireEvent("beforeload", this, params) !== false){
15919
15920             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15921
15922             var url = this.url;
15923             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15924             if(this.nocache){
15925                 url += "&_dc=" + (new Date().getTime());
15926             }
15927             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15928             var trans = {
15929                 id : transId,
15930                 cb : "stcCallback"+transId,
15931                 scriptId : "stcScript"+transId,
15932                 params : params,
15933                 arg : arg,
15934                 url : url,
15935                 callback : callback,
15936                 scope : scope,
15937                 reader : reader
15938             };
15939             var conn = this;
15940
15941             window[trans.cb] = function(o){
15942                 conn.handleResponse(o, trans);
15943             };
15944
15945             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15946
15947             if(this.autoAbort !== false){
15948                 this.abort();
15949             }
15950
15951             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15952
15953             var script = document.createElement("script");
15954             script.setAttribute("src", url);
15955             script.setAttribute("type", "text/javascript");
15956             script.setAttribute("id", trans.scriptId);
15957             this.head.appendChild(script);
15958
15959             this.trans = trans;
15960         }else{
15961             callback.call(scope||this, null, arg, false);
15962         }
15963     },
15964
15965     // private
15966     isLoading : function(){
15967         return this.trans ? true : false;
15968     },
15969
15970     /**
15971      * Abort the current server request.
15972      */
15973     abort : function(){
15974         if(this.isLoading()){
15975             this.destroyTrans(this.trans);
15976         }
15977     },
15978
15979     // private
15980     destroyTrans : function(trans, isLoaded){
15981         this.head.removeChild(document.getElementById(trans.scriptId));
15982         clearTimeout(trans.timeoutId);
15983         if(isLoaded){
15984             window[trans.cb] = undefined;
15985             try{
15986                 delete window[trans.cb];
15987             }catch(e){}
15988         }else{
15989             // if hasn't been loaded, wait for load to remove it to prevent script error
15990             window[trans.cb] = function(){
15991                 window[trans.cb] = undefined;
15992                 try{
15993                     delete window[trans.cb];
15994                 }catch(e){}
15995             };
15996         }
15997     },
15998
15999     // private
16000     handleResponse : function(o, trans){
16001         this.trans = false;
16002         this.destroyTrans(trans, true);
16003         var result;
16004         try {
16005             result = trans.reader.readRecords(o);
16006         }catch(e){
16007             this.fireEvent("loadexception", this, o, trans.arg, e);
16008             trans.callback.call(trans.scope||window, null, trans.arg, false);
16009             return;
16010         }
16011         this.fireEvent("load", this, o, trans.arg);
16012         trans.callback.call(trans.scope||window, result, trans.arg, true);
16013     },
16014
16015     // private
16016     handleFailure : function(trans){
16017         this.trans = false;
16018         this.destroyTrans(trans, false);
16019         this.fireEvent("loadexception", this, null, trans.arg);
16020         trans.callback.call(trans.scope||window, null, trans.arg, false);
16021     }
16022 });/*
16023  * Based on:
16024  * Ext JS Library 1.1.1
16025  * Copyright(c) 2006-2007, Ext JS, LLC.
16026  *
16027  * Originally Released Under LGPL - original licence link has changed is not relivant.
16028  *
16029  * Fork - LGPL
16030  * <script type="text/javascript">
16031  */
16032
16033 /**
16034  * @class Roo.data.JsonReader
16035  * @extends Roo.data.DataReader
16036  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16037  * based on mappings in a provided Roo.data.Record constructor.
16038  * 
16039  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16040  * in the reply previously. 
16041  * 
16042  * <p>
16043  * Example code:
16044  * <pre><code>
16045 var RecordDef = Roo.data.Record.create([
16046     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16047     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16048 ]);
16049 var myReader = new Roo.data.JsonReader({
16050     totalProperty: "results",    // The property which contains the total dataset size (optional)
16051     root: "rows",                // The property which contains an Array of row objects
16052     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16053 }, RecordDef);
16054 </code></pre>
16055  * <p>
16056  * This would consume a JSON file like this:
16057  * <pre><code>
16058 { 'results': 2, 'rows': [
16059     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16060     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16061 }
16062 </code></pre>
16063  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16064  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16065  * paged from the remote server.
16066  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16067  * @cfg {String} root name of the property which contains the Array of row objects.
16068  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16069  * @cfg {Array} fields Array of field definition objects
16070  * @constructor
16071  * Create a new JsonReader
16072  * @param {Object} meta Metadata configuration options
16073  * @param {Object} recordType Either an Array of field definition objects,
16074  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16075  */
16076 Roo.data.JsonReader = function(meta, recordType){
16077     
16078     meta = meta || {};
16079     // set some defaults:
16080     Roo.applyIf(meta, {
16081         totalProperty: 'total',
16082         successProperty : 'success',
16083         root : 'data',
16084         id : 'id'
16085     });
16086     
16087     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16088 };
16089 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16090     
16091     readerType : 'Json',
16092     
16093     /**
16094      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16095      * Used by Store query builder to append _requestMeta to params.
16096      * 
16097      */
16098     metaFromRemote : false,
16099     /**
16100      * This method is only used by a DataProxy which has retrieved data from a remote server.
16101      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16102      * @return {Object} data A data block which is used by an Roo.data.Store object as
16103      * a cache of Roo.data.Records.
16104      */
16105     read : function(response){
16106         var json = response.responseText;
16107        
16108         var o = /* eval:var:o */ eval("("+json+")");
16109         if(!o) {
16110             throw {message: "JsonReader.read: Json object not found"};
16111         }
16112         
16113         if(o.metaData){
16114             
16115             delete this.ef;
16116             this.metaFromRemote = true;
16117             this.meta = o.metaData;
16118             this.recordType = Roo.data.Record.create(o.metaData.fields);
16119             this.onMetaChange(this.meta, this.recordType, o);
16120         }
16121         return this.readRecords(o);
16122     },
16123
16124     // private function a store will implement
16125     onMetaChange : function(meta, recordType, o){
16126
16127     },
16128
16129     /**
16130          * @ignore
16131          */
16132     simpleAccess: function(obj, subsc) {
16133         return obj[subsc];
16134     },
16135
16136         /**
16137          * @ignore
16138          */
16139     getJsonAccessor: function(){
16140         var re = /[\[\.]/;
16141         return function(expr) {
16142             try {
16143                 return(re.test(expr))
16144                     ? new Function("obj", "return obj." + expr)
16145                     : function(obj){
16146                         return obj[expr];
16147                     };
16148             } catch(e){}
16149             return Roo.emptyFn;
16150         };
16151     }(),
16152
16153     /**
16154      * Create a data block containing Roo.data.Records from an XML document.
16155      * @param {Object} o An object which contains an Array of row objects in the property specified
16156      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16157      * which contains the total size of the dataset.
16158      * @return {Object} data A data block which is used by an Roo.data.Store object as
16159      * a cache of Roo.data.Records.
16160      */
16161     readRecords : function(o){
16162         /**
16163          * After any data loads, the raw JSON data is available for further custom processing.
16164          * @type Object
16165          */
16166         this.o = o;
16167         var s = this.meta, Record = this.recordType,
16168             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16169
16170 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16171         if (!this.ef) {
16172             if(s.totalProperty) {
16173                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16174                 }
16175                 if(s.successProperty) {
16176                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16177                 }
16178                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16179                 if (s.id) {
16180                         var g = this.getJsonAccessor(s.id);
16181                         this.getId = function(rec) {
16182                                 var r = g(rec);  
16183                                 return (r === undefined || r === "") ? null : r;
16184                         };
16185                 } else {
16186                         this.getId = function(){return null;};
16187                 }
16188             this.ef = [];
16189             for(var jj = 0; jj < fl; jj++){
16190                 f = fi[jj];
16191                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16192                 this.ef[jj] = this.getJsonAccessor(map);
16193             }
16194         }
16195
16196         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16197         if(s.totalProperty){
16198             var vt = parseInt(this.getTotal(o), 10);
16199             if(!isNaN(vt)){
16200                 totalRecords = vt;
16201             }
16202         }
16203         if(s.successProperty){
16204             var vs = this.getSuccess(o);
16205             if(vs === false || vs === 'false'){
16206                 success = false;
16207             }
16208         }
16209         var records = [];
16210         for(var i = 0; i < c; i++){
16211                 var n = root[i];
16212             var values = {};
16213             var id = this.getId(n);
16214             for(var j = 0; j < fl; j++){
16215                 f = fi[j];
16216             var v = this.ef[j](n);
16217             if (!f.convert) {
16218                 Roo.log('missing convert for ' + f.name);
16219                 Roo.log(f);
16220                 continue;
16221             }
16222             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16223             }
16224             var record = new Record(values, id);
16225             record.json = n;
16226             records[i] = record;
16227         }
16228         return {
16229             raw : o,
16230             success : success,
16231             records : records,
16232             totalRecords : totalRecords
16233         };
16234     },
16235     // used when loading children.. @see loadDataFromChildren
16236     toLoadData: function(rec)
16237     {
16238         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16239         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16240         return { data : data, total : data.length };
16241         
16242     }
16243 });/*
16244  * Based on:
16245  * Ext JS Library 1.1.1
16246  * Copyright(c) 2006-2007, Ext JS, LLC.
16247  *
16248  * Originally Released Under LGPL - original licence link has changed is not relivant.
16249  *
16250  * Fork - LGPL
16251  * <script type="text/javascript">
16252  */
16253
16254 /**
16255  * @class Roo.data.ArrayReader
16256  * @extends Roo.data.DataReader
16257  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16258  * Each element of that Array represents a row of data fields. The
16259  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16260  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16261  * <p>
16262  * Example code:.
16263  * <pre><code>
16264 var RecordDef = Roo.data.Record.create([
16265     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16266     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16267 ]);
16268 var myReader = new Roo.data.ArrayReader({
16269     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16270 }, RecordDef);
16271 </code></pre>
16272  * <p>
16273  * This would consume an Array like this:
16274  * <pre><code>
16275 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16276   </code></pre>
16277  
16278  * @constructor
16279  * Create a new JsonReader
16280  * @param {Object} meta Metadata configuration options.
16281  * @param {Object|Array} recordType Either an Array of field definition objects
16282  * 
16283  * @cfg {Array} fields Array of field definition objects
16284  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16285  * as specified to {@link Roo.data.Record#create},
16286  * or an {@link Roo.data.Record} object
16287  *
16288  * 
16289  * created using {@link Roo.data.Record#create}.
16290  */
16291 Roo.data.ArrayReader = function(meta, recordType)
16292 {    
16293     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16294 };
16295
16296 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16297     
16298       /**
16299      * Create a data block containing Roo.data.Records from an XML document.
16300      * @param {Object} o An Array of row objects which represents the dataset.
16301      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16302      * a cache of Roo.data.Records.
16303      */
16304     readRecords : function(o)
16305     {
16306         var sid = this.meta ? this.meta.id : null;
16307         var recordType = this.recordType, fields = recordType.prototype.fields;
16308         var records = [];
16309         var root = o;
16310         for(var i = 0; i < root.length; i++){
16311             var n = root[i];
16312             var values = {};
16313             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16314             for(var j = 0, jlen = fields.length; j < jlen; j++){
16315                 var f = fields.items[j];
16316                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16317                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16318                 v = f.convert(v);
16319                 values[f.name] = v;
16320             }
16321             var record = new recordType(values, id);
16322             record.json = n;
16323             records[records.length] = record;
16324         }
16325         return {
16326             records : records,
16327             totalRecords : records.length
16328         };
16329     },
16330     // used when loading children.. @see loadDataFromChildren
16331     toLoadData: function(rec)
16332     {
16333         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16334         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16335         
16336     }
16337     
16338     
16339 });/*
16340  * - LGPL
16341  * * 
16342  */
16343
16344 /**
16345  * @class Roo.bootstrap.ComboBox
16346  * @extends Roo.bootstrap.TriggerField
16347  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16348  * @cfg {Boolean} append (true|false) default false
16349  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16350  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16351  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16352  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16353  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16354  * @cfg {Boolean} animate default true
16355  * @cfg {Boolean} emptyResultText only for touch device
16356  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16357  * @cfg {String} emptyTitle default ''
16358  * @cfg {Number} width fixed with? experimental
16359  * @constructor
16360  * Create a new ComboBox.
16361  * @param {Object} config Configuration options
16362  */
16363 Roo.bootstrap.ComboBox = function(config){
16364     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16365     this.addEvents({
16366         /**
16367          * @event expand
16368          * Fires when the dropdown list is expanded
16369         * @param {Roo.bootstrap.ComboBox} combo This combo box
16370         */
16371         'expand' : true,
16372         /**
16373          * @event collapse
16374          * Fires when the dropdown list is collapsed
16375         * @param {Roo.bootstrap.ComboBox} combo This combo box
16376         */
16377         'collapse' : true,
16378         /**
16379          * @event beforeselect
16380          * Fires before a list item is selected. Return false to cancel the selection.
16381         * @param {Roo.bootstrap.ComboBox} combo This combo box
16382         * @param {Roo.data.Record} record The data record returned from the underlying store
16383         * @param {Number} index The index of the selected item in the dropdown list
16384         */
16385         'beforeselect' : true,
16386         /**
16387          * @event select
16388          * Fires when a list item is selected
16389         * @param {Roo.bootstrap.ComboBox} combo This combo box
16390         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16391         * @param {Number} index The index of the selected item in the dropdown list
16392         */
16393         'select' : true,
16394         /**
16395          * @event beforequery
16396          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16397          * The event object passed has these properties:
16398         * @param {Roo.bootstrap.ComboBox} combo This combo box
16399         * @param {String} query The query
16400         * @param {Boolean} forceAll true to force "all" query
16401         * @param {Boolean} cancel true to cancel the query
16402         * @param {Object} e The query event object
16403         */
16404         'beforequery': true,
16405          /**
16406          * @event add
16407          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16408         * @param {Roo.bootstrap.ComboBox} combo This combo box
16409         */
16410         'add' : true,
16411         /**
16412          * @event edit
16413          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16414         * @param {Roo.bootstrap.ComboBox} combo This combo box
16415         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16416         */
16417         'edit' : true,
16418         /**
16419          * @event remove
16420          * Fires when the remove value from the combobox array
16421         * @param {Roo.bootstrap.ComboBox} combo This combo box
16422         */
16423         'remove' : true,
16424         /**
16425          * @event afterremove
16426          * Fires when the remove value from the combobox array
16427         * @param {Roo.bootstrap.ComboBox} combo This combo box
16428         */
16429         'afterremove' : true,
16430         /**
16431          * @event specialfilter
16432          * Fires when specialfilter
16433             * @param {Roo.bootstrap.ComboBox} combo This combo box
16434             */
16435         'specialfilter' : true,
16436         /**
16437          * @event tick
16438          * Fires when tick the element
16439             * @param {Roo.bootstrap.ComboBox} combo This combo box
16440             */
16441         'tick' : true,
16442         /**
16443          * @event touchviewdisplay
16444          * Fires when touch view require special display (default is using displayField)
16445             * @param {Roo.bootstrap.ComboBox} combo This combo box
16446             * @param {Object} cfg set html .
16447             */
16448         'touchviewdisplay' : true
16449         
16450     });
16451     
16452     this.item = [];
16453     this.tickItems = [];
16454     
16455     this.selectedIndex = -1;
16456     if(this.mode == 'local'){
16457         if(config.queryDelay === undefined){
16458             this.queryDelay = 10;
16459         }
16460         if(config.minChars === undefined){
16461             this.minChars = 0;
16462         }
16463     }
16464 };
16465
16466 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16467      
16468     /**
16469      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16470      * rendering into an Roo.Editor, defaults to false)
16471      */
16472     /**
16473      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16474      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16475      */
16476     /**
16477      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16478      */
16479     /**
16480      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16481      * the dropdown list (defaults to undefined, with no header element)
16482      */
16483
16484      /**
16485      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16486      */
16487      
16488      /**
16489      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16490      */
16491     listWidth: undefined,
16492     /**
16493      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16494      * mode = 'remote' or 'text' if mode = 'local')
16495      */
16496     displayField: undefined,
16497     
16498     /**
16499      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16500      * mode = 'remote' or 'value' if mode = 'local'). 
16501      * Note: use of a valueField requires the user make a selection
16502      * in order for a value to be mapped.
16503      */
16504     valueField: undefined,
16505     /**
16506      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16507      */
16508     modalTitle : '',
16509     
16510     /**
16511      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16512      * field's data value (defaults to the underlying DOM element's name)
16513      */
16514     hiddenName: undefined,
16515     /**
16516      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16517      */
16518     listClass: '',
16519     /**
16520      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16521      */
16522     selectedClass: 'active',
16523     
16524     /**
16525      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16526      */
16527     shadow:'sides',
16528     /**
16529      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16530      * anchor positions (defaults to 'tl-bl')
16531      */
16532     listAlign: 'tl-bl?',
16533     /**
16534      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16535      */
16536     maxHeight: 300,
16537     /**
16538      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16539      * query specified by the allQuery config option (defaults to 'query')
16540      */
16541     triggerAction: 'query',
16542     /**
16543      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16544      * (defaults to 4, does not apply if editable = false)
16545      */
16546     minChars : 4,
16547     /**
16548      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16549      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16550      */
16551     typeAhead: false,
16552     /**
16553      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16554      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16555      */
16556     queryDelay: 500,
16557     /**
16558      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16559      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16560      */
16561     pageSize: 0,
16562     /**
16563      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16564      * when editable = true (defaults to false)
16565      */
16566     selectOnFocus:false,
16567     /**
16568      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16569      */
16570     queryParam: 'query',
16571     /**
16572      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16573      * when mode = 'remote' (defaults to 'Loading...')
16574      */
16575     loadingText: 'Loading...',
16576     /**
16577      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16578      */
16579     resizable: false,
16580     /**
16581      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16582      */
16583     handleHeight : 8,
16584     /**
16585      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16586      * traditional select (defaults to true)
16587      */
16588     editable: true,
16589     /**
16590      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16591      */
16592     allQuery: '',
16593     /**
16594      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16595      */
16596     mode: 'remote',
16597     /**
16598      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16599      * listWidth has a higher value)
16600      */
16601     minListWidth : 70,
16602     /**
16603      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16604      * allow the user to set arbitrary text into the field (defaults to false)
16605      */
16606     forceSelection:false,
16607     /**
16608      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16609      * if typeAhead = true (defaults to 250)
16610      */
16611     typeAheadDelay : 250,
16612     /**
16613      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16614      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16615      */
16616     valueNotFoundText : undefined,
16617     /**
16618      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16619      */
16620     blockFocus : false,
16621     
16622     /**
16623      * @cfg {Boolean} disableClear Disable showing of clear button.
16624      */
16625     disableClear : false,
16626     /**
16627      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16628      */
16629     alwaysQuery : false,
16630     
16631     /**
16632      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16633      */
16634     multiple : false,
16635     
16636     /**
16637      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16638      */
16639     invalidClass : "has-warning",
16640     
16641     /**
16642      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16643      */
16644     validClass : "has-success",
16645     
16646     /**
16647      * @cfg {Boolean} specialFilter (true|false) special filter default false
16648      */
16649     specialFilter : false,
16650     
16651     /**
16652      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16653      */
16654     mobileTouchView : true,
16655     
16656     /**
16657      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16658      */
16659     useNativeIOS : false,
16660     
16661     /**
16662      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16663      */
16664     mobile_restrict_height : false,
16665     
16666     ios_options : false,
16667     
16668     //private
16669     addicon : false,
16670     editicon: false,
16671     
16672     page: 0,
16673     hasQuery: false,
16674     append: false,
16675     loadNext: false,
16676     autoFocus : true,
16677     tickable : false,
16678     btnPosition : 'right',
16679     triggerList : true,
16680     showToggleBtn : true,
16681     animate : true,
16682     emptyResultText: 'Empty',
16683     triggerText : 'Select',
16684     emptyTitle : '',
16685     width : false,
16686     
16687     // element that contains real text value.. (when hidden is used..)
16688     
16689     getAutoCreate : function()
16690     {   
16691         var cfg = false;
16692         //render
16693         /*
16694          * Render classic select for iso
16695          */
16696         
16697         if(Roo.isIOS && this.useNativeIOS){
16698             cfg = this.getAutoCreateNativeIOS();
16699             return cfg;
16700         }
16701         
16702         /*
16703          * Touch Devices
16704          */
16705         
16706         if(Roo.isTouch && this.mobileTouchView){
16707             cfg = this.getAutoCreateTouchView();
16708             return cfg;;
16709         }
16710         
16711         /*
16712          *  Normal ComboBox
16713          */
16714         if(!this.tickable){
16715             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16716             return cfg;
16717         }
16718         
16719         /*
16720          *  ComboBox with tickable selections
16721          */
16722              
16723         var align = this.labelAlign || this.parentLabelAlign();
16724         
16725         cfg = {
16726             cls : 'form-group roo-combobox-tickable' //input-group
16727         };
16728         
16729         var btn_text_select = '';
16730         var btn_text_done = '';
16731         var btn_text_cancel = '';
16732         
16733         if (this.btn_text_show) {
16734             btn_text_select = 'Select';
16735             btn_text_done = 'Done';
16736             btn_text_cancel = 'Cancel'; 
16737         }
16738         
16739         var buttons = {
16740             tag : 'div',
16741             cls : 'tickable-buttons',
16742             cn : [
16743                 {
16744                     tag : 'button',
16745                     type : 'button',
16746                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16747                     //html : this.triggerText
16748                     html: btn_text_select
16749                 },
16750                 {
16751                     tag : 'button',
16752                     type : 'button',
16753                     name : 'ok',
16754                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16755                     //html : 'Done'
16756                     html: btn_text_done
16757                 },
16758                 {
16759                     tag : 'button',
16760                     type : 'button',
16761                     name : 'cancel',
16762                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16763                     //html : 'Cancel'
16764                     html: btn_text_cancel
16765                 }
16766             ]
16767         };
16768         
16769         if(this.editable){
16770             buttons.cn.unshift({
16771                 tag: 'input',
16772                 cls: 'roo-select2-search-field-input'
16773             });
16774         }
16775         
16776         var _this = this;
16777         
16778         Roo.each(buttons.cn, function(c){
16779             if (_this.size) {
16780                 c.cls += ' btn-' + _this.size;
16781             }
16782
16783             if (_this.disabled) {
16784                 c.disabled = true;
16785             }
16786         });
16787         
16788         var box = {
16789             tag: 'div',
16790             style : 'display: contents',
16791             cn: [
16792                 {
16793                     tag: 'input',
16794                     type : 'hidden',
16795                     cls: 'form-hidden-field'
16796                 },
16797                 {
16798                     tag: 'ul',
16799                     cls: 'roo-select2-choices',
16800                     cn:[
16801                         {
16802                             tag: 'li',
16803                             cls: 'roo-select2-search-field',
16804                             cn: [
16805                                 buttons
16806                             ]
16807                         }
16808                     ]
16809                 }
16810             ]
16811         };
16812         
16813         var combobox = {
16814             cls: 'roo-select2-container input-group roo-select2-container-multi',
16815             cn: [
16816                 
16817                 box
16818 //                {
16819 //                    tag: 'ul',
16820 //                    cls: 'typeahead typeahead-long dropdown-menu',
16821 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16822 //                }
16823             ]
16824         };
16825         
16826         if(this.hasFeedback && !this.allowBlank){
16827             
16828             var feedback = {
16829                 tag: 'span',
16830                 cls: 'glyphicon form-control-feedback'
16831             };
16832
16833             combobox.cn.push(feedback);
16834         }
16835         
16836         
16837         
16838         var indicator = {
16839             tag : 'i',
16840             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16841             tooltip : 'This field is required'
16842         };
16843         if (Roo.bootstrap.version == 4) {
16844             indicator = {
16845                 tag : 'i',
16846                 style : 'display:none'
16847             };
16848         }
16849         if (align ==='left' && this.fieldLabel.length) {
16850             
16851             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16852             
16853             cfg.cn = [
16854                 indicator,
16855                 {
16856                     tag: 'label',
16857                     'for' :  id,
16858                     cls : 'control-label col-form-label',
16859                     html : this.fieldLabel
16860
16861                 },
16862                 {
16863                     cls : "", 
16864                     cn: [
16865                         combobox
16866                     ]
16867                 }
16868
16869             ];
16870             
16871             var labelCfg = cfg.cn[1];
16872             var contentCfg = cfg.cn[2];
16873             
16874
16875             if(this.indicatorpos == 'right'){
16876                 
16877                 cfg.cn = [
16878                     {
16879                         tag: 'label',
16880                         'for' :  id,
16881                         cls : 'control-label col-form-label',
16882                         cn : [
16883                             {
16884                                 tag : 'span',
16885                                 html : this.fieldLabel
16886                             },
16887                             indicator
16888                         ]
16889                     },
16890                     {
16891                         cls : "",
16892                         cn: [
16893                             combobox
16894                         ]
16895                     }
16896
16897                 ];
16898                 
16899                 
16900                 
16901                 labelCfg = cfg.cn[0];
16902                 contentCfg = cfg.cn[1];
16903             
16904             }
16905             
16906             if(this.labelWidth > 12){
16907                 labelCfg.style = "width: " + this.labelWidth + 'px';
16908             }
16909             if(this.width * 1 > 0){
16910                 contentCfg.style = "width: " + this.width + 'px';
16911             }
16912             if(this.labelWidth < 13 && this.labelmd == 0){
16913                 this.labelmd = this.labelWidth;
16914             }
16915             
16916             if(this.labellg > 0){
16917                 labelCfg.cls += ' col-lg-' + this.labellg;
16918                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16919             }
16920             
16921             if(this.labelmd > 0){
16922                 labelCfg.cls += ' col-md-' + this.labelmd;
16923                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16924             }
16925             
16926             if(this.labelsm > 0){
16927                 labelCfg.cls += ' col-sm-' + this.labelsm;
16928                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16929             }
16930             
16931             if(this.labelxs > 0){
16932                 labelCfg.cls += ' col-xs-' + this.labelxs;
16933                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16934             }
16935                 
16936                 
16937         } else if ( this.fieldLabel.length) {
16938 //                Roo.log(" label");
16939                  cfg.cn = [
16940                    indicator,
16941                     {
16942                         tag: 'label',
16943                         //cls : 'input-group-addon',
16944                         html : this.fieldLabel
16945                     },
16946                     combobox
16947                 ];
16948                 
16949                 if(this.indicatorpos == 'right'){
16950                     cfg.cn = [
16951                         {
16952                             tag: 'label',
16953                             //cls : 'input-group-addon',
16954                             html : this.fieldLabel
16955                         },
16956                         indicator,
16957                         combobox
16958                     ];
16959                     
16960                 }
16961
16962         } else {
16963             
16964 //                Roo.log(" no label && no align");
16965                 cfg = combobox
16966                      
16967                 
16968         }
16969          
16970         var settings=this;
16971         ['xs','sm','md','lg'].map(function(size){
16972             if (settings[size]) {
16973                 cfg.cls += ' col-' + size + '-' + settings[size];
16974             }
16975         });
16976         
16977         return cfg;
16978         
16979     },
16980     
16981     _initEventsCalled : false,
16982     
16983     // private
16984     initEvents: function()
16985     {   
16986         if (this._initEventsCalled) { // as we call render... prevent looping...
16987             return;
16988         }
16989         this._initEventsCalled = true;
16990         
16991         if (!this.store) {
16992             throw "can not find store for combo";
16993         }
16994         
16995         this.indicator = this.indicatorEl();
16996         
16997         this.store = Roo.factory(this.store, Roo.data);
16998         this.store.parent = this;
16999         
17000         // if we are building from html. then this element is so complex, that we can not really
17001         // use the rendered HTML.
17002         // so we have to trash and replace the previous code.
17003         if (Roo.XComponent.build_from_html) {
17004             // remove this element....
17005             var e = this.el.dom, k=0;
17006             while (e ) { e = e.previousSibling;  ++k;}
17007
17008             this.el.remove();
17009             
17010             this.el=false;
17011             this.rendered = false;
17012             
17013             this.render(this.parent().getChildContainer(true), k);
17014         }
17015         
17016         if(Roo.isIOS && this.useNativeIOS){
17017             this.initIOSView();
17018             return;
17019         }
17020         
17021         /*
17022          * Touch Devices
17023          */
17024         
17025         if(Roo.isTouch && this.mobileTouchView){
17026             this.initTouchView();
17027             return;
17028         }
17029         
17030         if(this.tickable){
17031             this.initTickableEvents();
17032             return;
17033         }
17034         
17035         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17036         
17037         if(this.hiddenName){
17038             
17039             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17040             
17041             this.hiddenField.dom.value =
17042                 this.hiddenValue !== undefined ? this.hiddenValue :
17043                 this.value !== undefined ? this.value : '';
17044
17045             // prevent input submission
17046             this.el.dom.removeAttribute('name');
17047             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17048              
17049              
17050         }
17051         //if(Roo.isGecko){
17052         //    this.el.dom.setAttribute('autocomplete', 'off');
17053         //}
17054         
17055         var cls = 'x-combo-list';
17056         
17057         //this.list = new Roo.Layer({
17058         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17059         //});
17060         
17061         var _this = this;
17062         
17063         (function(){
17064             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17065             _this.list.setWidth(lw);
17066         }).defer(100);
17067         
17068         this.list.on('mouseover', this.onViewOver, this);
17069         this.list.on('mousemove', this.onViewMove, this);
17070         this.list.on('scroll', this.onViewScroll, this);
17071         
17072         /*
17073         this.list.swallowEvent('mousewheel');
17074         this.assetHeight = 0;
17075
17076         if(this.title){
17077             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17078             this.assetHeight += this.header.getHeight();
17079         }
17080
17081         this.innerList = this.list.createChild({cls:cls+'-inner'});
17082         this.innerList.on('mouseover', this.onViewOver, this);
17083         this.innerList.on('mousemove', this.onViewMove, this);
17084         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17085         
17086         if(this.allowBlank && !this.pageSize && !this.disableClear){
17087             this.footer = this.list.createChild({cls:cls+'-ft'});
17088             this.pageTb = new Roo.Toolbar(this.footer);
17089            
17090         }
17091         if(this.pageSize){
17092             this.footer = this.list.createChild({cls:cls+'-ft'});
17093             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17094                     {pageSize: this.pageSize});
17095             
17096         }
17097         
17098         if (this.pageTb && this.allowBlank && !this.disableClear) {
17099             var _this = this;
17100             this.pageTb.add(new Roo.Toolbar.Fill(), {
17101                 cls: 'x-btn-icon x-btn-clear',
17102                 text: '&#160;',
17103                 handler: function()
17104                 {
17105                     _this.collapse();
17106                     _this.clearValue();
17107                     _this.onSelect(false, -1);
17108                 }
17109             });
17110         }
17111         if (this.footer) {
17112             this.assetHeight += this.footer.getHeight();
17113         }
17114         */
17115             
17116         if(!this.tpl){
17117             this.tpl = Roo.bootstrap.version == 4 ?
17118                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17119                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17120         }
17121
17122         this.view = new Roo.View(this.list, this.tpl, {
17123             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17124         });
17125         //this.view.wrapEl.setDisplayed(false);
17126         this.view.on('click', this.onViewClick, this);
17127         
17128         
17129         this.store.on('beforeload', this.onBeforeLoad, this);
17130         this.store.on('load', this.onLoad, this);
17131         this.store.on('loadexception', this.onLoadException, this);
17132         /*
17133         if(this.resizable){
17134             this.resizer = new Roo.Resizable(this.list,  {
17135                pinned:true, handles:'se'
17136             });
17137             this.resizer.on('resize', function(r, w, h){
17138                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17139                 this.listWidth = w;
17140                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17141                 this.restrictHeight();
17142             }, this);
17143             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17144         }
17145         */
17146         if(!this.editable){
17147             this.editable = true;
17148             this.setEditable(false);
17149         }
17150         
17151         /*
17152         
17153         if (typeof(this.events.add.listeners) != 'undefined') {
17154             
17155             this.addicon = this.wrap.createChild(
17156                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17157        
17158             this.addicon.on('click', function(e) {
17159                 this.fireEvent('add', this);
17160             }, this);
17161         }
17162         if (typeof(this.events.edit.listeners) != 'undefined') {
17163             
17164             this.editicon = this.wrap.createChild(
17165                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17166             if (this.addicon) {
17167                 this.editicon.setStyle('margin-left', '40px');
17168             }
17169             this.editicon.on('click', function(e) {
17170                 
17171                 // we fire even  if inothing is selected..
17172                 this.fireEvent('edit', this, this.lastData );
17173                 
17174             }, this);
17175         }
17176         */
17177         
17178         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17179             "up" : function(e){
17180                 this.inKeyMode = true;
17181                 this.selectPrev();
17182             },
17183
17184             "down" : function(e){
17185                 if(!this.isExpanded()){
17186                     this.onTriggerClick();
17187                 }else{
17188                     this.inKeyMode = true;
17189                     this.selectNext();
17190                 }
17191             },
17192
17193             "enter" : function(e){
17194 //                this.onViewClick();
17195                 //return true;
17196                 this.collapse();
17197                 
17198                 if(this.fireEvent("specialkey", this, e)){
17199                     this.onViewClick(false);
17200                 }
17201                 
17202                 return true;
17203             },
17204
17205             "esc" : function(e){
17206                 this.collapse();
17207             },
17208
17209             "tab" : function(e){
17210                 this.collapse();
17211                 
17212                 if(this.fireEvent("specialkey", this, e)){
17213                     this.onViewClick(false);
17214                 }
17215                 
17216                 return true;
17217             },
17218
17219             scope : this,
17220
17221             doRelay : function(foo, bar, hname){
17222                 if(hname == 'down' || this.scope.isExpanded()){
17223                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17224                 }
17225                 return true;
17226             },
17227
17228             forceKeyDown: true
17229         });
17230         
17231         
17232         this.queryDelay = Math.max(this.queryDelay || 10,
17233                 this.mode == 'local' ? 10 : 250);
17234         
17235         
17236         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17237         
17238         if(this.typeAhead){
17239             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17240         }
17241         if(this.editable !== false){
17242             this.inputEl().on("keyup", this.onKeyUp, this);
17243         }
17244         if(this.forceSelection){
17245             this.inputEl().on('blur', this.doForce, this);
17246         }
17247         
17248         if(this.multiple){
17249             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17250             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17251         }
17252     },
17253     
17254     initTickableEvents: function()
17255     {   
17256         this.createList();
17257         
17258         if(this.hiddenName){
17259             
17260             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17261             
17262             this.hiddenField.dom.value =
17263                 this.hiddenValue !== undefined ? this.hiddenValue :
17264                 this.value !== undefined ? this.value : '';
17265
17266             // prevent input submission
17267             this.el.dom.removeAttribute('name');
17268             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17269              
17270              
17271         }
17272         
17273 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17274         
17275         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17276         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17277         if(this.triggerList){
17278             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17279         }
17280          
17281         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17282         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17283         
17284         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17285         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17286         
17287         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17288         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17289         
17290         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17291         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17292         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17293         
17294         this.okBtn.hide();
17295         this.cancelBtn.hide();
17296         
17297         var _this = this;
17298         
17299         (function(){
17300             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17301             _this.list.setWidth(lw);
17302         }).defer(100);
17303         
17304         this.list.on('mouseover', this.onViewOver, this);
17305         this.list.on('mousemove', this.onViewMove, this);
17306         
17307         this.list.on('scroll', this.onViewScroll, this);
17308         
17309         if(!this.tpl){
17310             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17311                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17312         }
17313
17314         this.view = new Roo.View(this.list, this.tpl, {
17315             singleSelect:true,
17316             tickable:true,
17317             parent:this,
17318             store: this.store,
17319             selectedClass: this.selectedClass
17320         });
17321         
17322         //this.view.wrapEl.setDisplayed(false);
17323         this.view.on('click', this.onViewClick, this);
17324         
17325         
17326         
17327         this.store.on('beforeload', this.onBeforeLoad, this);
17328         this.store.on('load', this.onLoad, this);
17329         this.store.on('loadexception', this.onLoadException, this);
17330         
17331         if(this.editable){
17332             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17333                 "up" : function(e){
17334                     this.inKeyMode = true;
17335                     this.selectPrev();
17336                 },
17337
17338                 "down" : function(e){
17339                     this.inKeyMode = true;
17340                     this.selectNext();
17341                 },
17342
17343                 "enter" : function(e){
17344                     if(this.fireEvent("specialkey", this, e)){
17345                         this.onViewClick(false);
17346                     }
17347                     
17348                     return true;
17349                 },
17350
17351                 "esc" : function(e){
17352                     this.onTickableFooterButtonClick(e, false, false);
17353                 },
17354
17355                 "tab" : function(e){
17356                     this.fireEvent("specialkey", this, e);
17357                     
17358                     this.onTickableFooterButtonClick(e, false, false);
17359                     
17360                     return true;
17361                 },
17362
17363                 scope : this,
17364
17365                 doRelay : function(e, fn, key){
17366                     if(this.scope.isExpanded()){
17367                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17368                     }
17369                     return true;
17370                 },
17371
17372                 forceKeyDown: true
17373             });
17374         }
17375         
17376         this.queryDelay = Math.max(this.queryDelay || 10,
17377                 this.mode == 'local' ? 10 : 250);
17378         
17379         
17380         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17381         
17382         if(this.typeAhead){
17383             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17384         }
17385         
17386         if(this.editable !== false){
17387             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17388         }
17389         
17390         this.indicator = this.indicatorEl();
17391         
17392         if(this.indicator){
17393             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17394             this.indicator.hide();
17395         }
17396         
17397     },
17398
17399     onDestroy : function(){
17400         if(this.view){
17401             this.view.setStore(null);
17402             this.view.el.removeAllListeners();
17403             this.view.el.remove();
17404             this.view.purgeListeners();
17405         }
17406         if(this.list){
17407             this.list.dom.innerHTML  = '';
17408         }
17409         
17410         if(this.store){
17411             this.store.un('beforeload', this.onBeforeLoad, this);
17412             this.store.un('load', this.onLoad, this);
17413             this.store.un('loadexception', this.onLoadException, this);
17414         }
17415         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17416     },
17417
17418     // private
17419     fireKey : function(e){
17420         if(e.isNavKeyPress() && !this.list.isVisible()){
17421             this.fireEvent("specialkey", this, e);
17422         }
17423     },
17424
17425     // private
17426     onResize: function(w, h)
17427     {
17428         
17429         
17430 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17431 //        
17432 //        if(typeof w != 'number'){
17433 //            // we do not handle it!?!?
17434 //            return;
17435 //        }
17436 //        var tw = this.trigger.getWidth();
17437 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17438 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17439 //        var x = w - tw;
17440 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17441 //            
17442 //        //this.trigger.setStyle('left', x+'px');
17443 //        
17444 //        if(this.list && this.listWidth === undefined){
17445 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17446 //            this.list.setWidth(lw);
17447 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17448 //        }
17449         
17450     
17451         
17452     },
17453
17454     /**
17455      * Allow or prevent the user from directly editing the field text.  If false is passed,
17456      * the user will only be able to select from the items defined in the dropdown list.  This method
17457      * is the runtime equivalent of setting the 'editable' config option at config time.
17458      * @param {Boolean} value True to allow the user to directly edit the field text
17459      */
17460     setEditable : function(value){
17461         if(value == this.editable){
17462             return;
17463         }
17464         this.editable = value;
17465         if(!value){
17466             this.inputEl().dom.setAttribute('readOnly', true);
17467             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17468             this.inputEl().addClass('x-combo-noedit');
17469         }else{
17470             this.inputEl().dom.removeAttribute('readOnly');
17471             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17472             this.inputEl().removeClass('x-combo-noedit');
17473         }
17474     },
17475
17476     // private
17477     
17478     onBeforeLoad : function(combo,opts){
17479         if(!this.hasFocus){
17480             return;
17481         }
17482          if (!opts.add) {
17483             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17484          }
17485         this.restrictHeight();
17486         this.selectedIndex = -1;
17487     },
17488
17489     // private
17490     onLoad : function(){
17491         
17492         this.hasQuery = false;
17493         
17494         if(!this.hasFocus){
17495             return;
17496         }
17497         
17498         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17499             this.loading.hide();
17500         }
17501         
17502         if(this.store.getCount() > 0){
17503             
17504             this.expand();
17505             this.restrictHeight();
17506             if(this.lastQuery == this.allQuery){
17507                 if(this.editable && !this.tickable){
17508                     this.inputEl().dom.select();
17509                 }
17510                 
17511                 if(
17512                     !this.selectByValue(this.value, true) &&
17513                     this.autoFocus && 
17514                     (
17515                         !this.store.lastOptions ||
17516                         typeof(this.store.lastOptions.add) == 'undefined' || 
17517                         this.store.lastOptions.add != true
17518                     )
17519                 ){
17520                     this.select(0, true);
17521                 }
17522             }else{
17523                 if(this.autoFocus){
17524                     this.selectNext();
17525                 }
17526                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17527                     this.taTask.delay(this.typeAheadDelay);
17528                 }
17529             }
17530         }else{
17531             this.onEmptyResults();
17532         }
17533         
17534         //this.el.focus();
17535     },
17536     // private
17537     onLoadException : function()
17538     {
17539         this.hasQuery = false;
17540         
17541         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17542             this.loading.hide();
17543         }
17544         
17545         if(this.tickable && this.editable){
17546             return;
17547         }
17548         
17549         this.collapse();
17550         // only causes errors at present
17551         //Roo.log(this.store.reader.jsonData);
17552         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17553             // fixme
17554             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17555         //}
17556         
17557         
17558     },
17559     // private
17560     onTypeAhead : function(){
17561         if(this.store.getCount() > 0){
17562             var r = this.store.getAt(0);
17563             var newValue = r.data[this.displayField];
17564             var len = newValue.length;
17565             var selStart = this.getRawValue().length;
17566             
17567             if(selStart != len){
17568                 this.setRawValue(newValue);
17569                 this.selectText(selStart, newValue.length);
17570             }
17571         }
17572     },
17573
17574     // private
17575     onSelect : function(record, index){
17576         
17577         if(this.fireEvent('beforeselect', this, record, index) !== false){
17578         
17579             this.setFromData(index > -1 ? record.data : false);
17580             
17581             this.collapse();
17582             this.fireEvent('select', this, record, index);
17583         }
17584     },
17585
17586     /**
17587      * Returns the currently selected field value or empty string if no value is set.
17588      * @return {String} value The selected value
17589      */
17590     getValue : function()
17591     {
17592         if(Roo.isIOS && this.useNativeIOS){
17593             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17594         }
17595         
17596         if(this.multiple){
17597             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17598         }
17599         
17600         if(this.valueField){
17601             return typeof this.value != 'undefined' ? this.value : '';
17602         }else{
17603             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17604         }
17605     },
17606     
17607     getRawValue : function()
17608     {
17609         if(Roo.isIOS && this.useNativeIOS){
17610             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17611         }
17612         
17613         var v = this.inputEl().getValue();
17614         
17615         return v;
17616     },
17617
17618     /**
17619      * Clears any text/value currently set in the field
17620      */
17621     clearValue : function(){
17622         
17623         if(this.hiddenField){
17624             this.hiddenField.dom.value = '';
17625         }
17626         this.value = '';
17627         this.setRawValue('');
17628         this.lastSelectionText = '';
17629         this.lastData = false;
17630         
17631         var close = this.closeTriggerEl();
17632         
17633         if(close){
17634             close.hide();
17635         }
17636         
17637         this.validate();
17638         
17639     },
17640
17641     /**
17642      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17643      * will be displayed in the field.  If the value does not match the data value of an existing item,
17644      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17645      * Otherwise the field will be blank (although the value will still be set).
17646      * @param {String} value The value to match
17647      */
17648     setValue : function(v)
17649     {
17650         if(Roo.isIOS && this.useNativeIOS){
17651             this.setIOSValue(v);
17652             return;
17653         }
17654         
17655         if(this.multiple){
17656             this.syncValue();
17657             return;
17658         }
17659         
17660         var text = v;
17661         if(this.valueField){
17662             var r = this.findRecord(this.valueField, v);
17663             if(r){
17664                 text = r.data[this.displayField];
17665             }else if(this.valueNotFoundText !== undefined){
17666                 text = this.valueNotFoundText;
17667             }
17668         }
17669         this.lastSelectionText = text;
17670         if(this.hiddenField){
17671             this.hiddenField.dom.value = v;
17672         }
17673         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17674         this.value = v;
17675         
17676         var close = this.closeTriggerEl();
17677         
17678         if(close){
17679             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17680         }
17681         
17682         this.validate();
17683     },
17684     /**
17685      * @property {Object} the last set data for the element
17686      */
17687     
17688     lastData : false,
17689     /**
17690      * Sets the value of the field based on a object which is related to the record format for the store.
17691      * @param {Object} value the value to set as. or false on reset?
17692      */
17693     setFromData : function(o){
17694         
17695         if(this.multiple){
17696             this.addItem(o);
17697             return;
17698         }
17699             
17700         var dv = ''; // display value
17701         var vv = ''; // value value..
17702         this.lastData = o;
17703         if (this.displayField) {
17704             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17705         } else {
17706             // this is an error condition!!!
17707             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17708         }
17709         
17710         if(this.valueField){
17711             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17712         }
17713         
17714         var close = this.closeTriggerEl();
17715         
17716         if(close){
17717             if(dv.length || vv * 1 > 0){
17718                 close.show() ;
17719                 this.blockFocus=true;
17720             } else {
17721                 close.hide();
17722             }             
17723         }
17724         
17725         if(this.hiddenField){
17726             this.hiddenField.dom.value = vv;
17727             
17728             this.lastSelectionText = dv;
17729             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17730             this.value = vv;
17731             return;
17732         }
17733         // no hidden field.. - we store the value in 'value', but still display
17734         // display field!!!!
17735         this.lastSelectionText = dv;
17736         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17737         this.value = vv;
17738         
17739         
17740         
17741     },
17742     // private
17743     reset : function(){
17744         // overridden so that last data is reset..
17745         
17746         if(this.multiple){
17747             this.clearItem();
17748             return;
17749         }
17750         
17751         this.setValue(this.originalValue);
17752         //this.clearInvalid();
17753         this.lastData = false;
17754         if (this.view) {
17755             this.view.clearSelections();
17756         }
17757         
17758         this.validate();
17759     },
17760     // private
17761     findRecord : function(prop, value){
17762         var record;
17763         if(this.store.getCount() > 0){
17764             this.store.each(function(r){
17765                 if(r.data[prop] == value){
17766                     record = r;
17767                     return false;
17768                 }
17769                 return true;
17770             });
17771         }
17772         return record;
17773     },
17774     
17775     getName: function()
17776     {
17777         // returns hidden if it's set..
17778         if (!this.rendered) {return ''};
17779         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17780         
17781     },
17782     // private
17783     onViewMove : function(e, t){
17784         this.inKeyMode = false;
17785     },
17786
17787     // private
17788     onViewOver : function(e, t){
17789         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17790             return;
17791         }
17792         var item = this.view.findItemFromChild(t);
17793         
17794         if(item){
17795             var index = this.view.indexOf(item);
17796             this.select(index, false);
17797         }
17798     },
17799
17800     // private
17801     onViewClick : function(view, doFocus, el, e)
17802     {
17803         var index = this.view.getSelectedIndexes()[0];
17804         
17805         var r = this.store.getAt(index);
17806         
17807         if(this.tickable){
17808             
17809             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17810                 return;
17811             }
17812             
17813             var rm = false;
17814             var _this = this;
17815             
17816             Roo.each(this.tickItems, function(v,k){
17817                 
17818                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17819                     Roo.log(v);
17820                     _this.tickItems.splice(k, 1);
17821                     
17822                     if(typeof(e) == 'undefined' && view == false){
17823                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17824                     }
17825                     
17826                     rm = true;
17827                     return;
17828                 }
17829             });
17830             
17831             if(rm){
17832                 return;
17833             }
17834             
17835             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17836                 this.tickItems.push(r.data);
17837             }
17838             
17839             if(typeof(e) == 'undefined' && view == false){
17840                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17841             }
17842                     
17843             return;
17844         }
17845         
17846         if(r){
17847             this.onSelect(r, index);
17848         }
17849         if(doFocus !== false && !this.blockFocus){
17850             this.inputEl().focus();
17851         }
17852     },
17853
17854     // private
17855     restrictHeight : function(){
17856         //this.innerList.dom.style.height = '';
17857         //var inner = this.innerList.dom;
17858         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17859         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17860         //this.list.beginUpdate();
17861         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17862         this.list.alignTo(this.inputEl(), this.listAlign);
17863         this.list.alignTo(this.inputEl(), this.listAlign);
17864         //this.list.endUpdate();
17865     },
17866
17867     // private
17868     onEmptyResults : function(){
17869         
17870         if(this.tickable && this.editable){
17871             this.hasFocus = false;
17872             this.restrictHeight();
17873             return;
17874         }
17875         
17876         this.collapse();
17877     },
17878
17879     /**
17880      * Returns true if the dropdown list is expanded, else false.
17881      */
17882     isExpanded : function(){
17883         return this.list.isVisible();
17884     },
17885
17886     /**
17887      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17888      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17889      * @param {String} value The data value of the item to select
17890      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17891      * selected item if it is not currently in view (defaults to true)
17892      * @return {Boolean} True if the value matched an item in the list, else false
17893      */
17894     selectByValue : function(v, scrollIntoView){
17895         if(v !== undefined && v !== null){
17896             var r = this.findRecord(this.valueField || this.displayField, v);
17897             if(r){
17898                 this.select(this.store.indexOf(r), scrollIntoView);
17899                 return true;
17900             }
17901         }
17902         return false;
17903     },
17904
17905     /**
17906      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17907      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17908      * @param {Number} index The zero-based index of the list item to select
17909      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17910      * selected item if it is not currently in view (defaults to true)
17911      */
17912     select : function(index, scrollIntoView){
17913         this.selectedIndex = index;
17914         this.view.select(index);
17915         if(scrollIntoView !== false){
17916             var el = this.view.getNode(index);
17917             /*
17918              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17919              */
17920             if(el){
17921                 this.list.scrollChildIntoView(el, false);
17922             }
17923         }
17924     },
17925
17926     // private
17927     selectNext : function(){
17928         var ct = this.store.getCount();
17929         if(ct > 0){
17930             if(this.selectedIndex == -1){
17931                 this.select(0);
17932             }else if(this.selectedIndex < ct-1){
17933                 this.select(this.selectedIndex+1);
17934             }
17935         }
17936     },
17937
17938     // private
17939     selectPrev : function(){
17940         var ct = this.store.getCount();
17941         if(ct > 0){
17942             if(this.selectedIndex == -1){
17943                 this.select(0);
17944             }else if(this.selectedIndex != 0){
17945                 this.select(this.selectedIndex-1);
17946             }
17947         }
17948     },
17949
17950     // private
17951     onKeyUp : function(e){
17952         if(this.editable !== false && !e.isSpecialKey()){
17953             this.lastKey = e.getKey();
17954             this.dqTask.delay(this.queryDelay);
17955         }
17956     },
17957
17958     // private
17959     validateBlur : function(){
17960         return !this.list || !this.list.isVisible();   
17961     },
17962
17963     // private
17964     initQuery : function(){
17965         
17966         var v = this.getRawValue();
17967         
17968         if(this.tickable && this.editable){
17969             v = this.tickableInputEl().getValue();
17970         }
17971         
17972         this.doQuery(v);
17973     },
17974
17975     // private
17976     doForce : function(){
17977         if(this.inputEl().dom.value.length > 0){
17978             this.inputEl().dom.value =
17979                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17980              
17981         }
17982     },
17983
17984     /**
17985      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17986      * query allowing the query action to be canceled if needed.
17987      * @param {String} query The SQL query to execute
17988      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17989      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17990      * saved in the current store (defaults to false)
17991      */
17992     doQuery : function(q, forceAll){
17993         
17994         if(q === undefined || q === null){
17995             q = '';
17996         }
17997         var qe = {
17998             query: q,
17999             forceAll: forceAll,
18000             combo: this,
18001             cancel:false
18002         };
18003         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18004             return false;
18005         }
18006         q = qe.query;
18007         
18008         forceAll = qe.forceAll;
18009         if(forceAll === true || (q.length >= this.minChars)){
18010             
18011             this.hasQuery = true;
18012             
18013             if(this.lastQuery != q || this.alwaysQuery){
18014                 this.lastQuery = q;
18015                 if(this.mode == 'local'){
18016                     this.selectedIndex = -1;
18017                     if(forceAll){
18018                         this.store.clearFilter();
18019                     }else{
18020                         
18021                         if(this.specialFilter){
18022                             this.fireEvent('specialfilter', this);
18023                             this.onLoad();
18024                             return;
18025                         }
18026                         
18027                         this.store.filter(this.displayField, q);
18028                     }
18029                     
18030                     this.store.fireEvent("datachanged", this.store);
18031                     
18032                     this.onLoad();
18033                     
18034                     
18035                 }else{
18036                     
18037                     this.store.baseParams[this.queryParam] = q;
18038                     
18039                     var options = {params : this.getParams(q)};
18040                     
18041                     if(this.loadNext){
18042                         options.add = true;
18043                         options.params.start = this.page * this.pageSize;
18044                     }
18045                     
18046                     this.store.load(options);
18047                     
18048                     /*
18049                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18050                      *  we should expand the list on onLoad
18051                      *  so command out it
18052                      */
18053 //                    this.expand();
18054                 }
18055             }else{
18056                 this.selectedIndex = -1;
18057                 this.onLoad();   
18058             }
18059         }
18060         
18061         this.loadNext = false;
18062     },
18063     
18064     // private
18065     getParams : function(q){
18066         var p = {};
18067         //p[this.queryParam] = q;
18068         
18069         if(this.pageSize){
18070             p.start = 0;
18071             p.limit = this.pageSize;
18072         }
18073         return p;
18074     },
18075
18076     /**
18077      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18078      */
18079     collapse : function(){
18080         if(!this.isExpanded()){
18081             return;
18082         }
18083         
18084         this.list.hide();
18085         
18086         this.hasFocus = false;
18087         
18088         if(this.tickable){
18089             this.okBtn.hide();
18090             this.cancelBtn.hide();
18091             this.trigger.show();
18092             
18093             if(this.editable){
18094                 this.tickableInputEl().dom.value = '';
18095                 this.tickableInputEl().blur();
18096             }
18097             
18098         }
18099         
18100         Roo.get(document).un('mousedown', this.collapseIf, this);
18101         Roo.get(document).un('mousewheel', this.collapseIf, this);
18102         if (!this.editable) {
18103             Roo.get(document).un('keydown', this.listKeyPress, this);
18104         }
18105         this.fireEvent('collapse', this);
18106         
18107         this.validate();
18108     },
18109
18110     // private
18111     collapseIf : function(e){
18112         var in_combo  = e.within(this.el);
18113         var in_list =  e.within(this.list);
18114         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18115         
18116         if (in_combo || in_list || is_list) {
18117             //e.stopPropagation();
18118             return;
18119         }
18120         
18121         if(this.tickable){
18122             this.onTickableFooterButtonClick(e, false, false);
18123         }
18124
18125         this.collapse();
18126         
18127     },
18128
18129     /**
18130      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18131      */
18132     expand : function(){
18133        
18134         if(this.isExpanded() || !this.hasFocus){
18135             return;
18136         }
18137         
18138         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18139         this.list.setWidth(lw);
18140         
18141         Roo.log('expand');
18142         
18143         this.list.show();
18144         
18145         this.restrictHeight();
18146         
18147         if(this.tickable){
18148             
18149             this.tickItems = Roo.apply([], this.item);
18150             
18151             this.okBtn.show();
18152             this.cancelBtn.show();
18153             this.trigger.hide();
18154             
18155             if(this.editable){
18156                 this.tickableInputEl().focus();
18157             }
18158             
18159         }
18160         
18161         Roo.get(document).on('mousedown', this.collapseIf, this);
18162         Roo.get(document).on('mousewheel', this.collapseIf, this);
18163         if (!this.editable) {
18164             Roo.get(document).on('keydown', this.listKeyPress, this);
18165         }
18166         
18167         this.fireEvent('expand', this);
18168     },
18169
18170     // private
18171     // Implements the default empty TriggerField.onTriggerClick function
18172     onTriggerClick : function(e)
18173     {
18174         Roo.log('trigger click');
18175         
18176         if(this.disabled || !this.triggerList){
18177             return;
18178         }
18179         
18180         this.page = 0;
18181         this.loadNext = false;
18182         
18183         if(this.isExpanded()){
18184             this.collapse();
18185             if (!this.blockFocus) {
18186                 this.inputEl().focus();
18187             }
18188             
18189         }else {
18190             this.hasFocus = true;
18191             if(this.triggerAction == 'all') {
18192                 this.doQuery(this.allQuery, true);
18193             } else {
18194                 this.doQuery(this.getRawValue());
18195             }
18196             if (!this.blockFocus) {
18197                 this.inputEl().focus();
18198             }
18199         }
18200     },
18201     
18202     onTickableTriggerClick : function(e)
18203     {
18204         if(this.disabled){
18205             return;
18206         }
18207         
18208         this.page = 0;
18209         this.loadNext = false;
18210         this.hasFocus = true;
18211         
18212         if(this.triggerAction == 'all') {
18213             this.doQuery(this.allQuery, true);
18214         } else {
18215             this.doQuery(this.getRawValue());
18216         }
18217     },
18218     
18219     onSearchFieldClick : function(e)
18220     {
18221         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18222             this.onTickableFooterButtonClick(e, false, false);
18223             return;
18224         }
18225         
18226         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18227             return;
18228         }
18229         
18230         this.page = 0;
18231         this.loadNext = false;
18232         this.hasFocus = true;
18233         
18234         if(this.triggerAction == 'all') {
18235             this.doQuery(this.allQuery, true);
18236         } else {
18237             this.doQuery(this.getRawValue());
18238         }
18239     },
18240     
18241     listKeyPress : function(e)
18242     {
18243         //Roo.log('listkeypress');
18244         // scroll to first matching element based on key pres..
18245         if (e.isSpecialKey()) {
18246             return false;
18247         }
18248         var k = String.fromCharCode(e.getKey()).toUpperCase();
18249         //Roo.log(k);
18250         var match  = false;
18251         var csel = this.view.getSelectedNodes();
18252         var cselitem = false;
18253         if (csel.length) {
18254             var ix = this.view.indexOf(csel[0]);
18255             cselitem  = this.store.getAt(ix);
18256             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18257                 cselitem = false;
18258             }
18259             
18260         }
18261         
18262         this.store.each(function(v) { 
18263             if (cselitem) {
18264                 // start at existing selection.
18265                 if (cselitem.id == v.id) {
18266                     cselitem = false;
18267                 }
18268                 return true;
18269             }
18270                 
18271             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18272                 match = this.store.indexOf(v);
18273                 return false;
18274             }
18275             return true;
18276         }, this);
18277         
18278         if (match === false) {
18279             return true; // no more action?
18280         }
18281         // scroll to?
18282         this.view.select(match);
18283         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18284         sn.scrollIntoView(sn.dom.parentNode, false);
18285     },
18286     
18287     onViewScroll : function(e, t){
18288         
18289         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){
18290             return;
18291         }
18292         
18293         this.hasQuery = true;
18294         
18295         this.loading = this.list.select('.loading', true).first();
18296         
18297         if(this.loading === null){
18298             this.list.createChild({
18299                 tag: 'div',
18300                 cls: 'loading roo-select2-more-results roo-select2-active',
18301                 html: 'Loading more results...'
18302             });
18303             
18304             this.loading = this.list.select('.loading', true).first();
18305             
18306             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18307             
18308             this.loading.hide();
18309         }
18310         
18311         this.loading.show();
18312         
18313         var _combo = this;
18314         
18315         this.page++;
18316         this.loadNext = true;
18317         
18318         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18319         
18320         return;
18321     },
18322     
18323     addItem : function(o)
18324     {   
18325         var dv = ''; // display value
18326         
18327         if (this.displayField) {
18328             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18329         } else {
18330             // this is an error condition!!!
18331             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18332         }
18333         
18334         if(!dv.length){
18335             return;
18336         }
18337         
18338         var choice = this.choices.createChild({
18339             tag: 'li',
18340             cls: 'roo-select2-search-choice',
18341             cn: [
18342                 {
18343                     tag: 'div',
18344                     html: dv
18345                 },
18346                 {
18347                     tag: 'a',
18348                     href: '#',
18349                     cls: 'roo-select2-search-choice-close fa fa-times',
18350                     tabindex: '-1'
18351                 }
18352             ]
18353             
18354         }, this.searchField);
18355         
18356         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18357         
18358         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18359         
18360         this.item.push(o);
18361         
18362         this.lastData = o;
18363         
18364         this.syncValue();
18365         
18366         this.inputEl().dom.value = '';
18367         
18368         this.validate();
18369     },
18370     
18371     onRemoveItem : function(e, _self, o)
18372     {
18373         e.preventDefault();
18374         
18375         this.lastItem = Roo.apply([], this.item);
18376         
18377         var index = this.item.indexOf(o.data) * 1;
18378         
18379         if( index < 0){
18380             Roo.log('not this item?!');
18381             return;
18382         }
18383         
18384         this.item.splice(index, 1);
18385         o.item.remove();
18386         
18387         this.syncValue();
18388         
18389         this.fireEvent('remove', this, e);
18390         
18391         this.validate();
18392         
18393     },
18394     
18395     syncValue : function()
18396     {
18397         if(!this.item.length){
18398             this.clearValue();
18399             return;
18400         }
18401             
18402         var value = [];
18403         var _this = this;
18404         Roo.each(this.item, function(i){
18405             if(_this.valueField){
18406                 value.push(i[_this.valueField]);
18407                 return;
18408             }
18409
18410             value.push(i);
18411         });
18412
18413         this.value = value.join(',');
18414
18415         if(this.hiddenField){
18416             this.hiddenField.dom.value = this.value;
18417         }
18418         
18419         this.store.fireEvent("datachanged", this.store);
18420         
18421         this.validate();
18422     },
18423     
18424     clearItem : function()
18425     {
18426         if(!this.multiple){
18427             return;
18428         }
18429         
18430         this.item = [];
18431         
18432         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18433            c.remove();
18434         });
18435         
18436         this.syncValue();
18437         
18438         this.validate();
18439         
18440         if(this.tickable && !Roo.isTouch){
18441             this.view.refresh();
18442         }
18443     },
18444     
18445     inputEl: function ()
18446     {
18447         if(Roo.isIOS && this.useNativeIOS){
18448             return this.el.select('select.roo-ios-select', true).first();
18449         }
18450         
18451         if(Roo.isTouch && this.mobileTouchView){
18452             return this.el.select('input.form-control',true).first();
18453         }
18454         
18455         if(this.tickable){
18456             return this.searchField;
18457         }
18458         
18459         return this.el.select('input.form-control',true).first();
18460     },
18461     
18462     onTickableFooterButtonClick : function(e, btn, el)
18463     {
18464         e.preventDefault();
18465         
18466         this.lastItem = Roo.apply([], this.item);
18467         
18468         if(btn && btn.name == 'cancel'){
18469             this.tickItems = Roo.apply([], this.item);
18470             this.collapse();
18471             return;
18472         }
18473         
18474         this.clearItem();
18475         
18476         var _this = this;
18477         
18478         Roo.each(this.tickItems, function(o){
18479             _this.addItem(o);
18480         });
18481         
18482         this.collapse();
18483         
18484     },
18485     
18486     validate : function()
18487     {
18488         if(this.getVisibilityEl().hasClass('hidden')){
18489             return true;
18490         }
18491         
18492         var v = this.getRawValue();
18493         
18494         if(this.multiple){
18495             v = this.getValue();
18496         }
18497         
18498         if(this.disabled || this.allowBlank || v.length){
18499             this.markValid();
18500             return true;
18501         }
18502         
18503         this.markInvalid();
18504         return false;
18505     },
18506     
18507     tickableInputEl : function()
18508     {
18509         if(!this.tickable || !this.editable){
18510             return this.inputEl();
18511         }
18512         
18513         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18514     },
18515     
18516     
18517     getAutoCreateTouchView : function()
18518     {
18519         var id = Roo.id();
18520         
18521         var cfg = {
18522             cls: 'form-group' //input-group
18523         };
18524         
18525         var input =  {
18526             tag: 'input',
18527             id : id,
18528             type : this.inputType,
18529             cls : 'form-control x-combo-noedit',
18530             autocomplete: 'new-password',
18531             placeholder : this.placeholder || '',
18532             readonly : true
18533         };
18534         
18535         if (this.name) {
18536             input.name = this.name;
18537         }
18538         
18539         if (this.size) {
18540             input.cls += ' input-' + this.size;
18541         }
18542         
18543         if (this.disabled) {
18544             input.disabled = true;
18545         }
18546         
18547         var inputblock = {
18548             cls : 'roo-combobox-wrap',
18549             cn : [
18550                 input
18551             ]
18552         };
18553         
18554         if(this.before){
18555             inputblock.cls += ' input-group';
18556             
18557             inputblock.cn.unshift({
18558                 tag :'span',
18559                 cls : 'input-group-addon input-group-prepend input-group-text',
18560                 html : this.before
18561             });
18562         }
18563         
18564         if(this.removable && !this.multiple){
18565             inputblock.cls += ' roo-removable';
18566             
18567             inputblock.cn.push({
18568                 tag: 'button',
18569                 html : 'x',
18570                 cls : 'roo-combo-removable-btn close'
18571             });
18572         }
18573
18574         if(this.hasFeedback && !this.allowBlank){
18575             
18576             inputblock.cls += ' has-feedback';
18577             
18578             inputblock.cn.push({
18579                 tag: 'span',
18580                 cls: 'glyphicon form-control-feedback'
18581             });
18582             
18583         }
18584         
18585         if (this.after) {
18586             
18587             inputblock.cls += (this.before) ? '' : ' input-group';
18588             
18589             inputblock.cn.push({
18590                 tag :'span',
18591                 cls : 'input-group-addon input-group-append input-group-text',
18592                 html : this.after
18593             });
18594         }
18595
18596         
18597         var ibwrap = inputblock;
18598         
18599         if(this.multiple){
18600             ibwrap = {
18601                 tag: 'ul',
18602                 cls: 'roo-select2-choices',
18603                 cn:[
18604                     {
18605                         tag: 'li',
18606                         cls: 'roo-select2-search-field',
18607                         cn: [
18608
18609                             inputblock
18610                         ]
18611                     }
18612                 ]
18613             };
18614         
18615             
18616         }
18617         
18618         var combobox = {
18619             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18620             cn: [
18621                 {
18622                     tag: 'input',
18623                     type : 'hidden',
18624                     cls: 'form-hidden-field'
18625                 },
18626                 ibwrap
18627             ]
18628         };
18629         
18630         if(!this.multiple && this.showToggleBtn){
18631             
18632             var caret = {
18633                 cls: 'caret'
18634             };
18635             
18636             if (this.caret != false) {
18637                 caret = {
18638                      tag: 'i',
18639                      cls: 'fa fa-' + this.caret
18640                 };
18641                 
18642             }
18643             
18644             combobox.cn.push({
18645                 tag :'span',
18646                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18647                 cn : [
18648                     Roo.bootstrap.version == 3 ? caret : '',
18649                     {
18650                         tag: 'span',
18651                         cls: 'combobox-clear',
18652                         cn  : [
18653                             {
18654                                 tag : 'i',
18655                                 cls: 'icon-remove'
18656                             }
18657                         ]
18658                     }
18659                 ]
18660
18661             })
18662         }
18663         
18664         if(this.multiple){
18665             combobox.cls += ' roo-select2-container-multi';
18666         }
18667         
18668         var required =  this.allowBlank ?  {
18669                     tag : 'i',
18670                     style: 'display: none'
18671                 } : {
18672                    tag : 'i',
18673                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18674                    tooltip : 'This field is required'
18675                 };
18676         
18677         var align = this.labelAlign || this.parentLabelAlign();
18678         
18679         if (align ==='left' && this.fieldLabel.length) {
18680
18681             cfg.cn = [
18682                 required,
18683                 {
18684                     tag: 'label',
18685                     cls : 'control-label col-form-label',
18686                     html : this.fieldLabel
18687
18688                 },
18689                 {
18690                     cls : 'roo-combobox-wrap ', 
18691                     cn: [
18692                         combobox
18693                     ]
18694                 }
18695             ];
18696             
18697             var labelCfg = cfg.cn[1];
18698             var contentCfg = cfg.cn[2];
18699             
18700
18701             if(this.indicatorpos == 'right'){
18702                 cfg.cn = [
18703                     {
18704                         tag: 'label',
18705                         'for' :  id,
18706                         cls : 'control-label col-form-label',
18707                         cn : [
18708                             {
18709                                 tag : 'span',
18710                                 html : this.fieldLabel
18711                             },
18712                             required
18713                         ]
18714                     },
18715                     {
18716                         cls : "roo-combobox-wrap ",
18717                         cn: [
18718                             combobox
18719                         ]
18720                     }
18721
18722                 ];
18723                 
18724                 labelCfg = cfg.cn[0];
18725                 contentCfg = cfg.cn[1];
18726             }
18727             
18728            
18729             
18730             if(this.labelWidth > 12){
18731                 labelCfg.style = "width: " + this.labelWidth + 'px';
18732             }
18733            
18734             if(this.labelWidth < 13 && this.labelmd == 0){
18735                 this.labelmd = this.labelWidth;
18736             }
18737             
18738             if(this.labellg > 0){
18739                 labelCfg.cls += ' col-lg-' + this.labellg;
18740                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18741             }
18742             
18743             if(this.labelmd > 0){
18744                 labelCfg.cls += ' col-md-' + this.labelmd;
18745                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18746             }
18747             
18748             if(this.labelsm > 0){
18749                 labelCfg.cls += ' col-sm-' + this.labelsm;
18750                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18751             }
18752             
18753             if(this.labelxs > 0){
18754                 labelCfg.cls += ' col-xs-' + this.labelxs;
18755                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18756             }
18757                 
18758                 
18759         } else if ( this.fieldLabel.length) {
18760             cfg.cn = [
18761                required,
18762                 {
18763                     tag: 'label',
18764                     cls : 'control-label',
18765                     html : this.fieldLabel
18766
18767                 },
18768                 {
18769                     cls : '', 
18770                     cn: [
18771                         combobox
18772                     ]
18773                 }
18774             ];
18775             
18776             if(this.indicatorpos == 'right'){
18777                 cfg.cn = [
18778                     {
18779                         tag: 'label',
18780                         cls : 'control-label',
18781                         html : this.fieldLabel,
18782                         cn : [
18783                             required
18784                         ]
18785                     },
18786                     {
18787                         cls : '', 
18788                         cn: [
18789                             combobox
18790                         ]
18791                     }
18792                 ];
18793             }
18794         } else {
18795             cfg.cn = combobox;    
18796         }
18797         
18798         
18799         var settings = this;
18800         
18801         ['xs','sm','md','lg'].map(function(size){
18802             if (settings[size]) {
18803                 cfg.cls += ' col-' + size + '-' + settings[size];
18804             }
18805         });
18806         
18807         return cfg;
18808     },
18809     
18810     initTouchView : function()
18811     {
18812         this.renderTouchView();
18813         
18814         this.touchViewEl.on('scroll', function(){
18815             this.el.dom.scrollTop = 0;
18816         }, this);
18817         
18818         this.originalValue = this.getValue();
18819         
18820         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18821         
18822         this.inputEl().on("click", this.showTouchView, this);
18823         if (this.triggerEl) {
18824             this.triggerEl.on("click", this.showTouchView, this);
18825         }
18826         
18827         
18828         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18829         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18830         
18831         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18832         
18833         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18834         this.store.on('load', this.onTouchViewLoad, this);
18835         this.store.on('loadexception', this.onTouchViewLoadException, this);
18836         
18837         if(this.hiddenName){
18838             
18839             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18840             
18841             this.hiddenField.dom.value =
18842                 this.hiddenValue !== undefined ? this.hiddenValue :
18843                 this.value !== undefined ? this.value : '';
18844         
18845             this.el.dom.removeAttribute('name');
18846             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18847         }
18848         
18849         if(this.multiple){
18850             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18851             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18852         }
18853         
18854         if(this.removable && !this.multiple){
18855             var close = this.closeTriggerEl();
18856             if(close){
18857                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18858                 close.on('click', this.removeBtnClick, this, close);
18859             }
18860         }
18861         /*
18862          * fix the bug in Safari iOS8
18863          */
18864         this.inputEl().on("focus", function(e){
18865             document.activeElement.blur();
18866         }, this);
18867         
18868         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18869         
18870         return;
18871         
18872         
18873     },
18874     
18875     renderTouchView : function()
18876     {
18877         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18878         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18879         
18880         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18881         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18882         
18883         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18884         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18885         this.touchViewBodyEl.setStyle('overflow', 'auto');
18886         
18887         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18888         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18889         
18890         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18891         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18892         
18893     },
18894     
18895     showTouchView : function()
18896     {
18897         if(this.disabled){
18898             return;
18899         }
18900         
18901         this.touchViewHeaderEl.hide();
18902
18903         if(this.modalTitle.length){
18904             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18905             this.touchViewHeaderEl.show();
18906         }
18907
18908         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18909         this.touchViewEl.show();
18910
18911         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18912         
18913         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18914         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18915
18916         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18917
18918         if(this.modalTitle.length){
18919             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18920         }
18921         
18922         this.touchViewBodyEl.setHeight(bodyHeight);
18923
18924         if(this.animate){
18925             var _this = this;
18926             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18927         }else{
18928             this.touchViewEl.addClass(['in','show']);
18929         }
18930         
18931         if(this._touchViewMask){
18932             Roo.get(document.body).addClass("x-body-masked");
18933             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18934             this._touchViewMask.setStyle('z-index', 10000);
18935             this._touchViewMask.addClass('show');
18936         }
18937         
18938         this.doTouchViewQuery();
18939         
18940     },
18941     
18942     hideTouchView : function()
18943     {
18944         this.touchViewEl.removeClass(['in','show']);
18945
18946         if(this.animate){
18947             var _this = this;
18948             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18949         }else{
18950             this.touchViewEl.setStyle('display', 'none');
18951         }
18952         
18953         if(this._touchViewMask){
18954             this._touchViewMask.removeClass('show');
18955             Roo.get(document.body).removeClass("x-body-masked");
18956         }
18957     },
18958     
18959     setTouchViewValue : function()
18960     {
18961         if(this.multiple){
18962             this.clearItem();
18963         
18964             var _this = this;
18965
18966             Roo.each(this.tickItems, function(o){
18967                 this.addItem(o);
18968             }, this);
18969         }
18970         
18971         this.hideTouchView();
18972     },
18973     
18974     doTouchViewQuery : function()
18975     {
18976         var qe = {
18977             query: '',
18978             forceAll: true,
18979             combo: this,
18980             cancel:false
18981         };
18982         
18983         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18984             return false;
18985         }
18986         
18987         if(!this.alwaysQuery || this.mode == 'local'){
18988             this.onTouchViewLoad();
18989             return;
18990         }
18991         
18992         this.store.load();
18993     },
18994     
18995     onTouchViewBeforeLoad : function(combo,opts)
18996     {
18997         return;
18998     },
18999
19000     // private
19001     onTouchViewLoad : function()
19002     {
19003         if(this.store.getCount() < 1){
19004             this.onTouchViewEmptyResults();
19005             return;
19006         }
19007         
19008         this.clearTouchView();
19009         
19010         var rawValue = this.getRawValue();
19011         
19012         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19013         
19014         this.tickItems = [];
19015         
19016         this.store.data.each(function(d, rowIndex){
19017             var row = this.touchViewListGroup.createChild(template);
19018             
19019             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19020                 row.addClass(d.data.cls);
19021             }
19022             
19023             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19024                 var cfg = {
19025                     data : d.data,
19026                     html : d.data[this.displayField]
19027                 };
19028                 
19029                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19030                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19031                 }
19032             }
19033             row.removeClass('selected');
19034             if(!this.multiple && this.valueField &&
19035                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19036             {
19037                 // radio buttons..
19038                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19039                 row.addClass('selected');
19040             }
19041             
19042             if(this.multiple && this.valueField &&
19043                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19044             {
19045                 
19046                 // checkboxes...
19047                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19048                 this.tickItems.push(d.data);
19049             }
19050             
19051             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19052             
19053         }, this);
19054         
19055         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19056         
19057         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19058
19059         if(this.modalTitle.length){
19060             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19061         }
19062
19063         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19064         
19065         if(this.mobile_restrict_height && listHeight < bodyHeight){
19066             this.touchViewBodyEl.setHeight(listHeight);
19067         }
19068         
19069         var _this = this;
19070         
19071         if(firstChecked && listHeight > bodyHeight){
19072             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19073         }
19074         
19075     },
19076     
19077     onTouchViewLoadException : function()
19078     {
19079         this.hideTouchView();
19080     },
19081     
19082     onTouchViewEmptyResults : function()
19083     {
19084         this.clearTouchView();
19085         
19086         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19087         
19088         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19089         
19090     },
19091     
19092     clearTouchView : function()
19093     {
19094         this.touchViewListGroup.dom.innerHTML = '';
19095     },
19096     
19097     onTouchViewClick : function(e, el, o)
19098     {
19099         e.preventDefault();
19100         
19101         var row = o.row;
19102         var rowIndex = o.rowIndex;
19103         
19104         var r = this.store.getAt(rowIndex);
19105         
19106         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19107             
19108             if(!this.multiple){
19109                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19110                     c.dom.removeAttribute('checked');
19111                 }, this);
19112
19113                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19114
19115                 this.setFromData(r.data);
19116
19117                 var close = this.closeTriggerEl();
19118
19119                 if(close){
19120                     close.show();
19121                 }
19122
19123                 this.hideTouchView();
19124
19125                 this.fireEvent('select', this, r, rowIndex);
19126
19127                 return;
19128             }
19129
19130             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19131                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19132                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19133                 return;
19134             }
19135
19136             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19137             this.addItem(r.data);
19138             this.tickItems.push(r.data);
19139         }
19140     },
19141     
19142     getAutoCreateNativeIOS : function()
19143     {
19144         var cfg = {
19145             cls: 'form-group' //input-group,
19146         };
19147         
19148         var combobox =  {
19149             tag: 'select',
19150             cls : 'roo-ios-select'
19151         };
19152         
19153         if (this.name) {
19154             combobox.name = this.name;
19155         }
19156         
19157         if (this.disabled) {
19158             combobox.disabled = true;
19159         }
19160         
19161         var settings = this;
19162         
19163         ['xs','sm','md','lg'].map(function(size){
19164             if (settings[size]) {
19165                 cfg.cls += ' col-' + size + '-' + settings[size];
19166             }
19167         });
19168         
19169         cfg.cn = combobox;
19170         
19171         return cfg;
19172         
19173     },
19174     
19175     initIOSView : function()
19176     {
19177         this.store.on('load', this.onIOSViewLoad, this);
19178         
19179         return;
19180     },
19181     
19182     onIOSViewLoad : function()
19183     {
19184         if(this.store.getCount() < 1){
19185             return;
19186         }
19187         
19188         this.clearIOSView();
19189         
19190         if(this.allowBlank) {
19191             
19192             var default_text = '-- SELECT --';
19193             
19194             if(this.placeholder.length){
19195                 default_text = this.placeholder;
19196             }
19197             
19198             if(this.emptyTitle.length){
19199                 default_text += ' - ' + this.emptyTitle + ' -';
19200             }
19201             
19202             var opt = this.inputEl().createChild({
19203                 tag: 'option',
19204                 value : 0,
19205                 html : default_text
19206             });
19207             
19208             var o = {};
19209             o[this.valueField] = 0;
19210             o[this.displayField] = default_text;
19211             
19212             this.ios_options.push({
19213                 data : o,
19214                 el : opt
19215             });
19216             
19217         }
19218         
19219         this.store.data.each(function(d, rowIndex){
19220             
19221             var html = '';
19222             
19223             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19224                 html = d.data[this.displayField];
19225             }
19226             
19227             var value = '';
19228             
19229             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19230                 value = d.data[this.valueField];
19231             }
19232             
19233             var option = {
19234                 tag: 'option',
19235                 value : value,
19236                 html : html
19237             };
19238             
19239             if(this.value == d.data[this.valueField]){
19240                 option['selected'] = true;
19241             }
19242             
19243             var opt = this.inputEl().createChild(option);
19244             
19245             this.ios_options.push({
19246                 data : d.data,
19247                 el : opt
19248             });
19249             
19250         }, this);
19251         
19252         this.inputEl().on('change', function(){
19253            this.fireEvent('select', this);
19254         }, this);
19255         
19256     },
19257     
19258     clearIOSView: function()
19259     {
19260         this.inputEl().dom.innerHTML = '';
19261         
19262         this.ios_options = [];
19263     },
19264     
19265     setIOSValue: function(v)
19266     {
19267         this.value = v;
19268         
19269         if(!this.ios_options){
19270             return;
19271         }
19272         
19273         Roo.each(this.ios_options, function(opts){
19274            
19275            opts.el.dom.removeAttribute('selected');
19276            
19277            if(opts.data[this.valueField] != v){
19278                return;
19279            }
19280            
19281            opts.el.dom.setAttribute('selected', true);
19282            
19283         }, this);
19284     }
19285
19286     /** 
19287     * @cfg {Boolean} grow 
19288     * @hide 
19289     */
19290     /** 
19291     * @cfg {Number} growMin 
19292     * @hide 
19293     */
19294     /** 
19295     * @cfg {Number} growMax 
19296     * @hide 
19297     */
19298     /**
19299      * @hide
19300      * @method autoSize
19301      */
19302 });
19303
19304 Roo.apply(Roo.bootstrap.ComboBox,  {
19305     
19306     header : {
19307         tag: 'div',
19308         cls: 'modal-header',
19309         cn: [
19310             {
19311                 tag: 'h4',
19312                 cls: 'modal-title'
19313             }
19314         ]
19315     },
19316     
19317     body : {
19318         tag: 'div',
19319         cls: 'modal-body',
19320         cn: [
19321             {
19322                 tag: 'ul',
19323                 cls: 'list-group'
19324             }
19325         ]
19326     },
19327     
19328     listItemRadio : {
19329         tag: 'li',
19330         cls: 'list-group-item',
19331         cn: [
19332             {
19333                 tag: 'span',
19334                 cls: 'roo-combobox-list-group-item-value'
19335             },
19336             {
19337                 tag: 'div',
19338                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19339                 cn: [
19340                     {
19341                         tag: 'input',
19342                         type: 'radio'
19343                     },
19344                     {
19345                         tag: 'label'
19346                     }
19347                 ]
19348             }
19349         ]
19350     },
19351     
19352     listItemCheckbox : {
19353         tag: 'li',
19354         cls: 'list-group-item',
19355         cn: [
19356             {
19357                 tag: 'span',
19358                 cls: 'roo-combobox-list-group-item-value'
19359             },
19360             {
19361                 tag: 'div',
19362                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19363                 cn: [
19364                     {
19365                         tag: 'input',
19366                         type: 'checkbox'
19367                     },
19368                     {
19369                         tag: 'label'
19370                     }
19371                 ]
19372             }
19373         ]
19374     },
19375     
19376     emptyResult : {
19377         tag: 'div',
19378         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19379     },
19380     
19381     footer : {
19382         tag: 'div',
19383         cls: 'modal-footer',
19384         cn: [
19385             {
19386                 tag: 'div',
19387                 cls: 'row',
19388                 cn: [
19389                     {
19390                         tag: 'div',
19391                         cls: 'col-xs-6 text-left',
19392                         cn: {
19393                             tag: 'button',
19394                             cls: 'btn btn-danger roo-touch-view-cancel',
19395                             html: 'Cancel'
19396                         }
19397                     },
19398                     {
19399                         tag: 'div',
19400                         cls: 'col-xs-6 text-right',
19401                         cn: {
19402                             tag: 'button',
19403                             cls: 'btn btn-success roo-touch-view-ok',
19404                             html: 'OK'
19405                         }
19406                     }
19407                 ]
19408             }
19409         ]
19410         
19411     }
19412 });
19413
19414 Roo.apply(Roo.bootstrap.ComboBox,  {
19415     
19416     touchViewTemplate : {
19417         tag: 'div',
19418         cls: 'modal fade roo-combobox-touch-view',
19419         cn: [
19420             {
19421                 tag: 'div',
19422                 cls: 'modal-dialog',
19423                 style : 'position:fixed', // we have to fix position....
19424                 cn: [
19425                     {
19426                         tag: 'div',
19427                         cls: 'modal-content',
19428                         cn: [
19429                             Roo.bootstrap.ComboBox.header,
19430                             Roo.bootstrap.ComboBox.body,
19431                             Roo.bootstrap.ComboBox.footer
19432                         ]
19433                     }
19434                 ]
19435             }
19436         ]
19437     }
19438 });/*
19439  * Based on:
19440  * Ext JS Library 1.1.1
19441  * Copyright(c) 2006-2007, Ext JS, LLC.
19442  *
19443  * Originally Released Under LGPL - original licence link has changed is not relivant.
19444  *
19445  * Fork - LGPL
19446  * <script type="text/javascript">
19447  */
19448
19449 /**
19450  * @class Roo.View
19451  * @extends Roo.util.Observable
19452  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19453  * This class also supports single and multi selection modes. <br>
19454  * Create a data model bound view:
19455  <pre><code>
19456  var store = new Roo.data.Store(...);
19457
19458  var view = new Roo.View({
19459     el : "my-element",
19460     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19461  
19462     singleSelect: true,
19463     selectedClass: "ydataview-selected",
19464     store: store
19465  });
19466
19467  // listen for node click?
19468  view.on("click", function(vw, index, node, e){
19469  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19470  });
19471
19472  // load XML data
19473  dataModel.load("foobar.xml");
19474  </code></pre>
19475  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19476  * <br><br>
19477  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19478  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19479  * 
19480  * Note: old style constructor is still suported (container, template, config)
19481  * 
19482  * @constructor
19483  * Create a new View
19484  * @param {Object} config The config object
19485  * 
19486  */
19487 Roo.View = function(config, depreciated_tpl, depreciated_config){
19488     
19489     this.parent = false;
19490     
19491     if (typeof(depreciated_tpl) == 'undefined') {
19492         // new way.. - universal constructor.
19493         Roo.apply(this, config);
19494         this.el  = Roo.get(this.el);
19495     } else {
19496         // old format..
19497         this.el  = Roo.get(config);
19498         this.tpl = depreciated_tpl;
19499         Roo.apply(this, depreciated_config);
19500     }
19501     this.wrapEl  = this.el.wrap().wrap();
19502     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19503     
19504     
19505     if(typeof(this.tpl) == "string"){
19506         this.tpl = new Roo.Template(this.tpl);
19507     } else {
19508         // support xtype ctors..
19509         this.tpl = new Roo.factory(this.tpl, Roo);
19510     }
19511     
19512     
19513     this.tpl.compile();
19514     
19515     /** @private */
19516     this.addEvents({
19517         /**
19518          * @event beforeclick
19519          * Fires before a click is processed. Returns false to cancel the default action.
19520          * @param {Roo.View} this
19521          * @param {Number} index The index of the target node
19522          * @param {HTMLElement} node The target node
19523          * @param {Roo.EventObject} e The raw event object
19524          */
19525             "beforeclick" : true,
19526         /**
19527          * @event click
19528          * Fires when a template node is clicked.
19529          * @param {Roo.View} this
19530          * @param {Number} index The index of the target node
19531          * @param {HTMLElement} node The target node
19532          * @param {Roo.EventObject} e The raw event object
19533          */
19534             "click" : true,
19535         /**
19536          * @event dblclick
19537          * Fires when a template node is double clicked.
19538          * @param {Roo.View} this
19539          * @param {Number} index The index of the target node
19540          * @param {HTMLElement} node The target node
19541          * @param {Roo.EventObject} e The raw event object
19542          */
19543             "dblclick" : true,
19544         /**
19545          * @event contextmenu
19546          * Fires when a template node is right clicked.
19547          * @param {Roo.View} this
19548          * @param {Number} index The index of the target node
19549          * @param {HTMLElement} node The target node
19550          * @param {Roo.EventObject} e The raw event object
19551          */
19552             "contextmenu" : true,
19553         /**
19554          * @event selectionchange
19555          * Fires when the selected nodes change.
19556          * @param {Roo.View} this
19557          * @param {Array} selections Array of the selected nodes
19558          */
19559             "selectionchange" : true,
19560     
19561         /**
19562          * @event beforeselect
19563          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19564          * @param {Roo.View} this
19565          * @param {HTMLElement} node The node to be selected
19566          * @param {Array} selections Array of currently selected nodes
19567          */
19568             "beforeselect" : true,
19569         /**
19570          * @event preparedata
19571          * Fires on every row to render, to allow you to change the data.
19572          * @param {Roo.View} this
19573          * @param {Object} data to be rendered (change this)
19574          */
19575           "preparedata" : true
19576           
19577           
19578         });
19579
19580
19581
19582     this.el.on({
19583         "click": this.onClick,
19584         "dblclick": this.onDblClick,
19585         "contextmenu": this.onContextMenu,
19586         scope:this
19587     });
19588
19589     this.selections = [];
19590     this.nodes = [];
19591     this.cmp = new Roo.CompositeElementLite([]);
19592     if(this.store){
19593         this.store = Roo.factory(this.store, Roo.data);
19594         this.setStore(this.store, true);
19595     }
19596     
19597     if ( this.footer && this.footer.xtype) {
19598            
19599          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19600         
19601         this.footer.dataSource = this.store;
19602         this.footer.container = fctr;
19603         this.footer = Roo.factory(this.footer, Roo);
19604         fctr.insertFirst(this.el);
19605         
19606         // this is a bit insane - as the paging toolbar seems to detach the el..
19607 //        dom.parentNode.parentNode.parentNode
19608          // they get detached?
19609     }
19610     
19611     
19612     Roo.View.superclass.constructor.call(this);
19613     
19614     
19615 };
19616
19617 Roo.extend(Roo.View, Roo.util.Observable, {
19618     
19619      /**
19620      * @cfg {Roo.data.Store} store Data store to load data from.
19621      */
19622     store : false,
19623     
19624     /**
19625      * @cfg {String|Roo.Element} el The container element.
19626      */
19627     el : '',
19628     
19629     /**
19630      * @cfg {String|Roo.Template} tpl The template used by this View 
19631      */
19632     tpl : false,
19633     /**
19634      * @cfg {String} dataName the named area of the template to use as the data area
19635      *                          Works with domtemplates roo-name="name"
19636      */
19637     dataName: false,
19638     /**
19639      * @cfg {String} selectedClass The css class to add to selected nodes
19640      */
19641     selectedClass : "x-view-selected",
19642      /**
19643      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19644      */
19645     emptyText : "",
19646     
19647     /**
19648      * @cfg {String} text to display on mask (default Loading)
19649      */
19650     mask : false,
19651     /**
19652      * @cfg {Boolean} multiSelect Allow multiple selection
19653      */
19654     multiSelect : false,
19655     /**
19656      * @cfg {Boolean} singleSelect Allow single selection
19657      */
19658     singleSelect:  false,
19659     
19660     /**
19661      * @cfg {Boolean} toggleSelect - selecting 
19662      */
19663     toggleSelect : false,
19664     
19665     /**
19666      * @cfg {Boolean} tickable - selecting 
19667      */
19668     tickable : false,
19669     
19670     /**
19671      * Returns the element this view is bound to.
19672      * @return {Roo.Element}
19673      */
19674     getEl : function(){
19675         return this.wrapEl;
19676     },
19677     
19678     
19679
19680     /**
19681      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19682      */
19683     refresh : function(){
19684         //Roo.log('refresh');
19685         var t = this.tpl;
19686         
19687         // if we are using something like 'domtemplate', then
19688         // the what gets used is:
19689         // t.applySubtemplate(NAME, data, wrapping data..)
19690         // the outer template then get' applied with
19691         //     the store 'extra data'
19692         // and the body get's added to the
19693         //      roo-name="data" node?
19694         //      <span class='roo-tpl-{name}'></span> ?????
19695         
19696         
19697         
19698         this.clearSelections();
19699         this.el.update("");
19700         var html = [];
19701         var records = this.store.getRange();
19702         if(records.length < 1) {
19703             
19704             // is this valid??  = should it render a template??
19705             
19706             this.el.update(this.emptyText);
19707             return;
19708         }
19709         var el = this.el;
19710         if (this.dataName) {
19711             this.el.update(t.apply(this.store.meta)); //????
19712             el = this.el.child('.roo-tpl-' + this.dataName);
19713         }
19714         
19715         for(var i = 0, len = records.length; i < len; i++){
19716             var data = this.prepareData(records[i].data, i, records[i]);
19717             this.fireEvent("preparedata", this, data, i, records[i]);
19718             
19719             var d = Roo.apply({}, data);
19720             
19721             if(this.tickable){
19722                 Roo.apply(d, {'roo-id' : Roo.id()});
19723                 
19724                 var _this = this;
19725             
19726                 Roo.each(this.parent.item, function(item){
19727                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19728                         return;
19729                     }
19730                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19731                 });
19732             }
19733             
19734             html[html.length] = Roo.util.Format.trim(
19735                 this.dataName ?
19736                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19737                     t.apply(d)
19738             );
19739         }
19740         
19741         
19742         
19743         el.update(html.join(""));
19744         this.nodes = el.dom.childNodes;
19745         this.updateIndexes(0);
19746     },
19747     
19748
19749     /**
19750      * Function to override to reformat the data that is sent to
19751      * the template for each node.
19752      * DEPRICATED - use the preparedata event handler.
19753      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19754      * a JSON object for an UpdateManager bound view).
19755      */
19756     prepareData : function(data, index, record)
19757     {
19758         this.fireEvent("preparedata", this, data, index, record);
19759         return data;
19760     },
19761
19762     onUpdate : function(ds, record){
19763         // Roo.log('on update');   
19764         this.clearSelections();
19765         var index = this.store.indexOf(record);
19766         var n = this.nodes[index];
19767         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19768         n.parentNode.removeChild(n);
19769         this.updateIndexes(index, index);
19770     },
19771
19772     
19773     
19774 // --------- FIXME     
19775     onAdd : function(ds, records, index)
19776     {
19777         //Roo.log(['on Add', ds, records, index] );        
19778         this.clearSelections();
19779         if(this.nodes.length == 0){
19780             this.refresh();
19781             return;
19782         }
19783         var n = this.nodes[index];
19784         for(var i = 0, len = records.length; i < len; i++){
19785             var d = this.prepareData(records[i].data, i, records[i]);
19786             if(n){
19787                 this.tpl.insertBefore(n, d);
19788             }else{
19789                 
19790                 this.tpl.append(this.el, d);
19791             }
19792         }
19793         this.updateIndexes(index);
19794     },
19795
19796     onRemove : function(ds, record, index){
19797        // Roo.log('onRemove');
19798         this.clearSelections();
19799         var el = this.dataName  ?
19800             this.el.child('.roo-tpl-' + this.dataName) :
19801             this.el; 
19802         
19803         el.dom.removeChild(this.nodes[index]);
19804         this.updateIndexes(index);
19805     },
19806
19807     /**
19808      * Refresh an individual node.
19809      * @param {Number} index
19810      */
19811     refreshNode : function(index){
19812         this.onUpdate(this.store, this.store.getAt(index));
19813     },
19814
19815     updateIndexes : function(startIndex, endIndex){
19816         var ns = this.nodes;
19817         startIndex = startIndex || 0;
19818         endIndex = endIndex || ns.length - 1;
19819         for(var i = startIndex; i <= endIndex; i++){
19820             ns[i].nodeIndex = i;
19821         }
19822     },
19823
19824     /**
19825      * Changes the data store this view uses and refresh the view.
19826      * @param {Store} store
19827      */
19828     setStore : function(store, initial){
19829         if(!initial && this.store){
19830             this.store.un("datachanged", this.refresh);
19831             this.store.un("add", this.onAdd);
19832             this.store.un("remove", this.onRemove);
19833             this.store.un("update", this.onUpdate);
19834             this.store.un("clear", this.refresh);
19835             this.store.un("beforeload", this.onBeforeLoad);
19836             this.store.un("load", this.onLoad);
19837             this.store.un("loadexception", this.onLoad);
19838         }
19839         if(store){
19840           
19841             store.on("datachanged", this.refresh, this);
19842             store.on("add", this.onAdd, this);
19843             store.on("remove", this.onRemove, this);
19844             store.on("update", this.onUpdate, this);
19845             store.on("clear", this.refresh, this);
19846             store.on("beforeload", this.onBeforeLoad, this);
19847             store.on("load", this.onLoad, this);
19848             store.on("loadexception", this.onLoad, this);
19849         }
19850         
19851         if(store){
19852             this.refresh();
19853         }
19854     },
19855     /**
19856      * onbeforeLoad - masks the loading area.
19857      *
19858      */
19859     onBeforeLoad : function(store,opts)
19860     {
19861          //Roo.log('onBeforeLoad');   
19862         if (!opts.add) {
19863             this.el.update("");
19864         }
19865         this.el.mask(this.mask ? this.mask : "Loading" ); 
19866     },
19867     onLoad : function ()
19868     {
19869         this.el.unmask();
19870     },
19871     
19872
19873     /**
19874      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19875      * @param {HTMLElement} node
19876      * @return {HTMLElement} The template node
19877      */
19878     findItemFromChild : function(node){
19879         var el = this.dataName  ?
19880             this.el.child('.roo-tpl-' + this.dataName,true) :
19881             this.el.dom; 
19882         
19883         if(!node || node.parentNode == el){
19884                     return node;
19885             }
19886             var p = node.parentNode;
19887             while(p && p != el){
19888             if(p.parentNode == el){
19889                 return p;
19890             }
19891             p = p.parentNode;
19892         }
19893             return null;
19894     },
19895
19896     /** @ignore */
19897     onClick : function(e){
19898         var item = this.findItemFromChild(e.getTarget());
19899         if(item){
19900             var index = this.indexOf(item);
19901             if(this.onItemClick(item, index, e) !== false){
19902                 this.fireEvent("click", this, index, item, e);
19903             }
19904         }else{
19905             this.clearSelections();
19906         }
19907     },
19908
19909     /** @ignore */
19910     onContextMenu : function(e){
19911         var item = this.findItemFromChild(e.getTarget());
19912         if(item){
19913             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19914         }
19915     },
19916
19917     /** @ignore */
19918     onDblClick : function(e){
19919         var item = this.findItemFromChild(e.getTarget());
19920         if(item){
19921             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19922         }
19923     },
19924
19925     onItemClick : function(item, index, e)
19926     {
19927         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19928             return false;
19929         }
19930         if (this.toggleSelect) {
19931             var m = this.isSelected(item) ? 'unselect' : 'select';
19932             //Roo.log(m);
19933             var _t = this;
19934             _t[m](item, true, false);
19935             return true;
19936         }
19937         if(this.multiSelect || this.singleSelect){
19938             if(this.multiSelect && e.shiftKey && this.lastSelection){
19939                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19940             }else{
19941                 this.select(item, this.multiSelect && e.ctrlKey);
19942                 this.lastSelection = item;
19943             }
19944             
19945             if(!this.tickable){
19946                 e.preventDefault();
19947             }
19948             
19949         }
19950         return true;
19951     },
19952
19953     /**
19954      * Get the number of selected nodes.
19955      * @return {Number}
19956      */
19957     getSelectionCount : function(){
19958         return this.selections.length;
19959     },
19960
19961     /**
19962      * Get the currently selected nodes.
19963      * @return {Array} An array of HTMLElements
19964      */
19965     getSelectedNodes : function(){
19966         return this.selections;
19967     },
19968
19969     /**
19970      * Get the indexes of the selected nodes.
19971      * @return {Array}
19972      */
19973     getSelectedIndexes : function(){
19974         var indexes = [], s = this.selections;
19975         for(var i = 0, len = s.length; i < len; i++){
19976             indexes.push(s[i].nodeIndex);
19977         }
19978         return indexes;
19979     },
19980
19981     /**
19982      * Clear all selections
19983      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19984      */
19985     clearSelections : function(suppressEvent){
19986         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19987             this.cmp.elements = this.selections;
19988             this.cmp.removeClass(this.selectedClass);
19989             this.selections = [];
19990             if(!suppressEvent){
19991                 this.fireEvent("selectionchange", this, this.selections);
19992             }
19993         }
19994     },
19995
19996     /**
19997      * Returns true if the passed node is selected
19998      * @param {HTMLElement/Number} node The node or node index
19999      * @return {Boolean}
20000      */
20001     isSelected : function(node){
20002         var s = this.selections;
20003         if(s.length < 1){
20004             return false;
20005         }
20006         node = this.getNode(node);
20007         return s.indexOf(node) !== -1;
20008     },
20009
20010     /**
20011      * Selects nodes.
20012      * @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
20013      * @param {Boolean} keepExisting (optional) true to keep existing selections
20014      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20015      */
20016     select : function(nodeInfo, keepExisting, suppressEvent){
20017         if(nodeInfo instanceof Array){
20018             if(!keepExisting){
20019                 this.clearSelections(true);
20020             }
20021             for(var i = 0, len = nodeInfo.length; i < len; i++){
20022                 this.select(nodeInfo[i], true, true);
20023             }
20024             return;
20025         } 
20026         var node = this.getNode(nodeInfo);
20027         if(!node || this.isSelected(node)){
20028             return; // already selected.
20029         }
20030         if(!keepExisting){
20031             this.clearSelections(true);
20032         }
20033         
20034         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20035             Roo.fly(node).addClass(this.selectedClass);
20036             this.selections.push(node);
20037             if(!suppressEvent){
20038                 this.fireEvent("selectionchange", this, this.selections);
20039             }
20040         }
20041         
20042         
20043     },
20044       /**
20045      * Unselects nodes.
20046      * @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
20047      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20048      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20049      */
20050     unselect : function(nodeInfo, keepExisting, suppressEvent)
20051     {
20052         if(nodeInfo instanceof Array){
20053             Roo.each(this.selections, function(s) {
20054                 this.unselect(s, nodeInfo);
20055             }, this);
20056             return;
20057         }
20058         var node = this.getNode(nodeInfo);
20059         if(!node || !this.isSelected(node)){
20060             //Roo.log("not selected");
20061             return; // not selected.
20062         }
20063         // fireevent???
20064         var ns = [];
20065         Roo.each(this.selections, function(s) {
20066             if (s == node ) {
20067                 Roo.fly(node).removeClass(this.selectedClass);
20068
20069                 return;
20070             }
20071             ns.push(s);
20072         },this);
20073         
20074         this.selections= ns;
20075         this.fireEvent("selectionchange", this, this.selections);
20076     },
20077
20078     /**
20079      * Gets a template node.
20080      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20081      * @return {HTMLElement} The node or null if it wasn't found
20082      */
20083     getNode : function(nodeInfo){
20084         if(typeof nodeInfo == "string"){
20085             return document.getElementById(nodeInfo);
20086         }else if(typeof nodeInfo == "number"){
20087             return this.nodes[nodeInfo];
20088         }
20089         return nodeInfo;
20090     },
20091
20092     /**
20093      * Gets a range template nodes.
20094      * @param {Number} startIndex
20095      * @param {Number} endIndex
20096      * @return {Array} An array of nodes
20097      */
20098     getNodes : function(start, end){
20099         var ns = this.nodes;
20100         start = start || 0;
20101         end = typeof end == "undefined" ? ns.length - 1 : end;
20102         var nodes = [];
20103         if(start <= end){
20104             for(var i = start; i <= end; i++){
20105                 nodes.push(ns[i]);
20106             }
20107         } else{
20108             for(var i = start; i >= end; i--){
20109                 nodes.push(ns[i]);
20110             }
20111         }
20112         return nodes;
20113     },
20114
20115     /**
20116      * Finds the index of the passed node
20117      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20118      * @return {Number} The index of the node or -1
20119      */
20120     indexOf : function(node){
20121         node = this.getNode(node);
20122         if(typeof node.nodeIndex == "number"){
20123             return node.nodeIndex;
20124         }
20125         var ns = this.nodes;
20126         for(var i = 0, len = ns.length; i < len; i++){
20127             if(ns[i] == node){
20128                 return i;
20129             }
20130         }
20131         return -1;
20132     }
20133 });
20134 /*
20135  * - LGPL
20136  *
20137  * based on jquery fullcalendar
20138  * 
20139  */
20140
20141 Roo.bootstrap = Roo.bootstrap || {};
20142 /**
20143  * @class Roo.bootstrap.Calendar
20144  * @extends Roo.bootstrap.Component
20145  * Bootstrap Calendar class
20146  * @cfg {Boolean} loadMask (true|false) default false
20147  * @cfg {Object} header generate the user specific header of the calendar, default false
20148
20149  * @constructor
20150  * Create a new Container
20151  * @param {Object} config The config object
20152  */
20153
20154
20155
20156 Roo.bootstrap.Calendar = function(config){
20157     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20158      this.addEvents({
20159         /**
20160              * @event select
20161              * Fires when a date is selected
20162              * @param {DatePicker} this
20163              * @param {Date} date The selected date
20164              */
20165         'select': true,
20166         /**
20167              * @event monthchange
20168              * Fires when the displayed month changes 
20169              * @param {DatePicker} this
20170              * @param {Date} date The selected month
20171              */
20172         'monthchange': true,
20173         /**
20174              * @event evententer
20175              * Fires when mouse over an event
20176              * @param {Calendar} this
20177              * @param {event} Event
20178              */
20179         'evententer': true,
20180         /**
20181              * @event eventleave
20182              * Fires when the mouse leaves an
20183              * @param {Calendar} this
20184              * @param {event}
20185              */
20186         'eventleave': true,
20187         /**
20188              * @event eventclick
20189              * Fires when the mouse click an
20190              * @param {Calendar} this
20191              * @param {event}
20192              */
20193         'eventclick': true
20194         
20195     });
20196
20197 };
20198
20199 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20200     
20201      /**
20202      * @cfg {Number} startDay
20203      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20204      */
20205     startDay : 0,
20206     
20207     loadMask : false,
20208     
20209     header : false,
20210       
20211     getAutoCreate : function(){
20212         
20213         
20214         var fc_button = function(name, corner, style, content ) {
20215             return Roo.apply({},{
20216                 tag : 'span',
20217                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20218                          (corner.length ?
20219                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20220                             ''
20221                         ),
20222                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20223                 unselectable: 'on'
20224             });
20225         };
20226         
20227         var header = {};
20228         
20229         if(!this.header){
20230             header = {
20231                 tag : 'table',
20232                 cls : 'fc-header',
20233                 style : 'width:100%',
20234                 cn : [
20235                     {
20236                         tag: 'tr',
20237                         cn : [
20238                             {
20239                                 tag : 'td',
20240                                 cls : 'fc-header-left',
20241                                 cn : [
20242                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20243                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20244                                     { tag: 'span', cls: 'fc-header-space' },
20245                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20246
20247
20248                                 ]
20249                             },
20250
20251                             {
20252                                 tag : 'td',
20253                                 cls : 'fc-header-center',
20254                                 cn : [
20255                                     {
20256                                         tag: 'span',
20257                                         cls: 'fc-header-title',
20258                                         cn : {
20259                                             tag: 'H2',
20260                                             html : 'month / year'
20261                                         }
20262                                     }
20263
20264                                 ]
20265                             },
20266                             {
20267                                 tag : 'td',
20268                                 cls : 'fc-header-right',
20269                                 cn : [
20270                               /*      fc_button('month', 'left', '', 'month' ),
20271                                     fc_button('week', '', '', 'week' ),
20272                                     fc_button('day', 'right', '', 'day' )
20273                                 */    
20274
20275                                 ]
20276                             }
20277
20278                         ]
20279                     }
20280                 ]
20281             };
20282         }
20283         
20284         header = this.header;
20285         
20286        
20287         var cal_heads = function() {
20288             var ret = [];
20289             // fixme - handle this.
20290             
20291             for (var i =0; i < Date.dayNames.length; i++) {
20292                 var d = Date.dayNames[i];
20293                 ret.push({
20294                     tag: 'th',
20295                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20296                     html : d.substring(0,3)
20297                 });
20298                 
20299             }
20300             ret[0].cls += ' fc-first';
20301             ret[6].cls += ' fc-last';
20302             return ret;
20303         };
20304         var cal_cell = function(n) {
20305             return  {
20306                 tag: 'td',
20307                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20308                 cn : [
20309                     {
20310                         cn : [
20311                             {
20312                                 cls: 'fc-day-number',
20313                                 html: 'D'
20314                             },
20315                             {
20316                                 cls: 'fc-day-content',
20317                              
20318                                 cn : [
20319                                      {
20320                                         style: 'position: relative;' // height: 17px;
20321                                     }
20322                                 ]
20323                             }
20324                             
20325                             
20326                         ]
20327                     }
20328                 ]
20329                 
20330             }
20331         };
20332         var cal_rows = function() {
20333             
20334             var ret = [];
20335             for (var r = 0; r < 6; r++) {
20336                 var row= {
20337                     tag : 'tr',
20338                     cls : 'fc-week',
20339                     cn : []
20340                 };
20341                 
20342                 for (var i =0; i < Date.dayNames.length; i++) {
20343                     var d = Date.dayNames[i];
20344                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20345
20346                 }
20347                 row.cn[0].cls+=' fc-first';
20348                 row.cn[0].cn[0].style = 'min-height:90px';
20349                 row.cn[6].cls+=' fc-last';
20350                 ret.push(row);
20351                 
20352             }
20353             ret[0].cls += ' fc-first';
20354             ret[4].cls += ' fc-prev-last';
20355             ret[5].cls += ' fc-last';
20356             return ret;
20357             
20358         };
20359         
20360         var cal_table = {
20361             tag: 'table',
20362             cls: 'fc-border-separate',
20363             style : 'width:100%',
20364             cellspacing  : 0,
20365             cn : [
20366                 { 
20367                     tag: 'thead',
20368                     cn : [
20369                         { 
20370                             tag: 'tr',
20371                             cls : 'fc-first fc-last',
20372                             cn : cal_heads()
20373                         }
20374                     ]
20375                 },
20376                 { 
20377                     tag: 'tbody',
20378                     cn : cal_rows()
20379                 }
20380                   
20381             ]
20382         };
20383          
20384          var cfg = {
20385             cls : 'fc fc-ltr',
20386             cn : [
20387                 header,
20388                 {
20389                     cls : 'fc-content',
20390                     style : "position: relative;",
20391                     cn : [
20392                         {
20393                             cls : 'fc-view fc-view-month fc-grid',
20394                             style : 'position: relative',
20395                             unselectable : 'on',
20396                             cn : [
20397                                 {
20398                                     cls : 'fc-event-container',
20399                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20400                                 },
20401                                 cal_table
20402                             ]
20403                         }
20404                     ]
20405     
20406                 }
20407            ] 
20408             
20409         };
20410         
20411          
20412         
20413         return cfg;
20414     },
20415     
20416     
20417     initEvents : function()
20418     {
20419         if(!this.store){
20420             throw "can not find store for calendar";
20421         }
20422         
20423         var mark = {
20424             tag: "div",
20425             cls:"x-dlg-mask",
20426             style: "text-align:center",
20427             cn: [
20428                 {
20429                     tag: "div",
20430                     style: "background-color:white;width:50%;margin:250 auto",
20431                     cn: [
20432                         {
20433                             tag: "img",
20434                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20435                         },
20436                         {
20437                             tag: "span",
20438                             html: "Loading"
20439                         }
20440                         
20441                     ]
20442                 }
20443             ]
20444         };
20445         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20446         
20447         var size = this.el.select('.fc-content', true).first().getSize();
20448         this.maskEl.setSize(size.width, size.height);
20449         this.maskEl.enableDisplayMode("block");
20450         if(!this.loadMask){
20451             this.maskEl.hide();
20452         }
20453         
20454         this.store = Roo.factory(this.store, Roo.data);
20455         this.store.on('load', this.onLoad, this);
20456         this.store.on('beforeload', this.onBeforeLoad, this);
20457         
20458         this.resize();
20459         
20460         this.cells = this.el.select('.fc-day',true);
20461         //Roo.log(this.cells);
20462         this.textNodes = this.el.query('.fc-day-number');
20463         this.cells.addClassOnOver('fc-state-hover');
20464         
20465         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20466         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20467         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20468         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20469         
20470         this.on('monthchange', this.onMonthChange, this);
20471         
20472         this.update(new Date().clearTime());
20473     },
20474     
20475     resize : function() {
20476         var sz  = this.el.getSize();
20477         
20478         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20479         this.el.select('.fc-day-content div',true).setHeight(34);
20480     },
20481     
20482     
20483     // private
20484     showPrevMonth : function(e){
20485         this.update(this.activeDate.add("mo", -1));
20486     },
20487     showToday : function(e){
20488         this.update(new Date().clearTime());
20489     },
20490     // private
20491     showNextMonth : function(e){
20492         this.update(this.activeDate.add("mo", 1));
20493     },
20494
20495     // private
20496     showPrevYear : function(){
20497         this.update(this.activeDate.add("y", -1));
20498     },
20499
20500     // private
20501     showNextYear : function(){
20502         this.update(this.activeDate.add("y", 1));
20503     },
20504
20505     
20506    // private
20507     update : function(date)
20508     {
20509         var vd = this.activeDate;
20510         this.activeDate = date;
20511 //        if(vd && this.el){
20512 //            var t = date.getTime();
20513 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20514 //                Roo.log('using add remove');
20515 //                
20516 //                this.fireEvent('monthchange', this, date);
20517 //                
20518 //                this.cells.removeClass("fc-state-highlight");
20519 //                this.cells.each(function(c){
20520 //                   if(c.dateValue == t){
20521 //                       c.addClass("fc-state-highlight");
20522 //                       setTimeout(function(){
20523 //                            try{c.dom.firstChild.focus();}catch(e){}
20524 //                       }, 50);
20525 //                       return false;
20526 //                   }
20527 //                   return true;
20528 //                });
20529 //                return;
20530 //            }
20531 //        }
20532         
20533         var days = date.getDaysInMonth();
20534         
20535         var firstOfMonth = date.getFirstDateOfMonth();
20536         var startingPos = firstOfMonth.getDay()-this.startDay;
20537         
20538         if(startingPos < this.startDay){
20539             startingPos += 7;
20540         }
20541         
20542         var pm = date.add(Date.MONTH, -1);
20543         var prevStart = pm.getDaysInMonth()-startingPos;
20544 //        
20545         this.cells = this.el.select('.fc-day',true);
20546         this.textNodes = this.el.query('.fc-day-number');
20547         this.cells.addClassOnOver('fc-state-hover');
20548         
20549         var cells = this.cells.elements;
20550         var textEls = this.textNodes;
20551         
20552         Roo.each(cells, function(cell){
20553             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20554         });
20555         
20556         days += startingPos;
20557
20558         // convert everything to numbers so it's fast
20559         var day = 86400000;
20560         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20561         //Roo.log(d);
20562         //Roo.log(pm);
20563         //Roo.log(prevStart);
20564         
20565         var today = new Date().clearTime().getTime();
20566         var sel = date.clearTime().getTime();
20567         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20568         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20569         var ddMatch = this.disabledDatesRE;
20570         var ddText = this.disabledDatesText;
20571         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20572         var ddaysText = this.disabledDaysText;
20573         var format = this.format;
20574         
20575         var setCellClass = function(cal, cell){
20576             cell.row = 0;
20577             cell.events = [];
20578             cell.more = [];
20579             //Roo.log('set Cell Class');
20580             cell.title = "";
20581             var t = d.getTime();
20582             
20583             //Roo.log(d);
20584             
20585             cell.dateValue = t;
20586             if(t == today){
20587                 cell.className += " fc-today";
20588                 cell.className += " fc-state-highlight";
20589                 cell.title = cal.todayText;
20590             }
20591             if(t == sel){
20592                 // disable highlight in other month..
20593                 //cell.className += " fc-state-highlight";
20594                 
20595             }
20596             // disabling
20597             if(t < min) {
20598                 cell.className = " fc-state-disabled";
20599                 cell.title = cal.minText;
20600                 return;
20601             }
20602             if(t > max) {
20603                 cell.className = " fc-state-disabled";
20604                 cell.title = cal.maxText;
20605                 return;
20606             }
20607             if(ddays){
20608                 if(ddays.indexOf(d.getDay()) != -1){
20609                     cell.title = ddaysText;
20610                     cell.className = " fc-state-disabled";
20611                 }
20612             }
20613             if(ddMatch && format){
20614                 var fvalue = d.dateFormat(format);
20615                 if(ddMatch.test(fvalue)){
20616                     cell.title = ddText.replace("%0", fvalue);
20617                     cell.className = " fc-state-disabled";
20618                 }
20619             }
20620             
20621             if (!cell.initialClassName) {
20622                 cell.initialClassName = cell.dom.className;
20623             }
20624             
20625             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20626         };
20627
20628         var i = 0;
20629         
20630         for(; i < startingPos; i++) {
20631             textEls[i].innerHTML = (++prevStart);
20632             d.setDate(d.getDate()+1);
20633             
20634             cells[i].className = "fc-past fc-other-month";
20635             setCellClass(this, cells[i]);
20636         }
20637         
20638         var intDay = 0;
20639         
20640         for(; i < days; i++){
20641             intDay = i - startingPos + 1;
20642             textEls[i].innerHTML = (intDay);
20643             d.setDate(d.getDate()+1);
20644             
20645             cells[i].className = ''; // "x-date-active";
20646             setCellClass(this, cells[i]);
20647         }
20648         var extraDays = 0;
20649         
20650         for(; i < 42; i++) {
20651             textEls[i].innerHTML = (++extraDays);
20652             d.setDate(d.getDate()+1);
20653             
20654             cells[i].className = "fc-future fc-other-month";
20655             setCellClass(this, cells[i]);
20656         }
20657         
20658         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20659         
20660         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20661         
20662         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20663         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20664         
20665         if(totalRows != 6){
20666             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20667             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20668         }
20669         
20670         this.fireEvent('monthchange', this, date);
20671         
20672         
20673         /*
20674         if(!this.internalRender){
20675             var main = this.el.dom.firstChild;
20676             var w = main.offsetWidth;
20677             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20678             Roo.fly(main).setWidth(w);
20679             this.internalRender = true;
20680             // opera does not respect the auto grow header center column
20681             // then, after it gets a width opera refuses to recalculate
20682             // without a second pass
20683             if(Roo.isOpera && !this.secondPass){
20684                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20685                 this.secondPass = true;
20686                 this.update.defer(10, this, [date]);
20687             }
20688         }
20689         */
20690         
20691     },
20692     
20693     findCell : function(dt) {
20694         dt = dt.clearTime().getTime();
20695         var ret = false;
20696         this.cells.each(function(c){
20697             //Roo.log("check " +c.dateValue + '?=' + dt);
20698             if(c.dateValue == dt){
20699                 ret = c;
20700                 return false;
20701             }
20702             return true;
20703         });
20704         
20705         return ret;
20706     },
20707     
20708     findCells : function(ev) {
20709         var s = ev.start.clone().clearTime().getTime();
20710        // Roo.log(s);
20711         var e= ev.end.clone().clearTime().getTime();
20712        // Roo.log(e);
20713         var ret = [];
20714         this.cells.each(function(c){
20715              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20716             
20717             if(c.dateValue > e){
20718                 return ;
20719             }
20720             if(c.dateValue < s){
20721                 return ;
20722             }
20723             ret.push(c);
20724         });
20725         
20726         return ret;    
20727     },
20728     
20729 //    findBestRow: function(cells)
20730 //    {
20731 //        var ret = 0;
20732 //        
20733 //        for (var i =0 ; i < cells.length;i++) {
20734 //            ret  = Math.max(cells[i].rows || 0,ret);
20735 //        }
20736 //        return ret;
20737 //        
20738 //    },
20739     
20740     
20741     addItem : function(ev)
20742     {
20743         // look for vertical location slot in
20744         var cells = this.findCells(ev);
20745         
20746 //        ev.row = this.findBestRow(cells);
20747         
20748         // work out the location.
20749         
20750         var crow = false;
20751         var rows = [];
20752         for(var i =0; i < cells.length; i++) {
20753             
20754             cells[i].row = cells[0].row;
20755             
20756             if(i == 0){
20757                 cells[i].row = cells[i].row + 1;
20758             }
20759             
20760             if (!crow) {
20761                 crow = {
20762                     start : cells[i],
20763                     end :  cells[i]
20764                 };
20765                 continue;
20766             }
20767             if (crow.start.getY() == cells[i].getY()) {
20768                 // on same row.
20769                 crow.end = cells[i];
20770                 continue;
20771             }
20772             // different row.
20773             rows.push(crow);
20774             crow = {
20775                 start: cells[i],
20776                 end : cells[i]
20777             };
20778             
20779         }
20780         
20781         rows.push(crow);
20782         ev.els = [];
20783         ev.rows = rows;
20784         ev.cells = cells;
20785         
20786         cells[0].events.push(ev);
20787         
20788         this.calevents.push(ev);
20789     },
20790     
20791     clearEvents: function() {
20792         
20793         if(!this.calevents){
20794             return;
20795         }
20796         
20797         Roo.each(this.cells.elements, function(c){
20798             c.row = 0;
20799             c.events = [];
20800             c.more = [];
20801         });
20802         
20803         Roo.each(this.calevents, function(e) {
20804             Roo.each(e.els, function(el) {
20805                 el.un('mouseenter' ,this.onEventEnter, this);
20806                 el.un('mouseleave' ,this.onEventLeave, this);
20807                 el.remove();
20808             },this);
20809         },this);
20810         
20811         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20812             e.remove();
20813         });
20814         
20815     },
20816     
20817     renderEvents: function()
20818     {   
20819         var _this = this;
20820         
20821         this.cells.each(function(c) {
20822             
20823             if(c.row < 5){
20824                 return;
20825             }
20826             
20827             var ev = c.events;
20828             
20829             var r = 4;
20830             if(c.row != c.events.length){
20831                 r = 4 - (4 - (c.row - c.events.length));
20832             }
20833             
20834             c.events = ev.slice(0, r);
20835             c.more = ev.slice(r);
20836             
20837             if(c.more.length && c.more.length == 1){
20838                 c.events.push(c.more.pop());
20839             }
20840             
20841             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20842             
20843         });
20844             
20845         this.cells.each(function(c) {
20846             
20847             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20848             
20849             
20850             for (var e = 0; e < c.events.length; e++){
20851                 var ev = c.events[e];
20852                 var rows = ev.rows;
20853                 
20854                 for(var i = 0; i < rows.length; i++) {
20855                 
20856                     // how many rows should it span..
20857
20858                     var  cfg = {
20859                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20860                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20861
20862                         unselectable : "on",
20863                         cn : [
20864                             {
20865                                 cls: 'fc-event-inner',
20866                                 cn : [
20867     //                                {
20868     //                                  tag:'span',
20869     //                                  cls: 'fc-event-time',
20870     //                                  html : cells.length > 1 ? '' : ev.time
20871     //                                },
20872                                     {
20873                                       tag:'span',
20874                                       cls: 'fc-event-title',
20875                                       html : String.format('{0}', ev.title)
20876                                     }
20877
20878
20879                                 ]
20880                             },
20881                             {
20882                                 cls: 'ui-resizable-handle ui-resizable-e',
20883                                 html : '&nbsp;&nbsp;&nbsp'
20884                             }
20885
20886                         ]
20887                     };
20888
20889                     if (i == 0) {
20890                         cfg.cls += ' fc-event-start';
20891                     }
20892                     if ((i+1) == rows.length) {
20893                         cfg.cls += ' fc-event-end';
20894                     }
20895
20896                     var ctr = _this.el.select('.fc-event-container',true).first();
20897                     var cg = ctr.createChild(cfg);
20898
20899                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20900                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20901
20902                     var r = (c.more.length) ? 1 : 0;
20903                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20904                     cg.setWidth(ebox.right - sbox.x -2);
20905
20906                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20907                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20908                     cg.on('click', _this.onEventClick, _this, ev);
20909
20910                     ev.els.push(cg);
20911                     
20912                 }
20913                 
20914             }
20915             
20916             
20917             if(c.more.length){
20918                 var  cfg = {
20919                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20920                     style : 'position: absolute',
20921                     unselectable : "on",
20922                     cn : [
20923                         {
20924                             cls: 'fc-event-inner',
20925                             cn : [
20926                                 {
20927                                   tag:'span',
20928                                   cls: 'fc-event-title',
20929                                   html : 'More'
20930                                 }
20931
20932
20933                             ]
20934                         },
20935                         {
20936                             cls: 'ui-resizable-handle ui-resizable-e',
20937                             html : '&nbsp;&nbsp;&nbsp'
20938                         }
20939
20940                     ]
20941                 };
20942
20943                 var ctr = _this.el.select('.fc-event-container',true).first();
20944                 var cg = ctr.createChild(cfg);
20945
20946                 var sbox = c.select('.fc-day-content',true).first().getBox();
20947                 var ebox = c.select('.fc-day-content',true).first().getBox();
20948                 //Roo.log(cg);
20949                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20950                 cg.setWidth(ebox.right - sbox.x -2);
20951
20952                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20953                 
20954             }
20955             
20956         });
20957         
20958         
20959         
20960     },
20961     
20962     onEventEnter: function (e, el,event,d) {
20963         this.fireEvent('evententer', this, el, event);
20964     },
20965     
20966     onEventLeave: function (e, el,event,d) {
20967         this.fireEvent('eventleave', this, el, event);
20968     },
20969     
20970     onEventClick: function (e, el,event,d) {
20971         this.fireEvent('eventclick', this, el, event);
20972     },
20973     
20974     onMonthChange: function () {
20975         this.store.load();
20976     },
20977     
20978     onMoreEventClick: function(e, el, more)
20979     {
20980         var _this = this;
20981         
20982         this.calpopover.placement = 'right';
20983         this.calpopover.setTitle('More');
20984         
20985         this.calpopover.setContent('');
20986         
20987         var ctr = this.calpopover.el.select('.popover-content', true).first();
20988         
20989         Roo.each(more, function(m){
20990             var cfg = {
20991                 cls : 'fc-event-hori fc-event-draggable',
20992                 html : m.title
20993             };
20994             var cg = ctr.createChild(cfg);
20995             
20996             cg.on('click', _this.onEventClick, _this, m);
20997         });
20998         
20999         this.calpopover.show(el);
21000         
21001         
21002     },
21003     
21004     onLoad: function () 
21005     {   
21006         this.calevents = [];
21007         var cal = this;
21008         
21009         if(this.store.getCount() > 0){
21010             this.store.data.each(function(d){
21011                cal.addItem({
21012                     id : d.data.id,
21013                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21014                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21015                     time : d.data.start_time,
21016                     title : d.data.title,
21017                     description : d.data.description,
21018                     venue : d.data.venue
21019                 });
21020             });
21021         }
21022         
21023         this.renderEvents();
21024         
21025         if(this.calevents.length && this.loadMask){
21026             this.maskEl.hide();
21027         }
21028     },
21029     
21030     onBeforeLoad: function()
21031     {
21032         this.clearEvents();
21033         if(this.loadMask){
21034             this.maskEl.show();
21035         }
21036     }
21037 });
21038
21039  
21040  /*
21041  * - LGPL
21042  *
21043  * element
21044  * 
21045  */
21046
21047 /**
21048  * @class Roo.bootstrap.Popover
21049  * @extends Roo.bootstrap.Component
21050  * Bootstrap Popover class
21051  * @cfg {String} html contents of the popover   (or false to use children..)
21052  * @cfg {String} title of popover (or false to hide)
21053  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21054  * @cfg {String} trigger click || hover (or false to trigger manually)
21055  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21056  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21057  *      - if false and it has a 'parent' then it will be automatically added to that element
21058  *      - if string - Roo.get  will be called 
21059  * @cfg {Number} delay - delay before showing
21060  
21061  * @constructor
21062  * Create a new Popover
21063  * @param {Object} config The config object
21064  */
21065
21066 Roo.bootstrap.Popover = function(config){
21067     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21068     
21069     this.addEvents({
21070         // raw events
21071          /**
21072          * @event show
21073          * After the popover show
21074          * 
21075          * @param {Roo.bootstrap.Popover} this
21076          */
21077         "show" : true,
21078         /**
21079          * @event hide
21080          * After the popover hide
21081          * 
21082          * @param {Roo.bootstrap.Popover} this
21083          */
21084         "hide" : true
21085     });
21086 };
21087
21088 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21089     
21090     title: false,
21091     html: false,
21092     
21093     placement : 'right',
21094     trigger : 'hover', // hover
21095     modal : false,
21096     delay : 0,
21097     
21098     over: false,
21099     
21100     can_build_overlaid : false,
21101     
21102     maskEl : false, // the mask element
21103     headerEl : false,
21104     contentEl : false,
21105     alignEl : false, // when show is called with an element - this get's stored.
21106     
21107     getChildContainer : function()
21108     {
21109         return this.contentEl;
21110         
21111     },
21112     getPopoverHeader : function()
21113     {
21114         this.title = true; // flag not to hide it..
21115         this.headerEl.addClass('p-0');
21116         return this.headerEl
21117     },
21118     
21119     
21120     getAutoCreate : function(){
21121          
21122         var cfg = {
21123            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21124            style: 'display:block',
21125            cn : [
21126                 {
21127                     cls : 'arrow'
21128                 },
21129                 {
21130                     cls : 'popover-inner ',
21131                     cn : [
21132                         {
21133                             tag: 'h3',
21134                             cls: 'popover-title popover-header',
21135                             html : this.title === false ? '' : this.title
21136                         },
21137                         {
21138                             cls : 'popover-content popover-body '  + (this.cls || ''),
21139                             html : this.html || ''
21140                         }
21141                     ]
21142                     
21143                 }
21144            ]
21145         };
21146         
21147         return cfg;
21148     },
21149     /**
21150      * @param {string} the title
21151      */
21152     setTitle: function(str)
21153     {
21154         this.title = str;
21155         if (this.el) {
21156             this.headerEl.dom.innerHTML = str;
21157         }
21158         
21159     },
21160     /**
21161      * @param {string} the body content
21162      */
21163     setContent: function(str)
21164     {
21165         this.html = str;
21166         if (this.contentEl) {
21167             this.contentEl.dom.innerHTML = str;
21168         }
21169         
21170     },
21171     // as it get's added to the bottom of the page.
21172     onRender : function(ct, position)
21173     {
21174         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21175         
21176         
21177         
21178         if(!this.el){
21179             var cfg = Roo.apply({},  this.getAutoCreate());
21180             cfg.id = Roo.id();
21181             
21182             if (this.cls) {
21183                 cfg.cls += ' ' + this.cls;
21184             }
21185             if (this.style) {
21186                 cfg.style = this.style;
21187             }
21188             //Roo.log("adding to ");
21189             this.el = Roo.get(document.body).createChild(cfg, position);
21190 //            Roo.log(this.el);
21191         }
21192         
21193         this.contentEl = this.el.select('.popover-content',true).first();
21194         this.headerEl =  this.el.select('.popover-title',true).first();
21195         
21196         var nitems = [];
21197         if(typeof(this.items) != 'undefined'){
21198             var items = this.items;
21199             delete this.items;
21200
21201             for(var i =0;i < items.length;i++) {
21202                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21203             }
21204         }
21205
21206         this.items = nitems;
21207         
21208         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21209         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21210         
21211         
21212         
21213         this.initEvents();
21214     },
21215     
21216     resizeMask : function()
21217     {
21218         this.maskEl.setSize(
21219             Roo.lib.Dom.getViewWidth(true),
21220             Roo.lib.Dom.getViewHeight(true)
21221         );
21222     },
21223     
21224     initEvents : function()
21225     {
21226         
21227         if (!this.modal) { 
21228             Roo.bootstrap.Popover.register(this);
21229         }
21230          
21231         this.arrowEl = this.el.select('.arrow',true).first();
21232         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21233         this.el.enableDisplayMode('block');
21234         this.el.hide();
21235  
21236         
21237         if (this.over === false && !this.parent()) {
21238             return; 
21239         }
21240         if (this.triggers === false) {
21241             return;
21242         }
21243          
21244         // support parent
21245         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21246         var triggers = this.trigger ? this.trigger.split(' ') : [];
21247         Roo.each(triggers, function(trigger) {
21248         
21249             if (trigger == 'click') {
21250                 on_el.on('click', this.toggle, this);
21251             } else if (trigger != 'manual') {
21252                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21253                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21254       
21255                 on_el.on(eventIn  ,this.enter, this);
21256                 on_el.on(eventOut, this.leave, this);
21257             }
21258         }, this);
21259     },
21260     
21261     
21262     // private
21263     timeout : null,
21264     hoverState : null,
21265     
21266     toggle : function () {
21267         this.hoverState == 'in' ? this.leave() : this.enter();
21268     },
21269     
21270     enter : function () {
21271         
21272         clearTimeout(this.timeout);
21273     
21274         this.hoverState = 'in';
21275     
21276         if (!this.delay || !this.delay.show) {
21277             this.show();
21278             return;
21279         }
21280         var _t = this;
21281         this.timeout = setTimeout(function () {
21282             if (_t.hoverState == 'in') {
21283                 _t.show();
21284             }
21285         }, this.delay.show)
21286     },
21287     
21288     leave : function() {
21289         clearTimeout(this.timeout);
21290     
21291         this.hoverState = 'out';
21292     
21293         if (!this.delay || !this.delay.hide) {
21294             this.hide();
21295             return;
21296         }
21297         var _t = this;
21298         this.timeout = setTimeout(function () {
21299             if (_t.hoverState == 'out') {
21300                 _t.hide();
21301             }
21302         }, this.delay.hide)
21303     },
21304     /**
21305      * Show the popover
21306      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21307      * @param {string} (left|right|top|bottom) position
21308      */
21309     show : function (on_el, placement)
21310     {
21311         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21312         on_el = on_el || false; // default to false
21313          
21314         if (!on_el) {
21315             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21316                 on_el = this.parent().el;
21317             } else if (this.over) {
21318                 on_el = Roo.get(this.over);
21319             }
21320             
21321         }
21322         
21323         this.alignEl = Roo.get( on_el );
21324
21325         if (!this.el) {
21326             this.render(document.body);
21327         }
21328         
21329         
21330          
21331         
21332         if (this.title === false) {
21333             this.headerEl.hide();
21334         }
21335         
21336        
21337         this.el.show();
21338         this.el.dom.style.display = 'block';
21339          
21340  
21341         if (this.alignEl) {
21342             this.updatePosition(this.placement, true);
21343              
21344         } else {
21345             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21346             var es = this.el.getSize();
21347             var x = Roo.lib.Dom.getViewWidth()/2;
21348             var y = Roo.lib.Dom.getViewHeight()/2;
21349             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21350             
21351         }
21352
21353         
21354         //var arrow = this.el.select('.arrow',true).first();
21355         //arrow.set(align[2], 
21356         
21357         this.el.addClass('in');
21358         
21359          
21360         
21361         this.hoverState = 'in';
21362         
21363         if (this.modal) {
21364             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21365             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21366             this.maskEl.dom.style.display = 'block';
21367             this.maskEl.addClass('show');
21368         }
21369         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21370  
21371         this.fireEvent('show', this);
21372         
21373     },
21374     /**
21375      * fire this manually after loading a grid in the table for example
21376      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21377      * @param {Boolean} try and move it if we cant get right position.
21378      */
21379     updatePosition : function(placement, try_move)
21380     {
21381         // allow for calling with no parameters
21382         placement = placement   ? placement :  this.placement;
21383         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21384         
21385         this.el.removeClass([
21386             'fade','top','bottom', 'left', 'right','in',
21387             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21388         ]);
21389         this.el.addClass(placement + ' bs-popover-' + placement);
21390         
21391         if (!this.alignEl ) {
21392             return false;
21393         }
21394         
21395         switch (placement) {
21396             case 'right':
21397                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21398                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21399                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21400                     //normal display... or moved up/down.
21401                     this.el.setXY(offset);
21402                     var xy = this.alignEl.getAnchorXY('tr', false);
21403                     xy[0]+=2;xy[1]+=5;
21404                     this.arrowEl.setXY(xy);
21405                     return true;
21406                 }
21407                 // continue through...
21408                 return this.updatePosition('left', false);
21409                 
21410             
21411             case 'left':
21412                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21413                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21414                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21415                     //normal display... or moved up/down.
21416                     this.el.setXY(offset);
21417                     var xy = this.alignEl.getAnchorXY('tl', false);
21418                     xy[0]-=10;xy[1]+=5; // << fix me
21419                     this.arrowEl.setXY(xy);
21420                     return true;
21421                 }
21422                 // call self...
21423                 return this.updatePosition('right', false);
21424             
21425             case 'top':
21426                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21427                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21428                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21429                     //normal display... or moved up/down.
21430                     this.el.setXY(offset);
21431                     var xy = this.alignEl.getAnchorXY('t', false);
21432                     xy[1]-=10; // << fix me
21433                     this.arrowEl.setXY(xy);
21434                     return true;
21435                 }
21436                 // fall through
21437                return this.updatePosition('bottom', false);
21438             
21439             case 'bottom':
21440                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21441                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21442                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21443                     //normal display... or moved up/down.
21444                     this.el.setXY(offset);
21445                     var xy = this.alignEl.getAnchorXY('b', false);
21446                      xy[1]+=2; // << fix me
21447                     this.arrowEl.setXY(xy);
21448                     return true;
21449                 }
21450                 // fall through
21451                 return this.updatePosition('top', false);
21452                 
21453             
21454         }
21455         
21456         
21457         return false;
21458     },
21459     
21460     hide : function()
21461     {
21462         this.el.setXY([0,0]);
21463         this.el.removeClass('in');
21464         this.el.hide();
21465         this.hoverState = null;
21466         this.maskEl.hide(); // always..
21467         this.fireEvent('hide', this);
21468     }
21469     
21470 });
21471
21472
21473 Roo.apply(Roo.bootstrap.Popover, {
21474
21475     alignment : {
21476         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21477         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21478         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21479         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21480     },
21481     
21482     zIndex : 20001,
21483
21484     clickHander : false,
21485     
21486     
21487
21488     onMouseDown : function(e)
21489     {
21490         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21491             /// what is nothing is showing..
21492             this.hideAll();
21493         }
21494          
21495     },
21496     
21497     
21498     popups : [],
21499     
21500     register : function(popup)
21501     {
21502         if (!Roo.bootstrap.Popover.clickHandler) {
21503             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21504         }
21505         // hide other popups.
21506         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21507         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21508         this.hideAll(); //<< why?
21509         //this.popups.push(popup);
21510     },
21511     hideAll : function()
21512     {
21513         this.popups.forEach(function(p) {
21514             p.hide();
21515         });
21516     },
21517     onShow : function() {
21518         Roo.bootstrap.Popover.popups.push(this);
21519     },
21520     onHide : function() {
21521         Roo.bootstrap.Popover.popups.remove(this);
21522     } 
21523
21524 });/*
21525  * - LGPL
21526  *
21527  * Card header - holder for the card header elements.
21528  * 
21529  */
21530
21531 /**
21532  * @class Roo.bootstrap.PopoverNav
21533  * @extends Roo.bootstrap.NavGroup
21534  * Bootstrap Popover header navigation class
21535  * @constructor
21536  * Create a new Popover Header Navigation 
21537  * @param {Object} config The config object
21538  */
21539
21540 Roo.bootstrap.PopoverNav = function(config){
21541     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21542 };
21543
21544 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21545     
21546     
21547     container_method : 'getPopoverHeader' 
21548     
21549      
21550     
21551     
21552    
21553 });
21554
21555  
21556
21557  /*
21558  * - LGPL
21559  *
21560  * Progress
21561  * 
21562  */
21563
21564 /**
21565  * @class Roo.bootstrap.Progress
21566  * @extends Roo.bootstrap.Component
21567  * Bootstrap Progress class
21568  * @cfg {Boolean} striped striped of the progress bar
21569  * @cfg {Boolean} active animated of the progress bar
21570  * 
21571  * 
21572  * @constructor
21573  * Create a new Progress
21574  * @param {Object} config The config object
21575  */
21576
21577 Roo.bootstrap.Progress = function(config){
21578     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21579 };
21580
21581 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21582     
21583     striped : false,
21584     active: false,
21585     
21586     getAutoCreate : function(){
21587         var cfg = {
21588             tag: 'div',
21589             cls: 'progress'
21590         };
21591         
21592         
21593         if(this.striped){
21594             cfg.cls += ' progress-striped';
21595         }
21596       
21597         if(this.active){
21598             cfg.cls += ' active';
21599         }
21600         
21601         
21602         return cfg;
21603     }
21604    
21605 });
21606
21607  
21608
21609  /*
21610  * - LGPL
21611  *
21612  * ProgressBar
21613  * 
21614  */
21615
21616 /**
21617  * @class Roo.bootstrap.ProgressBar
21618  * @extends Roo.bootstrap.Component
21619  * Bootstrap ProgressBar class
21620  * @cfg {Number} aria_valuenow aria-value now
21621  * @cfg {Number} aria_valuemin aria-value min
21622  * @cfg {Number} aria_valuemax aria-value max
21623  * @cfg {String} label label for the progress bar
21624  * @cfg {String} panel (success | info | warning | danger )
21625  * @cfg {String} role role of the progress bar
21626  * @cfg {String} sr_only text
21627  * 
21628  * 
21629  * @constructor
21630  * Create a new ProgressBar
21631  * @param {Object} config The config object
21632  */
21633
21634 Roo.bootstrap.ProgressBar = function(config){
21635     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21636 };
21637
21638 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21639     
21640     aria_valuenow : 0,
21641     aria_valuemin : 0,
21642     aria_valuemax : 100,
21643     label : false,
21644     panel : false,
21645     role : false,
21646     sr_only: false,
21647     
21648     getAutoCreate : function()
21649     {
21650         
21651         var cfg = {
21652             tag: 'div',
21653             cls: 'progress-bar',
21654             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21655         };
21656         
21657         if(this.sr_only){
21658             cfg.cn = {
21659                 tag: 'span',
21660                 cls: 'sr-only',
21661                 html: this.sr_only
21662             }
21663         }
21664         
21665         if(this.role){
21666             cfg.role = this.role;
21667         }
21668         
21669         if(this.aria_valuenow){
21670             cfg['aria-valuenow'] = this.aria_valuenow;
21671         }
21672         
21673         if(this.aria_valuemin){
21674             cfg['aria-valuemin'] = this.aria_valuemin;
21675         }
21676         
21677         if(this.aria_valuemax){
21678             cfg['aria-valuemax'] = this.aria_valuemax;
21679         }
21680         
21681         if(this.label && !this.sr_only){
21682             cfg.html = this.label;
21683         }
21684         
21685         if(this.panel){
21686             cfg.cls += ' progress-bar-' + this.panel;
21687         }
21688         
21689         return cfg;
21690     },
21691     
21692     update : function(aria_valuenow)
21693     {
21694         this.aria_valuenow = aria_valuenow;
21695         
21696         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21697     }
21698    
21699 });
21700
21701  
21702
21703  /*
21704  * - LGPL
21705  *
21706  * column
21707  * 
21708  */
21709
21710 /**
21711  * @class Roo.bootstrap.TabGroup
21712  * @extends Roo.bootstrap.Column
21713  * Bootstrap Column class
21714  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21715  * @cfg {Boolean} carousel true to make the group behave like a carousel
21716  * @cfg {Boolean} bullets show bullets for the panels
21717  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21718  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21719  * @cfg {Boolean} showarrow (true|false) show arrow default true
21720  * 
21721  * @constructor
21722  * Create a new TabGroup
21723  * @param {Object} config The config object
21724  */
21725
21726 Roo.bootstrap.TabGroup = function(config){
21727     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21728     if (!this.navId) {
21729         this.navId = Roo.id();
21730     }
21731     this.tabs = [];
21732     Roo.bootstrap.TabGroup.register(this);
21733     
21734 };
21735
21736 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21737     
21738     carousel : false,
21739     transition : false,
21740     bullets : 0,
21741     timer : 0,
21742     autoslide : false,
21743     slideFn : false,
21744     slideOnTouch : false,
21745     showarrow : true,
21746     
21747     getAutoCreate : function()
21748     {
21749         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21750         
21751         cfg.cls += ' tab-content';
21752         
21753         if (this.carousel) {
21754             cfg.cls += ' carousel slide';
21755             
21756             cfg.cn = [{
21757                cls : 'carousel-inner',
21758                cn : []
21759             }];
21760         
21761             if(this.bullets  && !Roo.isTouch){
21762                 
21763                 var bullets = {
21764                     cls : 'carousel-bullets',
21765                     cn : []
21766                 };
21767                
21768                 if(this.bullets_cls){
21769                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21770                 }
21771                 
21772                 bullets.cn.push({
21773                     cls : 'clear'
21774                 });
21775                 
21776                 cfg.cn[0].cn.push(bullets);
21777             }
21778             
21779             if(this.showarrow){
21780                 cfg.cn[0].cn.push({
21781                     tag : 'div',
21782                     class : 'carousel-arrow',
21783                     cn : [
21784                         {
21785                             tag : 'div',
21786                             class : 'carousel-prev',
21787                             cn : [
21788                                 {
21789                                     tag : 'i',
21790                                     class : 'fa fa-chevron-left'
21791                                 }
21792                             ]
21793                         },
21794                         {
21795                             tag : 'div',
21796                             class : 'carousel-next',
21797                             cn : [
21798                                 {
21799                                     tag : 'i',
21800                                     class : 'fa fa-chevron-right'
21801                                 }
21802                             ]
21803                         }
21804                     ]
21805                 });
21806             }
21807             
21808         }
21809         
21810         return cfg;
21811     },
21812     
21813     initEvents:  function()
21814     {
21815 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21816 //            this.el.on("touchstart", this.onTouchStart, this);
21817 //        }
21818         
21819         if(this.autoslide){
21820             var _this = this;
21821             
21822             this.slideFn = window.setInterval(function() {
21823                 _this.showPanelNext();
21824             }, this.timer);
21825         }
21826         
21827         if(this.showarrow){
21828             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21829             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21830         }
21831         
21832         
21833     },
21834     
21835 //    onTouchStart : function(e, el, o)
21836 //    {
21837 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21838 //            return;
21839 //        }
21840 //        
21841 //        this.showPanelNext();
21842 //    },
21843     
21844     
21845     getChildContainer : function()
21846     {
21847         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21848     },
21849     
21850     /**
21851     * register a Navigation item
21852     * @param {Roo.bootstrap.NavItem} the navitem to add
21853     */
21854     register : function(item)
21855     {
21856         this.tabs.push( item);
21857         item.navId = this.navId; // not really needed..
21858         this.addBullet();
21859     
21860     },
21861     
21862     getActivePanel : function()
21863     {
21864         var r = false;
21865         Roo.each(this.tabs, function(t) {
21866             if (t.active) {
21867                 r = t;
21868                 return false;
21869             }
21870             return null;
21871         });
21872         return r;
21873         
21874     },
21875     getPanelByName : function(n)
21876     {
21877         var r = false;
21878         Roo.each(this.tabs, function(t) {
21879             if (t.tabId == n) {
21880                 r = t;
21881                 return false;
21882             }
21883             return null;
21884         });
21885         return r;
21886     },
21887     indexOfPanel : function(p)
21888     {
21889         var r = false;
21890         Roo.each(this.tabs, function(t,i) {
21891             if (t.tabId == p.tabId) {
21892                 r = i;
21893                 return false;
21894             }
21895             return null;
21896         });
21897         return r;
21898     },
21899     /**
21900      * show a specific panel
21901      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21902      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21903      */
21904     showPanel : function (pan)
21905     {
21906         if(this.transition || typeof(pan) == 'undefined'){
21907             Roo.log("waiting for the transitionend");
21908             return false;
21909         }
21910         
21911         if (typeof(pan) == 'number') {
21912             pan = this.tabs[pan];
21913         }
21914         
21915         if (typeof(pan) == 'string') {
21916             pan = this.getPanelByName(pan);
21917         }
21918         
21919         var cur = this.getActivePanel();
21920         
21921         if(!pan || !cur){
21922             Roo.log('pan or acitve pan is undefined');
21923             return false;
21924         }
21925         
21926         if (pan.tabId == this.getActivePanel().tabId) {
21927             return true;
21928         }
21929         
21930         if (false === cur.fireEvent('beforedeactivate')) {
21931             return false;
21932         }
21933         
21934         if(this.bullets > 0 && !Roo.isTouch){
21935             this.setActiveBullet(this.indexOfPanel(pan));
21936         }
21937         
21938         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21939             
21940             //class="carousel-item carousel-item-next carousel-item-left"
21941             
21942             this.transition = true;
21943             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21944             var lr = dir == 'next' ? 'left' : 'right';
21945             pan.el.addClass(dir); // or prev
21946             pan.el.addClass('carousel-item-' + dir); // or prev
21947             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21948             cur.el.addClass(lr); // or right
21949             pan.el.addClass(lr);
21950             cur.el.addClass('carousel-item-' +lr); // or right
21951             pan.el.addClass('carousel-item-' +lr);
21952             
21953             
21954             var _this = this;
21955             cur.el.on('transitionend', function() {
21956                 Roo.log("trans end?");
21957                 
21958                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21959                 pan.setActive(true);
21960                 
21961                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21962                 cur.setActive(false);
21963                 
21964                 _this.transition = false;
21965                 
21966             }, this, { single:  true } );
21967             
21968             return true;
21969         }
21970         
21971         cur.setActive(false);
21972         pan.setActive(true);
21973         
21974         return true;
21975         
21976     },
21977     showPanelNext : function()
21978     {
21979         var i = this.indexOfPanel(this.getActivePanel());
21980         
21981         if (i >= this.tabs.length - 1 && !this.autoslide) {
21982             return;
21983         }
21984         
21985         if (i >= this.tabs.length - 1 && this.autoslide) {
21986             i = -1;
21987         }
21988         
21989         this.showPanel(this.tabs[i+1]);
21990     },
21991     
21992     showPanelPrev : function()
21993     {
21994         var i = this.indexOfPanel(this.getActivePanel());
21995         
21996         if (i  < 1 && !this.autoslide) {
21997             return;
21998         }
21999         
22000         if (i < 1 && this.autoslide) {
22001             i = this.tabs.length;
22002         }
22003         
22004         this.showPanel(this.tabs[i-1]);
22005     },
22006     
22007     
22008     addBullet: function()
22009     {
22010         if(!this.bullets || Roo.isTouch){
22011             return;
22012         }
22013         var ctr = this.el.select('.carousel-bullets',true).first();
22014         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22015         var bullet = ctr.createChild({
22016             cls : 'bullet bullet-' + i
22017         },ctr.dom.lastChild);
22018         
22019         
22020         var _this = this;
22021         
22022         bullet.on('click', (function(e, el, o, ii, t){
22023
22024             e.preventDefault();
22025
22026             this.showPanel(ii);
22027
22028             if(this.autoslide && this.slideFn){
22029                 clearInterval(this.slideFn);
22030                 this.slideFn = window.setInterval(function() {
22031                     _this.showPanelNext();
22032                 }, this.timer);
22033             }
22034
22035         }).createDelegate(this, [i, bullet], true));
22036                 
22037         
22038     },
22039      
22040     setActiveBullet : function(i)
22041     {
22042         if(Roo.isTouch){
22043             return;
22044         }
22045         
22046         Roo.each(this.el.select('.bullet', true).elements, function(el){
22047             el.removeClass('selected');
22048         });
22049
22050         var bullet = this.el.select('.bullet-' + i, true).first();
22051         
22052         if(!bullet){
22053             return;
22054         }
22055         
22056         bullet.addClass('selected');
22057     }
22058     
22059     
22060   
22061 });
22062
22063  
22064
22065  
22066  
22067 Roo.apply(Roo.bootstrap.TabGroup, {
22068     
22069     groups: {},
22070      /**
22071     * register a Navigation Group
22072     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22073     */
22074     register : function(navgrp)
22075     {
22076         this.groups[navgrp.navId] = navgrp;
22077         
22078     },
22079     /**
22080     * fetch a Navigation Group based on the navigation ID
22081     * if one does not exist , it will get created.
22082     * @param {string} the navgroup to add
22083     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22084     */
22085     get: function(navId) {
22086         if (typeof(this.groups[navId]) == 'undefined') {
22087             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22088         }
22089         return this.groups[navId] ;
22090     }
22091     
22092     
22093     
22094 });
22095
22096  /*
22097  * - LGPL
22098  *
22099  * TabPanel
22100  * 
22101  */
22102
22103 /**
22104  * @class Roo.bootstrap.TabPanel
22105  * @extends Roo.bootstrap.Component
22106  * Bootstrap TabPanel class
22107  * @cfg {Boolean} active panel active
22108  * @cfg {String} html panel content
22109  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22110  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22111  * @cfg {String} href click to link..
22112  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22113  * 
22114  * 
22115  * @constructor
22116  * Create a new TabPanel
22117  * @param {Object} config The config object
22118  */
22119
22120 Roo.bootstrap.TabPanel = function(config){
22121     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22122     this.addEvents({
22123         /**
22124              * @event changed
22125              * Fires when the active status changes
22126              * @param {Roo.bootstrap.TabPanel} this
22127              * @param {Boolean} state the new state
22128             
22129          */
22130         'changed': true,
22131         /**
22132              * @event beforedeactivate
22133              * Fires before a tab is de-activated - can be used to do validation on a form.
22134              * @param {Roo.bootstrap.TabPanel} this
22135              * @return {Boolean} false if there is an error
22136             
22137          */
22138         'beforedeactivate': true
22139      });
22140     
22141     this.tabId = this.tabId || Roo.id();
22142   
22143 };
22144
22145 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22146     
22147     active: false,
22148     html: false,
22149     tabId: false,
22150     navId : false,
22151     href : '',
22152     touchSlide : false,
22153     getAutoCreate : function(){
22154         
22155         
22156         var cfg = {
22157             tag: 'div',
22158             // item is needed for carousel - not sure if it has any effect otherwise
22159             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22160             html: this.html || ''
22161         };
22162         
22163         if(this.active){
22164             cfg.cls += ' active';
22165         }
22166         
22167         if(this.tabId){
22168             cfg.tabId = this.tabId;
22169         }
22170         
22171         
22172         
22173         return cfg;
22174     },
22175     
22176     initEvents:  function()
22177     {
22178         var p = this.parent();
22179         
22180         this.navId = this.navId || p.navId;
22181         
22182         if (typeof(this.navId) != 'undefined') {
22183             // not really needed.. but just in case.. parent should be a NavGroup.
22184             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22185             
22186             tg.register(this);
22187             
22188             var i = tg.tabs.length - 1;
22189             
22190             if(this.active && tg.bullets > 0 && i < tg.bullets){
22191                 tg.setActiveBullet(i);
22192             }
22193         }
22194         
22195         this.el.on('click', this.onClick, this);
22196         
22197         if(Roo.isTouch && this.touchSlide){
22198             this.el.on("touchstart", this.onTouchStart, this);
22199             this.el.on("touchmove", this.onTouchMove, this);
22200             this.el.on("touchend", this.onTouchEnd, this);
22201         }
22202         
22203     },
22204     
22205     onRender : function(ct, position)
22206     {
22207         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22208     },
22209     
22210     setActive : function(state)
22211     {
22212         Roo.log("panel - set active " + this.tabId + "=" + state);
22213         
22214         this.active = state;
22215         if (!state) {
22216             this.el.removeClass('active');
22217             
22218         } else  if (!this.el.hasClass('active')) {
22219             this.el.addClass('active');
22220         }
22221         
22222         this.fireEvent('changed', this, state);
22223     },
22224     
22225     onClick : function(e)
22226     {
22227         e.preventDefault();
22228         
22229         if(!this.href.length){
22230             return;
22231         }
22232         
22233         window.location.href = this.href;
22234     },
22235     
22236     startX : 0,
22237     startY : 0,
22238     endX : 0,
22239     endY : 0,
22240     swiping : false,
22241     
22242     onTouchStart : function(e)
22243     {
22244         this.swiping = false;
22245         
22246         this.startX = e.browserEvent.touches[0].clientX;
22247         this.startY = e.browserEvent.touches[0].clientY;
22248     },
22249     
22250     onTouchMove : function(e)
22251     {
22252         this.swiping = true;
22253         
22254         this.endX = e.browserEvent.touches[0].clientX;
22255         this.endY = e.browserEvent.touches[0].clientY;
22256     },
22257     
22258     onTouchEnd : function(e)
22259     {
22260         if(!this.swiping){
22261             this.onClick(e);
22262             return;
22263         }
22264         
22265         var tabGroup = this.parent();
22266         
22267         if(this.endX > this.startX){ // swiping right
22268             tabGroup.showPanelPrev();
22269             return;
22270         }
22271         
22272         if(this.startX > this.endX){ // swiping left
22273             tabGroup.showPanelNext();
22274             return;
22275         }
22276     }
22277     
22278     
22279 });
22280  
22281
22282  
22283
22284  /*
22285  * - LGPL
22286  *
22287  * DateField
22288  * 
22289  */
22290
22291 /**
22292  * @class Roo.bootstrap.DateField
22293  * @extends Roo.bootstrap.Input
22294  * Bootstrap DateField class
22295  * @cfg {Number} weekStart default 0
22296  * @cfg {String} viewMode default empty, (months|years)
22297  * @cfg {String} minViewMode default empty, (months|years)
22298  * @cfg {Number} startDate default -Infinity
22299  * @cfg {Number} endDate default Infinity
22300  * @cfg {Boolean} todayHighlight default false
22301  * @cfg {Boolean} todayBtn default false
22302  * @cfg {Boolean} calendarWeeks default false
22303  * @cfg {Object} daysOfWeekDisabled default empty
22304  * @cfg {Boolean} singleMode default false (true | false)
22305  * 
22306  * @cfg {Boolean} keyboardNavigation default true
22307  * @cfg {String} language default en
22308  * 
22309  * @constructor
22310  * Create a new DateField
22311  * @param {Object} config The config object
22312  */
22313
22314 Roo.bootstrap.DateField = function(config){
22315     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22316      this.addEvents({
22317             /**
22318              * @event show
22319              * Fires when this field show.
22320              * @param {Roo.bootstrap.DateField} this
22321              * @param {Mixed} date The date value
22322              */
22323             show : true,
22324             /**
22325              * @event show
22326              * Fires when this field hide.
22327              * @param {Roo.bootstrap.DateField} this
22328              * @param {Mixed} date The date value
22329              */
22330             hide : true,
22331             /**
22332              * @event select
22333              * Fires when select a date.
22334              * @param {Roo.bootstrap.DateField} this
22335              * @param {Mixed} date The date value
22336              */
22337             select : true,
22338             /**
22339              * @event beforeselect
22340              * Fires when before select a date.
22341              * @param {Roo.bootstrap.DateField} this
22342              * @param {Mixed} date The date value
22343              */
22344             beforeselect : true
22345         });
22346 };
22347
22348 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22349     
22350     /**
22351      * @cfg {String} format
22352      * The default date format string which can be overriden for localization support.  The format must be
22353      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22354      */
22355     format : "m/d/y",
22356     /**
22357      * @cfg {String} altFormats
22358      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22359      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22360      */
22361     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22362     
22363     weekStart : 0,
22364     
22365     viewMode : '',
22366     
22367     minViewMode : '',
22368     
22369     todayHighlight : false,
22370     
22371     todayBtn: false,
22372     
22373     language: 'en',
22374     
22375     keyboardNavigation: true,
22376     
22377     calendarWeeks: false,
22378     
22379     startDate: -Infinity,
22380     
22381     endDate: Infinity,
22382     
22383     daysOfWeekDisabled: [],
22384     
22385     _events: [],
22386     
22387     singleMode : false,
22388     
22389     UTCDate: function()
22390     {
22391         return new Date(Date.UTC.apply(Date, arguments));
22392     },
22393     
22394     UTCToday: function()
22395     {
22396         var today = new Date();
22397         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22398     },
22399     
22400     getDate: function() {
22401             var d = this.getUTCDate();
22402             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22403     },
22404     
22405     getUTCDate: function() {
22406             return this.date;
22407     },
22408     
22409     setDate: function(d) {
22410             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22411     },
22412     
22413     setUTCDate: function(d) {
22414             this.date = d;
22415             this.setValue(this.formatDate(this.date));
22416     },
22417         
22418     onRender: function(ct, position)
22419     {
22420         
22421         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22422         
22423         this.language = this.language || 'en';
22424         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22425         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22426         
22427         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22428         this.format = this.format || 'm/d/y';
22429         this.isInline = false;
22430         this.isInput = true;
22431         this.component = this.el.select('.add-on', true).first() || false;
22432         this.component = (this.component && this.component.length === 0) ? false : this.component;
22433         this.hasInput = this.component && this.inputEl().length;
22434         
22435         if (typeof(this.minViewMode === 'string')) {
22436             switch (this.minViewMode) {
22437                 case 'months':
22438                     this.minViewMode = 1;
22439                     break;
22440                 case 'years':
22441                     this.minViewMode = 2;
22442                     break;
22443                 default:
22444                     this.minViewMode = 0;
22445                     break;
22446             }
22447         }
22448         
22449         if (typeof(this.viewMode === 'string')) {
22450             switch (this.viewMode) {
22451                 case 'months':
22452                     this.viewMode = 1;
22453                     break;
22454                 case 'years':
22455                     this.viewMode = 2;
22456                     break;
22457                 default:
22458                     this.viewMode = 0;
22459                     break;
22460             }
22461         }
22462                 
22463         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22464         
22465 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22466         
22467         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22468         
22469         this.picker().on('mousedown', this.onMousedown, this);
22470         this.picker().on('click', this.onClick, this);
22471         
22472         this.picker().addClass('datepicker-dropdown');
22473         
22474         this.startViewMode = this.viewMode;
22475         
22476         if(this.singleMode){
22477             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22478                 v.setVisibilityMode(Roo.Element.DISPLAY);
22479                 v.hide();
22480             });
22481             
22482             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22483                 v.setStyle('width', '189px');
22484             });
22485         }
22486         
22487         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22488             if(!this.calendarWeeks){
22489                 v.remove();
22490                 return;
22491             }
22492             
22493             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22494             v.attr('colspan', function(i, val){
22495                 return parseInt(val) + 1;
22496             });
22497         });
22498                         
22499         
22500         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22501         
22502         this.setStartDate(this.startDate);
22503         this.setEndDate(this.endDate);
22504         
22505         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22506         
22507         this.fillDow();
22508         this.fillMonths();
22509         this.update();
22510         this.showMode();
22511         
22512         if(this.isInline) {
22513             this.showPopup();
22514         }
22515     },
22516     
22517     picker : function()
22518     {
22519         return this.pickerEl;
22520 //        return this.el.select('.datepicker', true).first();
22521     },
22522     
22523     fillDow: function()
22524     {
22525         var dowCnt = this.weekStart;
22526         
22527         var dow = {
22528             tag: 'tr',
22529             cn: [
22530                 
22531             ]
22532         };
22533         
22534         if(this.calendarWeeks){
22535             dow.cn.push({
22536                 tag: 'th',
22537                 cls: 'cw',
22538                 html: '&nbsp;'
22539             })
22540         }
22541         
22542         while (dowCnt < this.weekStart + 7) {
22543             dow.cn.push({
22544                 tag: 'th',
22545                 cls: 'dow',
22546                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22547             });
22548         }
22549         
22550         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22551     },
22552     
22553     fillMonths: function()
22554     {    
22555         var i = 0;
22556         var months = this.picker().select('>.datepicker-months td', true).first();
22557         
22558         months.dom.innerHTML = '';
22559         
22560         while (i < 12) {
22561             var month = {
22562                 tag: 'span',
22563                 cls: 'month',
22564                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22565             };
22566             
22567             months.createChild(month);
22568         }
22569         
22570     },
22571     
22572     update: function()
22573     {
22574         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;
22575         
22576         if (this.date < this.startDate) {
22577             this.viewDate = new Date(this.startDate);
22578         } else if (this.date > this.endDate) {
22579             this.viewDate = new Date(this.endDate);
22580         } else {
22581             this.viewDate = new Date(this.date);
22582         }
22583         
22584         this.fill();
22585     },
22586     
22587     fill: function() 
22588     {
22589         var d = new Date(this.viewDate),
22590                 year = d.getUTCFullYear(),
22591                 month = d.getUTCMonth(),
22592                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22593                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22594                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22595                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22596                 currentDate = this.date && this.date.valueOf(),
22597                 today = this.UTCToday();
22598         
22599         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22600         
22601 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22602         
22603 //        this.picker.select('>tfoot th.today').
22604 //                                              .text(dates[this.language].today)
22605 //                                              .toggle(this.todayBtn !== false);
22606     
22607         this.updateNavArrows();
22608         this.fillMonths();
22609                                                 
22610         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22611         
22612         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22613          
22614         prevMonth.setUTCDate(day);
22615         
22616         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22617         
22618         var nextMonth = new Date(prevMonth);
22619         
22620         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22621         
22622         nextMonth = nextMonth.valueOf();
22623         
22624         var fillMonths = false;
22625         
22626         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22627         
22628         while(prevMonth.valueOf() <= nextMonth) {
22629             var clsName = '';
22630             
22631             if (prevMonth.getUTCDay() === this.weekStart) {
22632                 if(fillMonths){
22633                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22634                 }
22635                     
22636                 fillMonths = {
22637                     tag: 'tr',
22638                     cn: []
22639                 };
22640                 
22641                 if(this.calendarWeeks){
22642                     // ISO 8601: First week contains first thursday.
22643                     // ISO also states week starts on Monday, but we can be more abstract here.
22644                     var
22645                     // Start of current week: based on weekstart/current date
22646                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22647                     // Thursday of this week
22648                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22649                     // First Thursday of year, year from thursday
22650                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22651                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22652                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22653                     
22654                     fillMonths.cn.push({
22655                         tag: 'td',
22656                         cls: 'cw',
22657                         html: calWeek
22658                     });
22659                 }
22660             }
22661             
22662             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22663                 clsName += ' old';
22664             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22665                 clsName += ' new';
22666             }
22667             if (this.todayHighlight &&
22668                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22669                 prevMonth.getUTCMonth() == today.getMonth() &&
22670                 prevMonth.getUTCDate() == today.getDate()) {
22671                 clsName += ' today';
22672             }
22673             
22674             if (currentDate && prevMonth.valueOf() === currentDate) {
22675                 clsName += ' active';
22676             }
22677             
22678             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22679                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22680                     clsName += ' disabled';
22681             }
22682             
22683             fillMonths.cn.push({
22684                 tag: 'td',
22685                 cls: 'day ' + clsName,
22686                 html: prevMonth.getDate()
22687             });
22688             
22689             prevMonth.setDate(prevMonth.getDate()+1);
22690         }
22691           
22692         var currentYear = this.date && this.date.getUTCFullYear();
22693         var currentMonth = this.date && this.date.getUTCMonth();
22694         
22695         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22696         
22697         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22698             v.removeClass('active');
22699             
22700             if(currentYear === year && k === currentMonth){
22701                 v.addClass('active');
22702             }
22703             
22704             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22705                 v.addClass('disabled');
22706             }
22707             
22708         });
22709         
22710         
22711         year = parseInt(year/10, 10) * 10;
22712         
22713         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22714         
22715         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22716         
22717         year -= 1;
22718         for (var i = -1; i < 11; i++) {
22719             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22720                 tag: 'span',
22721                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22722                 html: year
22723             });
22724             
22725             year += 1;
22726         }
22727     },
22728     
22729     showMode: function(dir) 
22730     {
22731         if (dir) {
22732             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22733         }
22734         
22735         Roo.each(this.picker().select('>div',true).elements, function(v){
22736             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22737             v.hide();
22738         });
22739         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22740     },
22741     
22742     place: function()
22743     {
22744         if(this.isInline) {
22745             return;
22746         }
22747         
22748         this.picker().removeClass(['bottom', 'top']);
22749         
22750         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22751             /*
22752              * place to the top of element!
22753              *
22754              */
22755             
22756             this.picker().addClass('top');
22757             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22758             
22759             return;
22760         }
22761         
22762         this.picker().addClass('bottom');
22763         
22764         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22765     },
22766     
22767     parseDate : function(value)
22768     {
22769         if(!value || value instanceof Date){
22770             return value;
22771         }
22772         var v = Date.parseDate(value, this.format);
22773         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22774             v = Date.parseDate(value, 'Y-m-d');
22775         }
22776         if(!v && this.altFormats){
22777             if(!this.altFormatsArray){
22778                 this.altFormatsArray = this.altFormats.split("|");
22779             }
22780             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22781                 v = Date.parseDate(value, this.altFormatsArray[i]);
22782             }
22783         }
22784         return v;
22785     },
22786     
22787     formatDate : function(date, fmt)
22788     {   
22789         return (!date || !(date instanceof Date)) ?
22790         date : date.dateFormat(fmt || this.format);
22791     },
22792     
22793     onFocus : function()
22794     {
22795         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22796         this.showPopup();
22797     },
22798     
22799     onBlur : function()
22800     {
22801         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22802         
22803         var d = this.inputEl().getValue();
22804         
22805         this.setValue(d);
22806                 
22807         this.hidePopup();
22808     },
22809     
22810     showPopup : function()
22811     {
22812         this.picker().show();
22813         this.update();
22814         this.place();
22815         
22816         this.fireEvent('showpopup', this, this.date);
22817     },
22818     
22819     hidePopup : function()
22820     {
22821         if(this.isInline) {
22822             return;
22823         }
22824         this.picker().hide();
22825         this.viewMode = this.startViewMode;
22826         this.showMode();
22827         
22828         this.fireEvent('hidepopup', this, this.date);
22829         
22830     },
22831     
22832     onMousedown: function(e)
22833     {
22834         e.stopPropagation();
22835         e.preventDefault();
22836     },
22837     
22838     keyup: function(e)
22839     {
22840         Roo.bootstrap.DateField.superclass.keyup.call(this);
22841         this.update();
22842     },
22843
22844     setValue: function(v)
22845     {
22846         if(this.fireEvent('beforeselect', this, v) !== false){
22847             var d = new Date(this.parseDate(v) ).clearTime();
22848         
22849             if(isNaN(d.getTime())){
22850                 this.date = this.viewDate = '';
22851                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22852                 return;
22853             }
22854
22855             v = this.formatDate(d);
22856
22857             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22858
22859             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22860
22861             this.update();
22862
22863             this.fireEvent('select', this, this.date);
22864         }
22865     },
22866     
22867     getValue: function()
22868     {
22869         return this.formatDate(this.date);
22870     },
22871     
22872     fireKey: function(e)
22873     {
22874         if (!this.picker().isVisible()){
22875             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22876                 this.showPopup();
22877             }
22878             return;
22879         }
22880         
22881         var dateChanged = false,
22882         dir, day, month,
22883         newDate, newViewDate;
22884         
22885         switch(e.keyCode){
22886             case 27: // escape
22887                 this.hidePopup();
22888                 e.preventDefault();
22889                 break;
22890             case 37: // left
22891             case 39: // right
22892                 if (!this.keyboardNavigation) {
22893                     break;
22894                 }
22895                 dir = e.keyCode == 37 ? -1 : 1;
22896                 
22897                 if (e.ctrlKey){
22898                     newDate = this.moveYear(this.date, dir);
22899                     newViewDate = this.moveYear(this.viewDate, dir);
22900                 } else if (e.shiftKey){
22901                     newDate = this.moveMonth(this.date, dir);
22902                     newViewDate = this.moveMonth(this.viewDate, dir);
22903                 } else {
22904                     newDate = new Date(this.date);
22905                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22906                     newViewDate = new Date(this.viewDate);
22907                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22908                 }
22909                 if (this.dateWithinRange(newDate)){
22910                     this.date = newDate;
22911                     this.viewDate = newViewDate;
22912                     this.setValue(this.formatDate(this.date));
22913 //                    this.update();
22914                     e.preventDefault();
22915                     dateChanged = true;
22916                 }
22917                 break;
22918             case 38: // up
22919             case 40: // down
22920                 if (!this.keyboardNavigation) {
22921                     break;
22922                 }
22923                 dir = e.keyCode == 38 ? -1 : 1;
22924                 if (e.ctrlKey){
22925                     newDate = this.moveYear(this.date, dir);
22926                     newViewDate = this.moveYear(this.viewDate, dir);
22927                 } else if (e.shiftKey){
22928                     newDate = this.moveMonth(this.date, dir);
22929                     newViewDate = this.moveMonth(this.viewDate, dir);
22930                 } else {
22931                     newDate = new Date(this.date);
22932                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22933                     newViewDate = new Date(this.viewDate);
22934                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22935                 }
22936                 if (this.dateWithinRange(newDate)){
22937                     this.date = newDate;
22938                     this.viewDate = newViewDate;
22939                     this.setValue(this.formatDate(this.date));
22940 //                    this.update();
22941                     e.preventDefault();
22942                     dateChanged = true;
22943                 }
22944                 break;
22945             case 13: // enter
22946                 this.setValue(this.formatDate(this.date));
22947                 this.hidePopup();
22948                 e.preventDefault();
22949                 break;
22950             case 9: // tab
22951                 this.setValue(this.formatDate(this.date));
22952                 this.hidePopup();
22953                 break;
22954             case 16: // shift
22955             case 17: // ctrl
22956             case 18: // alt
22957                 break;
22958             default :
22959                 this.hidePopup();
22960                 
22961         }
22962     },
22963     
22964     
22965     onClick: function(e) 
22966     {
22967         e.stopPropagation();
22968         e.preventDefault();
22969         
22970         var target = e.getTarget();
22971         
22972         if(target.nodeName.toLowerCase() === 'i'){
22973             target = Roo.get(target).dom.parentNode;
22974         }
22975         
22976         var nodeName = target.nodeName;
22977         var className = target.className;
22978         var html = target.innerHTML;
22979         //Roo.log(nodeName);
22980         
22981         switch(nodeName.toLowerCase()) {
22982             case 'th':
22983                 switch(className) {
22984                     case 'switch':
22985                         this.showMode(1);
22986                         break;
22987                     case 'prev':
22988                     case 'next':
22989                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22990                         switch(this.viewMode){
22991                                 case 0:
22992                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22993                                         break;
22994                                 case 1:
22995                                 case 2:
22996                                         this.viewDate = this.moveYear(this.viewDate, dir);
22997                                         break;
22998                         }
22999                         this.fill();
23000                         break;
23001                     case 'today':
23002                         var date = new Date();
23003                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23004 //                        this.fill()
23005                         this.setValue(this.formatDate(this.date));
23006                         
23007                         this.hidePopup();
23008                         break;
23009                 }
23010                 break;
23011             case 'span':
23012                 if (className.indexOf('disabled') < 0) {
23013                 if (!this.viewDate) {
23014                     this.viewDate = new Date();
23015                 }
23016                 this.viewDate.setUTCDate(1);
23017                     if (className.indexOf('month') > -1) {
23018                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23019                     } else {
23020                         var year = parseInt(html, 10) || 0;
23021                         this.viewDate.setUTCFullYear(year);
23022                         
23023                     }
23024                     
23025                     if(this.singleMode){
23026                         this.setValue(this.formatDate(this.viewDate));
23027                         this.hidePopup();
23028                         return;
23029                     }
23030                     
23031                     this.showMode(-1);
23032                     this.fill();
23033                 }
23034                 break;
23035                 
23036             case 'td':
23037                 //Roo.log(className);
23038                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23039                     var day = parseInt(html, 10) || 1;
23040                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23041                         month = (this.viewDate || new Date()).getUTCMonth();
23042
23043                     if (className.indexOf('old') > -1) {
23044                         if(month === 0 ){
23045                             month = 11;
23046                             year -= 1;
23047                         }else{
23048                             month -= 1;
23049                         }
23050                     } else if (className.indexOf('new') > -1) {
23051                         if (month == 11) {
23052                             month = 0;
23053                             year += 1;
23054                         } else {
23055                             month += 1;
23056                         }
23057                     }
23058                     //Roo.log([year,month,day]);
23059                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23060                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23061 //                    this.fill();
23062                     //Roo.log(this.formatDate(this.date));
23063                     this.setValue(this.formatDate(this.date));
23064                     this.hidePopup();
23065                 }
23066                 break;
23067         }
23068     },
23069     
23070     setStartDate: function(startDate)
23071     {
23072         this.startDate = startDate || -Infinity;
23073         if (this.startDate !== -Infinity) {
23074             this.startDate = this.parseDate(this.startDate);
23075         }
23076         this.update();
23077         this.updateNavArrows();
23078     },
23079
23080     setEndDate: function(endDate)
23081     {
23082         this.endDate = endDate || Infinity;
23083         if (this.endDate !== Infinity) {
23084             this.endDate = this.parseDate(this.endDate);
23085         }
23086         this.update();
23087         this.updateNavArrows();
23088     },
23089     
23090     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23091     {
23092         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23093         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23094             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23095         }
23096         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23097             return parseInt(d, 10);
23098         });
23099         this.update();
23100         this.updateNavArrows();
23101     },
23102     
23103     updateNavArrows: function() 
23104     {
23105         if(this.singleMode){
23106             return;
23107         }
23108         
23109         var d = new Date(this.viewDate),
23110         year = d.getUTCFullYear(),
23111         month = d.getUTCMonth();
23112         
23113         Roo.each(this.picker().select('.prev', true).elements, function(v){
23114             v.show();
23115             switch (this.viewMode) {
23116                 case 0:
23117
23118                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23119                         v.hide();
23120                     }
23121                     break;
23122                 case 1:
23123                 case 2:
23124                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23125                         v.hide();
23126                     }
23127                     break;
23128             }
23129         });
23130         
23131         Roo.each(this.picker().select('.next', true).elements, function(v){
23132             v.show();
23133             switch (this.viewMode) {
23134                 case 0:
23135
23136                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23137                         v.hide();
23138                     }
23139                     break;
23140                 case 1:
23141                 case 2:
23142                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23143                         v.hide();
23144                     }
23145                     break;
23146             }
23147         })
23148     },
23149     
23150     moveMonth: function(date, dir)
23151     {
23152         if (!dir) {
23153             return date;
23154         }
23155         var new_date = new Date(date.valueOf()),
23156         day = new_date.getUTCDate(),
23157         month = new_date.getUTCMonth(),
23158         mag = Math.abs(dir),
23159         new_month, test;
23160         dir = dir > 0 ? 1 : -1;
23161         if (mag == 1){
23162             test = dir == -1
23163             // If going back one month, make sure month is not current month
23164             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23165             ? function(){
23166                 return new_date.getUTCMonth() == month;
23167             }
23168             // If going forward one month, make sure month is as expected
23169             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23170             : function(){
23171                 return new_date.getUTCMonth() != new_month;
23172             };
23173             new_month = month + dir;
23174             new_date.setUTCMonth(new_month);
23175             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23176             if (new_month < 0 || new_month > 11) {
23177                 new_month = (new_month + 12) % 12;
23178             }
23179         } else {
23180             // For magnitudes >1, move one month at a time...
23181             for (var i=0; i<mag; i++) {
23182                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23183                 new_date = this.moveMonth(new_date, dir);
23184             }
23185             // ...then reset the day, keeping it in the new month
23186             new_month = new_date.getUTCMonth();
23187             new_date.setUTCDate(day);
23188             test = function(){
23189                 return new_month != new_date.getUTCMonth();
23190             };
23191         }
23192         // Common date-resetting loop -- if date is beyond end of month, make it
23193         // end of month
23194         while (test()){
23195             new_date.setUTCDate(--day);
23196             new_date.setUTCMonth(new_month);
23197         }
23198         return new_date;
23199     },
23200
23201     moveYear: function(date, dir)
23202     {
23203         return this.moveMonth(date, dir*12);
23204     },
23205
23206     dateWithinRange: function(date)
23207     {
23208         return date >= this.startDate && date <= this.endDate;
23209     },
23210
23211     
23212     remove: function() 
23213     {
23214         this.picker().remove();
23215     },
23216     
23217     validateValue : function(value)
23218     {
23219         if(this.getVisibilityEl().hasClass('hidden')){
23220             return true;
23221         }
23222         
23223         if(value.length < 1)  {
23224             if(this.allowBlank){
23225                 return true;
23226             }
23227             return false;
23228         }
23229         
23230         if(value.length < this.minLength){
23231             return false;
23232         }
23233         if(value.length > this.maxLength){
23234             return false;
23235         }
23236         if(this.vtype){
23237             var vt = Roo.form.VTypes;
23238             if(!vt[this.vtype](value, this)){
23239                 return false;
23240             }
23241         }
23242         if(typeof this.validator == "function"){
23243             var msg = this.validator(value);
23244             if(msg !== true){
23245                 return false;
23246             }
23247         }
23248         
23249         if(this.regex && !this.regex.test(value)){
23250             return false;
23251         }
23252         
23253         if(typeof(this.parseDate(value)) == 'undefined'){
23254             return false;
23255         }
23256         
23257         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23258             return false;
23259         }      
23260         
23261         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23262             return false;
23263         } 
23264         
23265         
23266         return true;
23267     },
23268     
23269     reset : function()
23270     {
23271         this.date = this.viewDate = '';
23272         
23273         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23274     }
23275    
23276 });
23277
23278 Roo.apply(Roo.bootstrap.DateField,  {
23279     
23280     head : {
23281         tag: 'thead',
23282         cn: [
23283         {
23284             tag: 'tr',
23285             cn: [
23286             {
23287                 tag: 'th',
23288                 cls: 'prev',
23289                 html: '<i class="fa fa-arrow-left"/>'
23290             },
23291             {
23292                 tag: 'th',
23293                 cls: 'switch',
23294                 colspan: '5'
23295             },
23296             {
23297                 tag: 'th',
23298                 cls: 'next',
23299                 html: '<i class="fa fa-arrow-right"/>'
23300             }
23301
23302             ]
23303         }
23304         ]
23305     },
23306     
23307     content : {
23308         tag: 'tbody',
23309         cn: [
23310         {
23311             tag: 'tr',
23312             cn: [
23313             {
23314                 tag: 'td',
23315                 colspan: '7'
23316             }
23317             ]
23318         }
23319         ]
23320     },
23321     
23322     footer : {
23323         tag: 'tfoot',
23324         cn: [
23325         {
23326             tag: 'tr',
23327             cn: [
23328             {
23329                 tag: 'th',
23330                 colspan: '7',
23331                 cls: 'today'
23332             }
23333                     
23334             ]
23335         }
23336         ]
23337     },
23338     
23339     dates:{
23340         en: {
23341             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23342             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23343             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23344             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23345             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23346             today: "Today"
23347         }
23348     },
23349     
23350     modes: [
23351     {
23352         clsName: 'days',
23353         navFnc: 'Month',
23354         navStep: 1
23355     },
23356     {
23357         clsName: 'months',
23358         navFnc: 'FullYear',
23359         navStep: 1
23360     },
23361     {
23362         clsName: 'years',
23363         navFnc: 'FullYear',
23364         navStep: 10
23365     }]
23366 });
23367
23368 Roo.apply(Roo.bootstrap.DateField,  {
23369   
23370     template : {
23371         tag: 'div',
23372         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23373         cn: [
23374         {
23375             tag: 'div',
23376             cls: 'datepicker-days',
23377             cn: [
23378             {
23379                 tag: 'table',
23380                 cls: 'table-condensed',
23381                 cn:[
23382                 Roo.bootstrap.DateField.head,
23383                 {
23384                     tag: 'tbody'
23385                 },
23386                 Roo.bootstrap.DateField.footer
23387                 ]
23388             }
23389             ]
23390         },
23391         {
23392             tag: 'div',
23393             cls: 'datepicker-months',
23394             cn: [
23395             {
23396                 tag: 'table',
23397                 cls: 'table-condensed',
23398                 cn:[
23399                 Roo.bootstrap.DateField.head,
23400                 Roo.bootstrap.DateField.content,
23401                 Roo.bootstrap.DateField.footer
23402                 ]
23403             }
23404             ]
23405         },
23406         {
23407             tag: 'div',
23408             cls: 'datepicker-years',
23409             cn: [
23410             {
23411                 tag: 'table',
23412                 cls: 'table-condensed',
23413                 cn:[
23414                 Roo.bootstrap.DateField.head,
23415                 Roo.bootstrap.DateField.content,
23416                 Roo.bootstrap.DateField.footer
23417                 ]
23418             }
23419             ]
23420         }
23421         ]
23422     }
23423 });
23424
23425  
23426
23427  /*
23428  * - LGPL
23429  *
23430  * TimeField
23431  * 
23432  */
23433
23434 /**
23435  * @class Roo.bootstrap.TimeField
23436  * @extends Roo.bootstrap.Input
23437  * Bootstrap DateField class
23438  * 
23439  * 
23440  * @constructor
23441  * Create a new TimeField
23442  * @param {Object} config The config object
23443  */
23444
23445 Roo.bootstrap.TimeField = function(config){
23446     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23447     this.addEvents({
23448             /**
23449              * @event show
23450              * Fires when this field show.
23451              * @param {Roo.bootstrap.DateField} thisthis
23452              * @param {Mixed} date The date value
23453              */
23454             show : true,
23455             /**
23456              * @event show
23457              * Fires when this field hide.
23458              * @param {Roo.bootstrap.DateField} this
23459              * @param {Mixed} date The date value
23460              */
23461             hide : true,
23462             /**
23463              * @event select
23464              * Fires when select a date.
23465              * @param {Roo.bootstrap.DateField} this
23466              * @param {Mixed} date The date value
23467              */
23468             select : true
23469         });
23470 };
23471
23472 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23473     
23474     /**
23475      * @cfg {String} format
23476      * The default time format string which can be overriden for localization support.  The format must be
23477      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23478      */
23479     format : "H:i",
23480
23481     getAutoCreate : function()
23482     {
23483         this.after = '<i class="fa far fa-clock"></i>';
23484         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23485         
23486          
23487     },
23488     onRender: function(ct, position)
23489     {
23490         
23491         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23492                 
23493         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23494         
23495         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23496         
23497         this.pop = this.picker().select('>.datepicker-time',true).first();
23498         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23499         
23500         this.picker().on('mousedown', this.onMousedown, this);
23501         this.picker().on('click', this.onClick, this);
23502         
23503         this.picker().addClass('datepicker-dropdown');
23504     
23505         this.fillTime();
23506         this.update();
23507             
23508         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23509         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23510         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23511         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23512         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23513         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23514
23515     },
23516     
23517     fireKey: function(e){
23518         if (!this.picker().isVisible()){
23519             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23520                 this.show();
23521             }
23522             return;
23523         }
23524
23525         e.preventDefault();
23526         
23527         switch(e.keyCode){
23528             case 27: // escape
23529                 this.hide();
23530                 break;
23531             case 37: // left
23532             case 39: // right
23533                 this.onTogglePeriod();
23534                 break;
23535             case 38: // up
23536                 this.onIncrementMinutes();
23537                 break;
23538             case 40: // down
23539                 this.onDecrementMinutes();
23540                 break;
23541             case 13: // enter
23542             case 9: // tab
23543                 this.setTime();
23544                 break;
23545         }
23546     },
23547     
23548     onClick: function(e) {
23549         e.stopPropagation();
23550         e.preventDefault();
23551     },
23552     
23553     picker : function()
23554     {
23555         return this.pickerEl;
23556     },
23557     
23558     fillTime: function()
23559     {    
23560         var time = this.pop.select('tbody', true).first();
23561         
23562         time.dom.innerHTML = '';
23563         
23564         time.createChild({
23565             tag: 'tr',
23566             cn: [
23567                 {
23568                     tag: 'td',
23569                     cn: [
23570                         {
23571                             tag: 'a',
23572                             href: '#',
23573                             cls: 'btn',
23574                             cn: [
23575                                 {
23576                                     tag: 'i',
23577                                     cls: 'hours-up fa fas fa-chevron-up'
23578                                 }
23579                             ]
23580                         } 
23581                     ]
23582                 },
23583                 {
23584                     tag: 'td',
23585                     cls: 'separator'
23586                 },
23587                 {
23588                     tag: 'td',
23589                     cn: [
23590                         {
23591                             tag: 'a',
23592                             href: '#',
23593                             cls: 'btn',
23594                             cn: [
23595                                 {
23596                                     tag: 'i',
23597                                     cls: 'minutes-up fa fas fa-chevron-up'
23598                                 }
23599                             ]
23600                         }
23601                     ]
23602                 },
23603                 {
23604                     tag: 'td',
23605                     cls: 'separator'
23606                 }
23607             ]
23608         });
23609         
23610         time.createChild({
23611             tag: 'tr',
23612             cn: [
23613                 {
23614                     tag: 'td',
23615                     cn: [
23616                         {
23617                             tag: 'span',
23618                             cls: 'timepicker-hour',
23619                             html: '00'
23620                         }  
23621                     ]
23622                 },
23623                 {
23624                     tag: 'td',
23625                     cls: 'separator',
23626                     html: ':'
23627                 },
23628                 {
23629                     tag: 'td',
23630                     cn: [
23631                         {
23632                             tag: 'span',
23633                             cls: 'timepicker-minute',
23634                             html: '00'
23635                         }  
23636                     ]
23637                 },
23638                 {
23639                     tag: 'td',
23640                     cls: 'separator'
23641                 },
23642                 {
23643                     tag: 'td',
23644                     cn: [
23645                         {
23646                             tag: 'button',
23647                             type: 'button',
23648                             cls: 'btn btn-primary period',
23649                             html: 'AM'
23650                             
23651                         }
23652                     ]
23653                 }
23654             ]
23655         });
23656         
23657         time.createChild({
23658             tag: 'tr',
23659             cn: [
23660                 {
23661                     tag: 'td',
23662                     cn: [
23663                         {
23664                             tag: 'a',
23665                             href: '#',
23666                             cls: 'btn',
23667                             cn: [
23668                                 {
23669                                     tag: 'span',
23670                                     cls: 'hours-down fa fas fa-chevron-down'
23671                                 }
23672                             ]
23673                         }
23674                     ]
23675                 },
23676                 {
23677                     tag: 'td',
23678                     cls: 'separator'
23679                 },
23680                 {
23681                     tag: 'td',
23682                     cn: [
23683                         {
23684                             tag: 'a',
23685                             href: '#',
23686                             cls: 'btn',
23687                             cn: [
23688                                 {
23689                                     tag: 'span',
23690                                     cls: 'minutes-down fa fas fa-chevron-down'
23691                                 }
23692                             ]
23693                         }
23694                     ]
23695                 },
23696                 {
23697                     tag: 'td',
23698                     cls: 'separator'
23699                 }
23700             ]
23701         });
23702         
23703     },
23704     
23705     update: function()
23706     {
23707         
23708         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23709         
23710         this.fill();
23711     },
23712     
23713     fill: function() 
23714     {
23715         var hours = this.time.getHours();
23716         var minutes = this.time.getMinutes();
23717         var period = 'AM';
23718         
23719         if(hours > 11){
23720             period = 'PM';
23721         }
23722         
23723         if(hours == 0){
23724             hours = 12;
23725         }
23726         
23727         
23728         if(hours > 12){
23729             hours = hours - 12;
23730         }
23731         
23732         if(hours < 10){
23733             hours = '0' + hours;
23734         }
23735         
23736         if(minutes < 10){
23737             minutes = '0' + minutes;
23738         }
23739         
23740         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23741         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23742         this.pop.select('button', true).first().dom.innerHTML = period;
23743         
23744     },
23745     
23746     place: function()
23747     {   
23748         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23749         
23750         var cls = ['bottom'];
23751         
23752         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23753             cls.pop();
23754             cls.push('top');
23755         }
23756         
23757         cls.push('right');
23758         
23759         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23760             cls.pop();
23761             cls.push('left');
23762         }
23763         //this.picker().setXY(20000,20000);
23764         this.picker().addClass(cls.join('-'));
23765         
23766         var _this = this;
23767         
23768         Roo.each(cls, function(c){
23769             if(c == 'bottom'){
23770                 (function() {
23771                  //  
23772                 }).defer(200);
23773                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23774                 //_this.picker().setTop(_this.inputEl().getHeight());
23775                 return;
23776             }
23777             if(c == 'top'){
23778                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23779                 
23780                 //_this.picker().setTop(0 - _this.picker().getHeight());
23781                 return;
23782             }
23783             /*
23784             if(c == 'left'){
23785                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23786                 return;
23787             }
23788             if(c == 'right'){
23789                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23790                 return;
23791             }
23792             */
23793         });
23794         
23795     },
23796   
23797     onFocus : function()
23798     {
23799         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23800         this.show();
23801     },
23802     
23803     onBlur : function()
23804     {
23805         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23806         this.hide();
23807     },
23808     
23809     show : function()
23810     {
23811         this.picker().show();
23812         this.pop.show();
23813         this.update();
23814         this.place();
23815         
23816         this.fireEvent('show', this, this.date);
23817     },
23818     
23819     hide : function()
23820     {
23821         this.picker().hide();
23822         this.pop.hide();
23823         
23824         this.fireEvent('hide', this, this.date);
23825     },
23826     
23827     setTime : function()
23828     {
23829         this.hide();
23830         this.setValue(this.time.format(this.format));
23831         
23832         this.fireEvent('select', this, this.date);
23833         
23834         
23835     },
23836     
23837     onMousedown: function(e){
23838         e.stopPropagation();
23839         e.preventDefault();
23840     },
23841     
23842     onIncrementHours: function()
23843     {
23844         Roo.log('onIncrementHours');
23845         this.time = this.time.add(Date.HOUR, 1);
23846         this.update();
23847         
23848     },
23849     
23850     onDecrementHours: function()
23851     {
23852         Roo.log('onDecrementHours');
23853         this.time = this.time.add(Date.HOUR, -1);
23854         this.update();
23855     },
23856     
23857     onIncrementMinutes: function()
23858     {
23859         Roo.log('onIncrementMinutes');
23860         this.time = this.time.add(Date.MINUTE, 1);
23861         this.update();
23862     },
23863     
23864     onDecrementMinutes: function()
23865     {
23866         Roo.log('onDecrementMinutes');
23867         this.time = this.time.add(Date.MINUTE, -1);
23868         this.update();
23869     },
23870     
23871     onTogglePeriod: function()
23872     {
23873         Roo.log('onTogglePeriod');
23874         this.time = this.time.add(Date.HOUR, 12);
23875         this.update();
23876     }
23877     
23878    
23879 });
23880  
23881
23882 Roo.apply(Roo.bootstrap.TimeField,  {
23883   
23884     template : {
23885         tag: 'div',
23886         cls: 'datepicker dropdown-menu',
23887         cn: [
23888             {
23889                 tag: 'div',
23890                 cls: 'datepicker-time',
23891                 cn: [
23892                 {
23893                     tag: 'table',
23894                     cls: 'table-condensed',
23895                     cn:[
23896                         {
23897                             tag: 'tbody',
23898                             cn: [
23899                                 {
23900                                     tag: 'tr',
23901                                     cn: [
23902                                     {
23903                                         tag: 'td',
23904                                         colspan: '7'
23905                                     }
23906                                     ]
23907                                 }
23908                             ]
23909                         },
23910                         {
23911                             tag: 'tfoot',
23912                             cn: [
23913                                 {
23914                                     tag: 'tr',
23915                                     cn: [
23916                                     {
23917                                         tag: 'th',
23918                                         colspan: '7',
23919                                         cls: '',
23920                                         cn: [
23921                                             {
23922                                                 tag: 'button',
23923                                                 cls: 'btn btn-info ok',
23924                                                 html: 'OK'
23925                                             }
23926                                         ]
23927                                     }
23928                     
23929                                     ]
23930                                 }
23931                             ]
23932                         }
23933                     ]
23934                 }
23935                 ]
23936             }
23937         ]
23938     }
23939 });
23940
23941  
23942
23943  /*
23944  * - LGPL
23945  *
23946  * MonthField
23947  * 
23948  */
23949
23950 /**
23951  * @class Roo.bootstrap.MonthField
23952  * @extends Roo.bootstrap.Input
23953  * Bootstrap MonthField class
23954  * 
23955  * @cfg {String} language default en
23956  * 
23957  * @constructor
23958  * Create a new MonthField
23959  * @param {Object} config The config object
23960  */
23961
23962 Roo.bootstrap.MonthField = function(config){
23963     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23964     
23965     this.addEvents({
23966         /**
23967          * @event show
23968          * Fires when this field show.
23969          * @param {Roo.bootstrap.MonthField} this
23970          * @param {Mixed} date The date value
23971          */
23972         show : true,
23973         /**
23974          * @event show
23975          * Fires when this field hide.
23976          * @param {Roo.bootstrap.MonthField} this
23977          * @param {Mixed} date The date value
23978          */
23979         hide : true,
23980         /**
23981          * @event select
23982          * Fires when select a date.
23983          * @param {Roo.bootstrap.MonthField} this
23984          * @param {String} oldvalue The old value
23985          * @param {String} newvalue The new value
23986          */
23987         select : true
23988     });
23989 };
23990
23991 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23992     
23993     onRender: function(ct, position)
23994     {
23995         
23996         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23997         
23998         this.language = this.language || 'en';
23999         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24000         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24001         
24002         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24003         this.isInline = false;
24004         this.isInput = true;
24005         this.component = this.el.select('.add-on', true).first() || false;
24006         this.component = (this.component && this.component.length === 0) ? false : this.component;
24007         this.hasInput = this.component && this.inputEL().length;
24008         
24009         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24010         
24011         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24012         
24013         this.picker().on('mousedown', this.onMousedown, this);
24014         this.picker().on('click', this.onClick, this);
24015         
24016         this.picker().addClass('datepicker-dropdown');
24017         
24018         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24019             v.setStyle('width', '189px');
24020         });
24021         
24022         this.fillMonths();
24023         
24024         this.update();
24025         
24026         if(this.isInline) {
24027             this.show();
24028         }
24029         
24030     },
24031     
24032     setValue: function(v, suppressEvent)
24033     {   
24034         var o = this.getValue();
24035         
24036         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24037         
24038         this.update();
24039
24040         if(suppressEvent !== true){
24041             this.fireEvent('select', this, o, v);
24042         }
24043         
24044     },
24045     
24046     getValue: function()
24047     {
24048         return this.value;
24049     },
24050     
24051     onClick: function(e) 
24052     {
24053         e.stopPropagation();
24054         e.preventDefault();
24055         
24056         var target = e.getTarget();
24057         
24058         if(target.nodeName.toLowerCase() === 'i'){
24059             target = Roo.get(target).dom.parentNode;
24060         }
24061         
24062         var nodeName = target.nodeName;
24063         var className = target.className;
24064         var html = target.innerHTML;
24065         
24066         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24067             return;
24068         }
24069         
24070         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24071         
24072         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24073         
24074         this.hide();
24075                         
24076     },
24077     
24078     picker : function()
24079     {
24080         return this.pickerEl;
24081     },
24082     
24083     fillMonths: function()
24084     {    
24085         var i = 0;
24086         var months = this.picker().select('>.datepicker-months td', true).first();
24087         
24088         months.dom.innerHTML = '';
24089         
24090         while (i < 12) {
24091             var month = {
24092                 tag: 'span',
24093                 cls: 'month',
24094                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24095             };
24096             
24097             months.createChild(month);
24098         }
24099         
24100     },
24101     
24102     update: function()
24103     {
24104         var _this = this;
24105         
24106         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24107             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24108         }
24109         
24110         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24111             e.removeClass('active');
24112             
24113             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24114                 e.addClass('active');
24115             }
24116         })
24117     },
24118     
24119     place: function()
24120     {
24121         if(this.isInline) {
24122             return;
24123         }
24124         
24125         this.picker().removeClass(['bottom', 'top']);
24126         
24127         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24128             /*
24129              * place to the top of element!
24130              *
24131              */
24132             
24133             this.picker().addClass('top');
24134             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24135             
24136             return;
24137         }
24138         
24139         this.picker().addClass('bottom');
24140         
24141         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24142     },
24143     
24144     onFocus : function()
24145     {
24146         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24147         this.show();
24148     },
24149     
24150     onBlur : function()
24151     {
24152         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24153         
24154         var d = this.inputEl().getValue();
24155         
24156         this.setValue(d);
24157                 
24158         this.hide();
24159     },
24160     
24161     show : function()
24162     {
24163         this.picker().show();
24164         this.picker().select('>.datepicker-months', true).first().show();
24165         this.update();
24166         this.place();
24167         
24168         this.fireEvent('show', this, this.date);
24169     },
24170     
24171     hide : function()
24172     {
24173         if(this.isInline) {
24174             return;
24175         }
24176         this.picker().hide();
24177         this.fireEvent('hide', this, this.date);
24178         
24179     },
24180     
24181     onMousedown: function(e)
24182     {
24183         e.stopPropagation();
24184         e.preventDefault();
24185     },
24186     
24187     keyup: function(e)
24188     {
24189         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24190         this.update();
24191     },
24192
24193     fireKey: function(e)
24194     {
24195         if (!this.picker().isVisible()){
24196             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24197                 this.show();
24198             }
24199             return;
24200         }
24201         
24202         var dir;
24203         
24204         switch(e.keyCode){
24205             case 27: // escape
24206                 this.hide();
24207                 e.preventDefault();
24208                 break;
24209             case 37: // left
24210             case 39: // right
24211                 dir = e.keyCode == 37 ? -1 : 1;
24212                 
24213                 this.vIndex = this.vIndex + dir;
24214                 
24215                 if(this.vIndex < 0){
24216                     this.vIndex = 0;
24217                 }
24218                 
24219                 if(this.vIndex > 11){
24220                     this.vIndex = 11;
24221                 }
24222                 
24223                 if(isNaN(this.vIndex)){
24224                     this.vIndex = 0;
24225                 }
24226                 
24227                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24228                 
24229                 break;
24230             case 38: // up
24231             case 40: // down
24232                 
24233                 dir = e.keyCode == 38 ? -1 : 1;
24234                 
24235                 this.vIndex = this.vIndex + dir * 4;
24236                 
24237                 if(this.vIndex < 0){
24238                     this.vIndex = 0;
24239                 }
24240                 
24241                 if(this.vIndex > 11){
24242                     this.vIndex = 11;
24243                 }
24244                 
24245                 if(isNaN(this.vIndex)){
24246                     this.vIndex = 0;
24247                 }
24248                 
24249                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24250                 break;
24251                 
24252             case 13: // enter
24253                 
24254                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24255                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24256                 }
24257                 
24258                 this.hide();
24259                 e.preventDefault();
24260                 break;
24261             case 9: // tab
24262                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24263                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24264                 }
24265                 this.hide();
24266                 break;
24267             case 16: // shift
24268             case 17: // ctrl
24269             case 18: // alt
24270                 break;
24271             default :
24272                 this.hide();
24273                 
24274         }
24275     },
24276     
24277     remove: function() 
24278     {
24279         this.picker().remove();
24280     }
24281    
24282 });
24283
24284 Roo.apply(Roo.bootstrap.MonthField,  {
24285     
24286     content : {
24287         tag: 'tbody',
24288         cn: [
24289         {
24290             tag: 'tr',
24291             cn: [
24292             {
24293                 tag: 'td',
24294                 colspan: '7'
24295             }
24296             ]
24297         }
24298         ]
24299     },
24300     
24301     dates:{
24302         en: {
24303             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24304             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24305         }
24306     }
24307 });
24308
24309 Roo.apply(Roo.bootstrap.MonthField,  {
24310   
24311     template : {
24312         tag: 'div',
24313         cls: 'datepicker dropdown-menu roo-dynamic',
24314         cn: [
24315             {
24316                 tag: 'div',
24317                 cls: 'datepicker-months',
24318                 cn: [
24319                 {
24320                     tag: 'table',
24321                     cls: 'table-condensed',
24322                     cn:[
24323                         Roo.bootstrap.DateField.content
24324                     ]
24325                 }
24326                 ]
24327             }
24328         ]
24329     }
24330 });
24331
24332  
24333
24334  
24335  /*
24336  * - LGPL
24337  *
24338  * CheckBox
24339  * 
24340  */
24341
24342 /**
24343  * @class Roo.bootstrap.CheckBox
24344  * @extends Roo.bootstrap.Input
24345  * Bootstrap CheckBox class
24346  * 
24347  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24348  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24349  * @cfg {String} boxLabel The text that appears beside the checkbox
24350  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24351  * @cfg {Boolean} checked initnal the element
24352  * @cfg {Boolean} inline inline the element (default false)
24353  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24354  * @cfg {String} tooltip label tooltip
24355  * 
24356  * @constructor
24357  * Create a new CheckBox
24358  * @param {Object} config The config object
24359  */
24360
24361 Roo.bootstrap.CheckBox = function(config){
24362     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24363    
24364     this.addEvents({
24365         /**
24366         * @event check
24367         * Fires when the element is checked or unchecked.
24368         * @param {Roo.bootstrap.CheckBox} this This input
24369         * @param {Boolean} checked The new checked value
24370         */
24371        check : true,
24372        /**
24373         * @event click
24374         * Fires when the element is click.
24375         * @param {Roo.bootstrap.CheckBox} this This input
24376         */
24377        click : true
24378     });
24379     
24380 };
24381
24382 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24383   
24384     inputType: 'checkbox',
24385     inputValue: 1,
24386     valueOff: 0,
24387     boxLabel: false,
24388     checked: false,
24389     weight : false,
24390     inline: false,
24391     tooltip : '',
24392     
24393     // checkbox success does not make any sense really.. 
24394     invalidClass : "",
24395     validClass : "",
24396     
24397     
24398     getAutoCreate : function()
24399     {
24400         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24401         
24402         var id = Roo.id();
24403         
24404         var cfg = {};
24405         
24406         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24407         
24408         if(this.inline){
24409             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24410         }
24411         
24412         var input =  {
24413             tag: 'input',
24414             id : id,
24415             type : this.inputType,
24416             value : this.inputValue,
24417             cls : 'roo-' + this.inputType, //'form-box',
24418             placeholder : this.placeholder || ''
24419             
24420         };
24421         
24422         if(this.inputType != 'radio'){
24423             var hidden =  {
24424                 tag: 'input',
24425                 type : 'hidden',
24426                 cls : 'roo-hidden-value',
24427                 value : this.checked ? this.inputValue : this.valueOff
24428             };
24429         }
24430         
24431             
24432         if (this.weight) { // Validity check?
24433             cfg.cls += " " + this.inputType + "-" + this.weight;
24434         }
24435         
24436         if (this.disabled) {
24437             input.disabled=true;
24438         }
24439         
24440         if(this.checked){
24441             input.checked = this.checked;
24442         }
24443         
24444         if (this.name) {
24445             
24446             input.name = this.name;
24447             
24448             if(this.inputType != 'radio'){
24449                 hidden.name = this.name;
24450                 input.name = '_hidden_' + this.name;
24451             }
24452         }
24453         
24454         if (this.size) {
24455             input.cls += ' input-' + this.size;
24456         }
24457         
24458         var settings=this;
24459         
24460         ['xs','sm','md','lg'].map(function(size){
24461             if (settings[size]) {
24462                 cfg.cls += ' col-' + size + '-' + settings[size];
24463             }
24464         });
24465         
24466         var inputblock = input;
24467          
24468         if (this.before || this.after) {
24469             
24470             inputblock = {
24471                 cls : 'input-group',
24472                 cn :  [] 
24473             };
24474             
24475             if (this.before) {
24476                 inputblock.cn.push({
24477                     tag :'span',
24478                     cls : 'input-group-addon',
24479                     html : this.before
24480                 });
24481             }
24482             
24483             inputblock.cn.push(input);
24484             
24485             if(this.inputType != 'radio'){
24486                 inputblock.cn.push(hidden);
24487             }
24488             
24489             if (this.after) {
24490                 inputblock.cn.push({
24491                     tag :'span',
24492                     cls : 'input-group-addon',
24493                     html : this.after
24494                 });
24495             }
24496             
24497         }
24498         var boxLabelCfg = false;
24499         
24500         if(this.boxLabel){
24501            
24502             boxLabelCfg = {
24503                 tag: 'label',
24504                 //'for': id, // box label is handled by onclick - so no for...
24505                 cls: 'box-label',
24506                 html: this.boxLabel
24507             };
24508             if(this.tooltip){
24509                 boxLabelCfg.tooltip = this.tooltip;
24510             }
24511              
24512         }
24513         
24514         
24515         if (align ==='left' && this.fieldLabel.length) {
24516 //                Roo.log("left and has label");
24517             cfg.cn = [
24518                 {
24519                     tag: 'label',
24520                     'for' :  id,
24521                     cls : 'control-label',
24522                     html : this.fieldLabel
24523                 },
24524                 {
24525                     cls : "", 
24526                     cn: [
24527                         inputblock
24528                     ]
24529                 }
24530             ];
24531             
24532             if (boxLabelCfg) {
24533                 cfg.cn[1].cn.push(boxLabelCfg);
24534             }
24535             
24536             if(this.labelWidth > 12){
24537                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24538             }
24539             
24540             if(this.labelWidth < 13 && this.labelmd == 0){
24541                 this.labelmd = this.labelWidth;
24542             }
24543             
24544             if(this.labellg > 0){
24545                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24546                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24547             }
24548             
24549             if(this.labelmd > 0){
24550                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24551                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24552             }
24553             
24554             if(this.labelsm > 0){
24555                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24556                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24557             }
24558             
24559             if(this.labelxs > 0){
24560                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24561                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24562             }
24563             
24564         } else if ( this.fieldLabel.length) {
24565 //                Roo.log(" label");
24566                 cfg.cn = [
24567                    
24568                     {
24569                         tag: this.boxLabel ? 'span' : 'label',
24570                         'for': id,
24571                         cls: 'control-label box-input-label',
24572                         //cls : 'input-group-addon',
24573                         html : this.fieldLabel
24574                     },
24575                     
24576                     inputblock
24577                     
24578                 ];
24579                 if (boxLabelCfg) {
24580                     cfg.cn.push(boxLabelCfg);
24581                 }
24582
24583         } else {
24584             
24585 //                Roo.log(" no label && no align");
24586                 cfg.cn = [  inputblock ] ;
24587                 if (boxLabelCfg) {
24588                     cfg.cn.push(boxLabelCfg);
24589                 }
24590
24591                 
24592         }
24593         
24594        
24595         
24596         if(this.inputType != 'radio'){
24597             cfg.cn.push(hidden);
24598         }
24599         
24600         return cfg;
24601         
24602     },
24603     
24604     /**
24605      * return the real input element.
24606      */
24607     inputEl: function ()
24608     {
24609         return this.el.select('input.roo-' + this.inputType,true).first();
24610     },
24611     hiddenEl: function ()
24612     {
24613         return this.el.select('input.roo-hidden-value',true).first();
24614     },
24615     
24616     labelEl: function()
24617     {
24618         return this.el.select('label.control-label',true).first();
24619     },
24620     /* depricated... */
24621     
24622     label: function()
24623     {
24624         return this.labelEl();
24625     },
24626     
24627     boxLabelEl: function()
24628     {
24629         return this.el.select('label.box-label',true).first();
24630     },
24631     
24632     initEvents : function()
24633     {
24634 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24635         
24636         this.inputEl().on('click', this.onClick,  this);
24637         
24638         if (this.boxLabel) { 
24639             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24640         }
24641         
24642         this.startValue = this.getValue();
24643         
24644         if(this.groupId){
24645             Roo.bootstrap.CheckBox.register(this);
24646         }
24647     },
24648     
24649     onClick : function(e)
24650     {   
24651         if(this.fireEvent('click', this, e) !== false){
24652             this.setChecked(!this.checked);
24653         }
24654         
24655     },
24656     
24657     setChecked : function(state,suppressEvent)
24658     {
24659         this.startValue = this.getValue();
24660
24661         if(this.inputType == 'radio'){
24662             
24663             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24664                 e.dom.checked = false;
24665             });
24666             
24667             this.inputEl().dom.checked = true;
24668             
24669             this.inputEl().dom.value = this.inputValue;
24670             
24671             if(suppressEvent !== true){
24672                 this.fireEvent('check', this, true);
24673             }
24674             
24675             this.validate();
24676             
24677             return;
24678         }
24679         
24680         this.checked = state;
24681         
24682         this.inputEl().dom.checked = state;
24683         
24684         
24685         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24686         
24687         if(suppressEvent !== true){
24688             this.fireEvent('check', this, state);
24689         }
24690         
24691         this.validate();
24692     },
24693     
24694     getValue : function()
24695     {
24696         if(this.inputType == 'radio'){
24697             return this.getGroupValue();
24698         }
24699         
24700         return this.hiddenEl().dom.value;
24701         
24702     },
24703     
24704     getGroupValue : function()
24705     {
24706         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24707             return '';
24708         }
24709         
24710         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24711     },
24712     
24713     setValue : function(v,suppressEvent)
24714     {
24715         if(this.inputType == 'radio'){
24716             this.setGroupValue(v, suppressEvent);
24717             return;
24718         }
24719         
24720         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24721         
24722         this.validate();
24723     },
24724     
24725     setGroupValue : function(v, suppressEvent)
24726     {
24727         this.startValue = this.getValue();
24728         
24729         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24730             e.dom.checked = false;
24731             
24732             if(e.dom.value == v){
24733                 e.dom.checked = true;
24734             }
24735         });
24736         
24737         if(suppressEvent !== true){
24738             this.fireEvent('check', this, true);
24739         }
24740
24741         this.validate();
24742         
24743         return;
24744     },
24745     
24746     validate : function()
24747     {
24748         if(this.getVisibilityEl().hasClass('hidden')){
24749             return true;
24750         }
24751         
24752         if(
24753                 this.disabled || 
24754                 (this.inputType == 'radio' && this.validateRadio()) ||
24755                 (this.inputType == 'checkbox' && this.validateCheckbox())
24756         ){
24757             this.markValid();
24758             return true;
24759         }
24760         
24761         this.markInvalid();
24762         return false;
24763     },
24764     
24765     validateRadio : function()
24766     {
24767         if(this.getVisibilityEl().hasClass('hidden')){
24768             return true;
24769         }
24770         
24771         if(this.allowBlank){
24772             return true;
24773         }
24774         
24775         var valid = false;
24776         
24777         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24778             if(!e.dom.checked){
24779                 return;
24780             }
24781             
24782             valid = true;
24783             
24784             return false;
24785         });
24786         
24787         return valid;
24788     },
24789     
24790     validateCheckbox : function()
24791     {
24792         if(!this.groupId){
24793             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24794             //return (this.getValue() == this.inputValue) ? true : false;
24795         }
24796         
24797         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24798         
24799         if(!group){
24800             return false;
24801         }
24802         
24803         var r = false;
24804         
24805         for(var i in group){
24806             if(group[i].el.isVisible(true)){
24807                 r = false;
24808                 break;
24809             }
24810             
24811             r = true;
24812         }
24813         
24814         for(var i in group){
24815             if(r){
24816                 break;
24817             }
24818             
24819             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24820         }
24821         
24822         return r;
24823     },
24824     
24825     /**
24826      * Mark this field as valid
24827      */
24828     markValid : function()
24829     {
24830         var _this = this;
24831         
24832         this.fireEvent('valid', this);
24833         
24834         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24835         
24836         if(this.groupId){
24837             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24838         }
24839         
24840         if(label){
24841             label.markValid();
24842         }
24843
24844         if(this.inputType == 'radio'){
24845             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24846                 var fg = e.findParent('.form-group', false, true);
24847                 if (Roo.bootstrap.version == 3) {
24848                     fg.removeClass([_this.invalidClass, _this.validClass]);
24849                     fg.addClass(_this.validClass);
24850                 } else {
24851                     fg.removeClass(['is-valid', 'is-invalid']);
24852                     fg.addClass('is-valid');
24853                 }
24854             });
24855             
24856             return;
24857         }
24858
24859         if(!this.groupId){
24860             var fg = this.el.findParent('.form-group', false, true);
24861             if (Roo.bootstrap.version == 3) {
24862                 fg.removeClass([this.invalidClass, this.validClass]);
24863                 fg.addClass(this.validClass);
24864             } else {
24865                 fg.removeClass(['is-valid', 'is-invalid']);
24866                 fg.addClass('is-valid');
24867             }
24868             return;
24869         }
24870         
24871         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24872         
24873         if(!group){
24874             return;
24875         }
24876         
24877         for(var i in group){
24878             var fg = group[i].el.findParent('.form-group', false, true);
24879             if (Roo.bootstrap.version == 3) {
24880                 fg.removeClass([this.invalidClass, this.validClass]);
24881                 fg.addClass(this.validClass);
24882             } else {
24883                 fg.removeClass(['is-valid', 'is-invalid']);
24884                 fg.addClass('is-valid');
24885             }
24886         }
24887     },
24888     
24889      /**
24890      * Mark this field as invalid
24891      * @param {String} msg The validation message
24892      */
24893     markInvalid : function(msg)
24894     {
24895         if(this.allowBlank){
24896             return;
24897         }
24898         
24899         var _this = this;
24900         
24901         this.fireEvent('invalid', this, msg);
24902         
24903         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24904         
24905         if(this.groupId){
24906             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24907         }
24908         
24909         if(label){
24910             label.markInvalid();
24911         }
24912             
24913         if(this.inputType == 'radio'){
24914             
24915             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24916                 var fg = e.findParent('.form-group', false, true);
24917                 if (Roo.bootstrap.version == 3) {
24918                     fg.removeClass([_this.invalidClass, _this.validClass]);
24919                     fg.addClass(_this.invalidClass);
24920                 } else {
24921                     fg.removeClass(['is-invalid', 'is-valid']);
24922                     fg.addClass('is-invalid');
24923                 }
24924             });
24925             
24926             return;
24927         }
24928         
24929         if(!this.groupId){
24930             var fg = this.el.findParent('.form-group', false, true);
24931             if (Roo.bootstrap.version == 3) {
24932                 fg.removeClass([_this.invalidClass, _this.validClass]);
24933                 fg.addClass(_this.invalidClass);
24934             } else {
24935                 fg.removeClass(['is-invalid', 'is-valid']);
24936                 fg.addClass('is-invalid');
24937             }
24938             return;
24939         }
24940         
24941         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24942         
24943         if(!group){
24944             return;
24945         }
24946         
24947         for(var i in group){
24948             var fg = group[i].el.findParent('.form-group', false, true);
24949             if (Roo.bootstrap.version == 3) {
24950                 fg.removeClass([_this.invalidClass, _this.validClass]);
24951                 fg.addClass(_this.invalidClass);
24952             } else {
24953                 fg.removeClass(['is-invalid', 'is-valid']);
24954                 fg.addClass('is-invalid');
24955             }
24956         }
24957         
24958     },
24959     
24960     clearInvalid : function()
24961     {
24962         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24963         
24964         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24965         
24966         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24967         
24968         if (label && label.iconEl) {
24969             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24970             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24971         }
24972     },
24973     
24974     disable : function()
24975     {
24976         if(this.inputType != 'radio'){
24977             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24978             return;
24979         }
24980         
24981         var _this = this;
24982         
24983         if(this.rendered){
24984             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24985                 _this.getActionEl().addClass(this.disabledClass);
24986                 e.dom.disabled = true;
24987             });
24988         }
24989         
24990         this.disabled = true;
24991         this.fireEvent("disable", this);
24992         return this;
24993     },
24994
24995     enable : function()
24996     {
24997         if(this.inputType != 'radio'){
24998             Roo.bootstrap.CheckBox.superclass.enable.call(this);
24999             return;
25000         }
25001         
25002         var _this = this;
25003         
25004         if(this.rendered){
25005             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25006                 _this.getActionEl().removeClass(this.disabledClass);
25007                 e.dom.disabled = false;
25008             });
25009         }
25010         
25011         this.disabled = false;
25012         this.fireEvent("enable", this);
25013         return this;
25014     },
25015     
25016     setBoxLabel : function(v)
25017     {
25018         this.boxLabel = v;
25019         
25020         if(this.rendered){
25021             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25022         }
25023     }
25024
25025 });
25026
25027 Roo.apply(Roo.bootstrap.CheckBox, {
25028     
25029     groups: {},
25030     
25031      /**
25032     * register a CheckBox Group
25033     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25034     */
25035     register : function(checkbox)
25036     {
25037         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25038             this.groups[checkbox.groupId] = {};
25039         }
25040         
25041         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25042             return;
25043         }
25044         
25045         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25046         
25047     },
25048     /**
25049     * fetch a CheckBox Group based on the group ID
25050     * @param {string} the group ID
25051     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25052     */
25053     get: function(groupId) {
25054         if (typeof(this.groups[groupId]) == 'undefined') {
25055             return false;
25056         }
25057         
25058         return this.groups[groupId] ;
25059     }
25060     
25061     
25062 });
25063 /*
25064  * - LGPL
25065  *
25066  * RadioItem
25067  * 
25068  */
25069
25070 /**
25071  * @class Roo.bootstrap.Radio
25072  * @extends Roo.bootstrap.Component
25073  * Bootstrap Radio class
25074  * @cfg {String} boxLabel - the label associated
25075  * @cfg {String} value - the value of radio
25076  * 
25077  * @constructor
25078  * Create a new Radio
25079  * @param {Object} config The config object
25080  */
25081 Roo.bootstrap.Radio = function(config){
25082     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25083     
25084 };
25085
25086 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25087     
25088     boxLabel : '',
25089     
25090     value : '',
25091     
25092     getAutoCreate : function()
25093     {
25094         var cfg = {
25095             tag : 'div',
25096             cls : 'form-group radio',
25097             cn : [
25098                 {
25099                     tag : 'label',
25100                     cls : 'box-label',
25101                     html : this.boxLabel
25102                 }
25103             ]
25104         };
25105         
25106         return cfg;
25107     },
25108     
25109     initEvents : function() 
25110     {
25111         this.parent().register(this);
25112         
25113         this.el.on('click', this.onClick, this);
25114         
25115     },
25116     
25117     onClick : function(e)
25118     {
25119         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25120             this.setChecked(true);
25121         }
25122     },
25123     
25124     setChecked : function(state, suppressEvent)
25125     {
25126         this.parent().setValue(this.value, suppressEvent);
25127         
25128     },
25129     
25130     setBoxLabel : function(v)
25131     {
25132         this.boxLabel = v;
25133         
25134         if(this.rendered){
25135             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25136         }
25137     }
25138     
25139 });
25140  
25141
25142  /*
25143  * - LGPL
25144  *
25145  * Input
25146  * 
25147  */
25148
25149 /**
25150  * @class Roo.bootstrap.SecurePass
25151  * @extends Roo.bootstrap.Input
25152  * Bootstrap SecurePass class
25153  *
25154  * 
25155  * @constructor
25156  * Create a new SecurePass
25157  * @param {Object} config The config object
25158  */
25159  
25160 Roo.bootstrap.SecurePass = function (config) {
25161     // these go here, so the translation tool can replace them..
25162     this.errors = {
25163         PwdEmpty: "Please type a password, and then retype it to confirm.",
25164         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25165         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25166         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25167         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25168         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25169         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25170         TooWeak: "Your password is Too Weak."
25171     },
25172     this.meterLabel = "Password strength:";
25173     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25174     this.meterClass = [
25175         "roo-password-meter-tooweak", 
25176         "roo-password-meter-weak", 
25177         "roo-password-meter-medium", 
25178         "roo-password-meter-strong", 
25179         "roo-password-meter-grey"
25180     ];
25181     
25182     this.errors = {};
25183     
25184     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25185 }
25186
25187 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25188     /**
25189      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25190      * {
25191      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25192      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25193      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25194      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25195      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25196      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25197      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25198      * })
25199      */
25200     // private
25201     
25202     meterWidth: 300,
25203     errorMsg :'',    
25204     errors: false,
25205     imageRoot: '/',
25206     /**
25207      * @cfg {String/Object} Label for the strength meter (defaults to
25208      * 'Password strength:')
25209      */
25210     // private
25211     meterLabel: '',
25212     /**
25213      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25214      * ['Weak', 'Medium', 'Strong'])
25215      */
25216     // private    
25217     pwdStrengths: false,    
25218     // private
25219     strength: 0,
25220     // private
25221     _lastPwd: null,
25222     // private
25223     kCapitalLetter: 0,
25224     kSmallLetter: 1,
25225     kDigit: 2,
25226     kPunctuation: 3,
25227     
25228     insecure: false,
25229     // private
25230     initEvents: function ()
25231     {
25232         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25233
25234         if (this.el.is('input[type=password]') && Roo.isSafari) {
25235             this.el.on('keydown', this.SafariOnKeyDown, this);
25236         }
25237
25238         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25239     },
25240     // private
25241     onRender: function (ct, position)
25242     {
25243         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25244         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25245         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25246
25247         this.trigger.createChild({
25248                    cn: [
25249                     {
25250                     //id: 'PwdMeter',
25251                     tag: 'div',
25252                     cls: 'roo-password-meter-grey col-xs-12',
25253                     style: {
25254                         //width: 0,
25255                         //width: this.meterWidth + 'px'                                                
25256                         }
25257                     },
25258                     {                            
25259                          cls: 'roo-password-meter-text'                          
25260                     }
25261                 ]            
25262         });
25263
25264          
25265         if (this.hideTrigger) {
25266             this.trigger.setDisplayed(false);
25267         }
25268         this.setSize(this.width || '', this.height || '');
25269     },
25270     // private
25271     onDestroy: function ()
25272     {
25273         if (this.trigger) {
25274             this.trigger.removeAllListeners();
25275             this.trigger.remove();
25276         }
25277         if (this.wrap) {
25278             this.wrap.remove();
25279         }
25280         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25281     },
25282     // private
25283     checkStrength: function ()
25284     {
25285         var pwd = this.inputEl().getValue();
25286         if (pwd == this._lastPwd) {
25287             return;
25288         }
25289
25290         var strength;
25291         if (this.ClientSideStrongPassword(pwd)) {
25292             strength = 3;
25293         } else if (this.ClientSideMediumPassword(pwd)) {
25294             strength = 2;
25295         } else if (this.ClientSideWeakPassword(pwd)) {
25296             strength = 1;
25297         } else {
25298             strength = 0;
25299         }
25300         
25301         Roo.log('strength1: ' + strength);
25302         
25303         //var pm = this.trigger.child('div/div/div').dom;
25304         var pm = this.trigger.child('div/div');
25305         pm.removeClass(this.meterClass);
25306         pm.addClass(this.meterClass[strength]);
25307                 
25308         
25309         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25310                 
25311         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25312         
25313         this._lastPwd = pwd;
25314     },
25315     reset: function ()
25316     {
25317         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25318         
25319         this._lastPwd = '';
25320         
25321         var pm = this.trigger.child('div/div');
25322         pm.removeClass(this.meterClass);
25323         pm.addClass('roo-password-meter-grey');        
25324         
25325         
25326         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25327         
25328         pt.innerHTML = '';
25329         this.inputEl().dom.type='password';
25330     },
25331     // private
25332     validateValue: function (value)
25333     {
25334         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25335             return false;
25336         }
25337         if (value.length == 0) {
25338             if (this.allowBlank) {
25339                 this.clearInvalid();
25340                 return true;
25341             }
25342
25343             this.markInvalid(this.errors.PwdEmpty);
25344             this.errorMsg = this.errors.PwdEmpty;
25345             return false;
25346         }
25347         
25348         if(this.insecure){
25349             return true;
25350         }
25351         
25352         if (!value.match(/[\x21-\x7e]+/)) {
25353             this.markInvalid(this.errors.PwdBadChar);
25354             this.errorMsg = this.errors.PwdBadChar;
25355             return false;
25356         }
25357         if (value.length < 6) {
25358             this.markInvalid(this.errors.PwdShort);
25359             this.errorMsg = this.errors.PwdShort;
25360             return false;
25361         }
25362         if (value.length > 16) {
25363             this.markInvalid(this.errors.PwdLong);
25364             this.errorMsg = this.errors.PwdLong;
25365             return false;
25366         }
25367         var strength;
25368         if (this.ClientSideStrongPassword(value)) {
25369             strength = 3;
25370         } else if (this.ClientSideMediumPassword(value)) {
25371             strength = 2;
25372         } else if (this.ClientSideWeakPassword(value)) {
25373             strength = 1;
25374         } else {
25375             strength = 0;
25376         }
25377
25378         
25379         if (strength < 2) {
25380             //this.markInvalid(this.errors.TooWeak);
25381             this.errorMsg = this.errors.TooWeak;
25382             //return false;
25383         }
25384         
25385         
25386         console.log('strength2: ' + strength);
25387         
25388         //var pm = this.trigger.child('div/div/div').dom;
25389         
25390         var pm = this.trigger.child('div/div');
25391         pm.removeClass(this.meterClass);
25392         pm.addClass(this.meterClass[strength]);
25393                 
25394         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25395                 
25396         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25397         
25398         this.errorMsg = ''; 
25399         return true;
25400     },
25401     // private
25402     CharacterSetChecks: function (type)
25403     {
25404         this.type = type;
25405         this.fResult = false;
25406     },
25407     // private
25408     isctype: function (character, type)
25409     {
25410         switch (type) {  
25411             case this.kCapitalLetter:
25412                 if (character >= 'A' && character <= 'Z') {
25413                     return true;
25414                 }
25415                 break;
25416             
25417             case this.kSmallLetter:
25418                 if (character >= 'a' && character <= 'z') {
25419                     return true;
25420                 }
25421                 break;
25422             
25423             case this.kDigit:
25424                 if (character >= '0' && character <= '9') {
25425                     return true;
25426                 }
25427                 break;
25428             
25429             case this.kPunctuation:
25430                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25431                     return true;
25432                 }
25433                 break;
25434             
25435             default:
25436                 return false;
25437         }
25438
25439     },
25440     // private
25441     IsLongEnough: function (pwd, size)
25442     {
25443         return !(pwd == null || isNaN(size) || pwd.length < size);
25444     },
25445     // private
25446     SpansEnoughCharacterSets: function (word, nb)
25447     {
25448         if (!this.IsLongEnough(word, nb))
25449         {
25450             return false;
25451         }
25452
25453         var characterSetChecks = new Array(
25454             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25455             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25456         );
25457         
25458         for (var index = 0; index < word.length; ++index) {
25459             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25460                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25461                     characterSetChecks[nCharSet].fResult = true;
25462                     break;
25463                 }
25464             }
25465         }
25466
25467         var nCharSets = 0;
25468         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25469             if (characterSetChecks[nCharSet].fResult) {
25470                 ++nCharSets;
25471             }
25472         }
25473
25474         if (nCharSets < nb) {
25475             return false;
25476         }
25477         return true;
25478     },
25479     // private
25480     ClientSideStrongPassword: function (pwd)
25481     {
25482         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25483     },
25484     // private
25485     ClientSideMediumPassword: function (pwd)
25486     {
25487         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25488     },
25489     // private
25490     ClientSideWeakPassword: function (pwd)
25491     {
25492         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25493     }
25494           
25495 })//<script type="text/javascript">
25496
25497 /*
25498  * Based  Ext JS Library 1.1.1
25499  * Copyright(c) 2006-2007, Ext JS, LLC.
25500  * LGPL
25501  *
25502  */
25503  
25504 /**
25505  * @class Roo.HtmlEditorCore
25506  * @extends Roo.Component
25507  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25508  *
25509  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25510  */
25511
25512 Roo.HtmlEditorCore = function(config){
25513     
25514     
25515     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25516     
25517     
25518     this.addEvents({
25519         /**
25520          * @event initialize
25521          * Fires when the editor is fully initialized (including the iframe)
25522          * @param {Roo.HtmlEditorCore} this
25523          */
25524         initialize: true,
25525         /**
25526          * @event activate
25527          * Fires when the editor is first receives the focus. Any insertion must wait
25528          * until after this event.
25529          * @param {Roo.HtmlEditorCore} this
25530          */
25531         activate: true,
25532          /**
25533          * @event beforesync
25534          * Fires before the textarea is updated with content from the editor iframe. Return false
25535          * to cancel the sync.
25536          * @param {Roo.HtmlEditorCore} this
25537          * @param {String} html
25538          */
25539         beforesync: true,
25540          /**
25541          * @event beforepush
25542          * Fires before the iframe editor is updated with content from the textarea. Return false
25543          * to cancel the push.
25544          * @param {Roo.HtmlEditorCore} this
25545          * @param {String} html
25546          */
25547         beforepush: true,
25548          /**
25549          * @event sync
25550          * Fires when the textarea is updated with content from the editor iframe.
25551          * @param {Roo.HtmlEditorCore} this
25552          * @param {String} html
25553          */
25554         sync: true,
25555          /**
25556          * @event push
25557          * Fires when the iframe editor is updated with content from the textarea.
25558          * @param {Roo.HtmlEditorCore} this
25559          * @param {String} html
25560          */
25561         push: true,
25562         
25563         /**
25564          * @event editorevent
25565          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25566          * @param {Roo.HtmlEditorCore} this
25567          */
25568         editorevent: true
25569         
25570     });
25571     
25572     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25573     
25574     // defaults : white / black...
25575     this.applyBlacklists();
25576     
25577     
25578     
25579 };
25580
25581
25582 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25583
25584
25585      /**
25586      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25587      */
25588     
25589     owner : false,
25590     
25591      /**
25592      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25593      *                        Roo.resizable.
25594      */
25595     resizable : false,
25596      /**
25597      * @cfg {Number} height (in pixels)
25598      */   
25599     height: 300,
25600    /**
25601      * @cfg {Number} width (in pixels)
25602      */   
25603     width: 500,
25604     
25605     /**
25606      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25607      * 
25608      */
25609     stylesheets: false,
25610     
25611     // id of frame..
25612     frameId: false,
25613     
25614     // private properties
25615     validationEvent : false,
25616     deferHeight: true,
25617     initialized : false,
25618     activated : false,
25619     sourceEditMode : false,
25620     onFocus : Roo.emptyFn,
25621     iframePad:3,
25622     hideMode:'offsets',
25623     
25624     clearUp: true,
25625     
25626     // blacklist + whitelisted elements..
25627     black: false,
25628     white: false,
25629      
25630     bodyCls : '',
25631
25632     /**
25633      * Protected method that will not generally be called directly. It
25634      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25635      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25636      */
25637     getDocMarkup : function(){
25638         // body styles..
25639         var st = '';
25640         
25641         // inherit styels from page...?? 
25642         if (this.stylesheets === false) {
25643             
25644             Roo.get(document.head).select('style').each(function(node) {
25645                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25646             });
25647             
25648             Roo.get(document.head).select('link').each(function(node) { 
25649                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25650             });
25651             
25652         } else if (!this.stylesheets.length) {
25653                 // simple..
25654                 st = '<style type="text/css">' +
25655                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25656                    '</style>';
25657         } else {
25658             for (var i in this.stylesheets) { 
25659                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25660             }
25661             
25662         }
25663         
25664         st +=  '<style type="text/css">' +
25665             'IMG { cursor: pointer } ' +
25666         '</style>';
25667
25668         var cls = 'roo-htmleditor-body';
25669         
25670         if(this.bodyCls.length){
25671             cls += ' ' + this.bodyCls;
25672         }
25673         
25674         return '<html><head>' + st  +
25675             //<style type="text/css">' +
25676             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25677             //'</style>' +
25678             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25679     },
25680
25681     // private
25682     onRender : function(ct, position)
25683     {
25684         var _t = this;
25685         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25686         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25687         
25688         
25689         this.el.dom.style.border = '0 none';
25690         this.el.dom.setAttribute('tabIndex', -1);
25691         this.el.addClass('x-hidden hide');
25692         
25693         
25694         
25695         if(Roo.isIE){ // fix IE 1px bogus margin
25696             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25697         }
25698        
25699         
25700         this.frameId = Roo.id();
25701         
25702          
25703         
25704         var iframe = this.owner.wrap.createChild({
25705             tag: 'iframe',
25706             cls: 'form-control', // bootstrap..
25707             id: this.frameId,
25708             name: this.frameId,
25709             frameBorder : 'no',
25710             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25711         }, this.el
25712         );
25713         
25714         
25715         this.iframe = iframe.dom;
25716
25717          this.assignDocWin();
25718         
25719         this.doc.designMode = 'on';
25720        
25721         this.doc.open();
25722         this.doc.write(this.getDocMarkup());
25723         this.doc.close();
25724
25725         
25726         var task = { // must defer to wait for browser to be ready
25727             run : function(){
25728                 //console.log("run task?" + this.doc.readyState);
25729                 this.assignDocWin();
25730                 if(this.doc.body || this.doc.readyState == 'complete'){
25731                     try {
25732                         this.doc.designMode="on";
25733                     } catch (e) {
25734                         return;
25735                     }
25736                     Roo.TaskMgr.stop(task);
25737                     this.initEditor.defer(10, this);
25738                 }
25739             },
25740             interval : 10,
25741             duration: 10000,
25742             scope: this
25743         };
25744         Roo.TaskMgr.start(task);
25745
25746     },
25747
25748     // private
25749     onResize : function(w, h)
25750     {
25751          Roo.log('resize: ' +w + ',' + h );
25752         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25753         if(!this.iframe){
25754             return;
25755         }
25756         if(typeof w == 'number'){
25757             
25758             this.iframe.style.width = w + 'px';
25759         }
25760         if(typeof h == 'number'){
25761             
25762             this.iframe.style.height = h + 'px';
25763             if(this.doc){
25764                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25765             }
25766         }
25767         
25768     },
25769
25770     /**
25771      * Toggles the editor between standard and source edit mode.
25772      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25773      */
25774     toggleSourceEdit : function(sourceEditMode){
25775         
25776         this.sourceEditMode = sourceEditMode === true;
25777         
25778         if(this.sourceEditMode){
25779  
25780             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25781             
25782         }else{
25783             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25784             //this.iframe.className = '';
25785             this.deferFocus();
25786         }
25787         //this.setSize(this.owner.wrap.getSize());
25788         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25789     },
25790
25791     
25792   
25793
25794     /**
25795      * Protected method that will not generally be called directly. If you need/want
25796      * custom HTML cleanup, this is the method you should override.
25797      * @param {String} html The HTML to be cleaned
25798      * return {String} The cleaned HTML
25799      */
25800     cleanHtml : function(html){
25801         html = String(html);
25802         if(html.length > 5){
25803             if(Roo.isSafari){ // strip safari nonsense
25804                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25805             }
25806         }
25807         if(html == '&nbsp;'){
25808             html = '';
25809         }
25810         return html;
25811     },
25812
25813     /**
25814      * HTML Editor -> Textarea
25815      * Protected method that will not generally be called directly. Syncs the contents
25816      * of the editor iframe with the textarea.
25817      */
25818     syncValue : function(){
25819         if(this.initialized){
25820             var bd = (this.doc.body || this.doc.documentElement);
25821             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25822             var html = bd.innerHTML;
25823             if(Roo.isSafari){
25824                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25825                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25826                 if(m && m[1]){
25827                     html = '<div style="'+m[0]+'">' + html + '</div>';
25828                 }
25829             }
25830             html = this.cleanHtml(html);
25831             // fix up the special chars.. normaly like back quotes in word...
25832             // however we do not want to do this with chinese..
25833             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25834                 
25835                 var cc = match.charCodeAt();
25836
25837                 // Get the character value, handling surrogate pairs
25838                 if (match.length == 2) {
25839                     // It's a surrogate pair, calculate the Unicode code point
25840                     var high = match.charCodeAt(0) - 0xD800;
25841                     var low  = match.charCodeAt(1) - 0xDC00;
25842                     cc = (high * 0x400) + low + 0x10000;
25843                 }  else if (
25844                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25845                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25846                     (cc >= 0xf900 && cc < 0xfb00 )
25847                 ) {
25848                         return match;
25849                 }  
25850          
25851                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25852                 return "&#" + cc + ";";
25853                 
25854                 
25855             });
25856             
25857             
25858              
25859             if(this.owner.fireEvent('beforesync', this, html) !== false){
25860                 this.el.dom.value = html;
25861                 this.owner.fireEvent('sync', this, html);
25862             }
25863         }
25864     },
25865
25866     /**
25867      * Protected method that will not generally be called directly. Pushes the value of the textarea
25868      * into the iframe editor.
25869      */
25870     pushValue : function(){
25871         if(this.initialized){
25872             var v = this.el.dom.value.trim();
25873             
25874 //            if(v.length < 1){
25875 //                v = '&#160;';
25876 //            }
25877             
25878             if(this.owner.fireEvent('beforepush', this, v) !== false){
25879                 var d = (this.doc.body || this.doc.documentElement);
25880                 d.innerHTML = v;
25881                 this.cleanUpPaste();
25882                 this.el.dom.value = d.innerHTML;
25883                 this.owner.fireEvent('push', this, v);
25884             }
25885         }
25886     },
25887
25888     // private
25889     deferFocus : function(){
25890         this.focus.defer(10, this);
25891     },
25892
25893     // doc'ed in Field
25894     focus : function(){
25895         if(this.win && !this.sourceEditMode){
25896             this.win.focus();
25897         }else{
25898             this.el.focus();
25899         }
25900     },
25901     
25902     assignDocWin: function()
25903     {
25904         var iframe = this.iframe;
25905         
25906          if(Roo.isIE){
25907             this.doc = iframe.contentWindow.document;
25908             this.win = iframe.contentWindow;
25909         } else {
25910 //            if (!Roo.get(this.frameId)) {
25911 //                return;
25912 //            }
25913 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25914 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25915             
25916             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25917                 return;
25918             }
25919             
25920             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25921             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25922         }
25923     },
25924     
25925     // private
25926     initEditor : function(){
25927         //console.log("INIT EDITOR");
25928         this.assignDocWin();
25929         
25930         
25931         
25932         this.doc.designMode="on";
25933         this.doc.open();
25934         this.doc.write(this.getDocMarkup());
25935         this.doc.close();
25936         
25937         var dbody = (this.doc.body || this.doc.documentElement);
25938         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25939         // this copies styles from the containing element into thsi one..
25940         // not sure why we need all of this..
25941         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25942         
25943         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25944         //ss['background-attachment'] = 'fixed'; // w3c
25945         dbody.bgProperties = 'fixed'; // ie
25946         //Roo.DomHelper.applyStyles(dbody, ss);
25947         Roo.EventManager.on(this.doc, {
25948             //'mousedown': this.onEditorEvent,
25949             'mouseup': this.onEditorEvent,
25950             'dblclick': this.onEditorEvent,
25951             'click': this.onEditorEvent,
25952             'keyup': this.onEditorEvent,
25953             buffer:100,
25954             scope: this
25955         });
25956         if(Roo.isGecko){
25957             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25958         }
25959         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25960             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25961         }
25962         this.initialized = true;
25963
25964         this.owner.fireEvent('initialize', this);
25965         this.pushValue();
25966     },
25967
25968     // private
25969     onDestroy : function(){
25970         
25971         
25972         
25973         if(this.rendered){
25974             
25975             //for (var i =0; i < this.toolbars.length;i++) {
25976             //    // fixme - ask toolbars for heights?
25977             //    this.toolbars[i].onDestroy();
25978            // }
25979             
25980             //this.wrap.dom.innerHTML = '';
25981             //this.wrap.remove();
25982         }
25983     },
25984
25985     // private
25986     onFirstFocus : function(){
25987         
25988         this.assignDocWin();
25989         
25990         
25991         this.activated = true;
25992          
25993     
25994         if(Roo.isGecko){ // prevent silly gecko errors
25995             this.win.focus();
25996             var s = this.win.getSelection();
25997             if(!s.focusNode || s.focusNode.nodeType != 3){
25998                 var r = s.getRangeAt(0);
25999                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26000                 r.collapse(true);
26001                 this.deferFocus();
26002             }
26003             try{
26004                 this.execCmd('useCSS', true);
26005                 this.execCmd('styleWithCSS', false);
26006             }catch(e){}
26007         }
26008         this.owner.fireEvent('activate', this);
26009     },
26010
26011     // private
26012     adjustFont: function(btn){
26013         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26014         //if(Roo.isSafari){ // safari
26015         //    adjust *= 2;
26016        // }
26017         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26018         if(Roo.isSafari){ // safari
26019             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26020             v =  (v < 10) ? 10 : v;
26021             v =  (v > 48) ? 48 : v;
26022             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26023             
26024         }
26025         
26026         
26027         v = Math.max(1, v+adjust);
26028         
26029         this.execCmd('FontSize', v  );
26030     },
26031
26032     onEditorEvent : function(e)
26033     {
26034         this.owner.fireEvent('editorevent', this, e);
26035       //  this.updateToolbar();
26036         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26037     },
26038
26039     insertTag : function(tg)
26040     {
26041         // could be a bit smarter... -> wrap the current selected tRoo..
26042         if (tg.toLowerCase() == 'span' ||
26043             tg.toLowerCase() == 'code' ||
26044             tg.toLowerCase() == 'sup' ||
26045             tg.toLowerCase() == 'sub' 
26046             ) {
26047             
26048             range = this.createRange(this.getSelection());
26049             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26050             wrappingNode.appendChild(range.extractContents());
26051             range.insertNode(wrappingNode);
26052
26053             return;
26054             
26055             
26056             
26057         }
26058         this.execCmd("formatblock",   tg);
26059         
26060     },
26061     
26062     insertText : function(txt)
26063     {
26064         
26065         
26066         var range = this.createRange();
26067         range.deleteContents();
26068                //alert(Sender.getAttribute('label'));
26069                
26070         range.insertNode(this.doc.createTextNode(txt));
26071     } ,
26072     
26073      
26074
26075     /**
26076      * Executes a Midas editor command on the editor document and performs necessary focus and
26077      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26078      * @param {String} cmd The Midas command
26079      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26080      */
26081     relayCmd : function(cmd, value){
26082         this.win.focus();
26083         this.execCmd(cmd, value);
26084         this.owner.fireEvent('editorevent', this);
26085         //this.updateToolbar();
26086         this.owner.deferFocus();
26087     },
26088
26089     /**
26090      * Executes a Midas editor command directly on the editor document.
26091      * For visual commands, you should use {@link #relayCmd} instead.
26092      * <b>This should only be called after the editor is initialized.</b>
26093      * @param {String} cmd The Midas command
26094      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26095      */
26096     execCmd : function(cmd, value){
26097         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26098         this.syncValue();
26099     },
26100  
26101  
26102    
26103     /**
26104      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26105      * to insert tRoo.
26106      * @param {String} text | dom node.. 
26107      */
26108     insertAtCursor : function(text)
26109     {
26110         
26111         if(!this.activated){
26112             return;
26113         }
26114         /*
26115         if(Roo.isIE){
26116             this.win.focus();
26117             var r = this.doc.selection.createRange();
26118             if(r){
26119                 r.collapse(true);
26120                 r.pasteHTML(text);
26121                 this.syncValue();
26122                 this.deferFocus();
26123             
26124             }
26125             return;
26126         }
26127         */
26128         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26129             this.win.focus();
26130             
26131             
26132             // from jquery ui (MIT licenced)
26133             var range, node;
26134             var win = this.win;
26135             
26136             if (win.getSelection && win.getSelection().getRangeAt) {
26137                 range = win.getSelection().getRangeAt(0);
26138                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26139                 range.insertNode(node);
26140             } else if (win.document.selection && win.document.selection.createRange) {
26141                 // no firefox support
26142                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26143                 win.document.selection.createRange().pasteHTML(txt);
26144             } else {
26145                 // no firefox support
26146                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26147                 this.execCmd('InsertHTML', txt);
26148             } 
26149             
26150             this.syncValue();
26151             
26152             this.deferFocus();
26153         }
26154     },
26155  // private
26156     mozKeyPress : function(e){
26157         if(e.ctrlKey){
26158             var c = e.getCharCode(), cmd;
26159           
26160             if(c > 0){
26161                 c = String.fromCharCode(c).toLowerCase();
26162                 switch(c){
26163                     case 'b':
26164                         cmd = 'bold';
26165                         break;
26166                     case 'i':
26167                         cmd = 'italic';
26168                         break;
26169                     
26170                     case 'u':
26171                         cmd = 'underline';
26172                         break;
26173                     
26174                     case 'v':
26175                         this.cleanUpPaste.defer(100, this);
26176                         return;
26177                         
26178                 }
26179                 if(cmd){
26180                     this.win.focus();
26181                     this.execCmd(cmd);
26182                     this.deferFocus();
26183                     e.preventDefault();
26184                 }
26185                 
26186             }
26187         }
26188     },
26189
26190     // private
26191     fixKeys : function(){ // load time branching for fastest keydown performance
26192         if(Roo.isIE){
26193             return function(e){
26194                 var k = e.getKey(), r;
26195                 if(k == e.TAB){
26196                     e.stopEvent();
26197                     r = this.doc.selection.createRange();
26198                     if(r){
26199                         r.collapse(true);
26200                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26201                         this.deferFocus();
26202                     }
26203                     return;
26204                 }
26205                 
26206                 if(k == e.ENTER){
26207                     r = this.doc.selection.createRange();
26208                     if(r){
26209                         var target = r.parentElement();
26210                         if(!target || target.tagName.toLowerCase() != 'li'){
26211                             e.stopEvent();
26212                             r.pasteHTML('<br />');
26213                             r.collapse(false);
26214                             r.select();
26215                         }
26216                     }
26217                 }
26218                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26219                     this.cleanUpPaste.defer(100, this);
26220                     return;
26221                 }
26222                 
26223                 
26224             };
26225         }else if(Roo.isOpera){
26226             return function(e){
26227                 var k = e.getKey();
26228                 if(k == e.TAB){
26229                     e.stopEvent();
26230                     this.win.focus();
26231                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26232                     this.deferFocus();
26233                 }
26234                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26235                     this.cleanUpPaste.defer(100, this);
26236                     return;
26237                 }
26238                 
26239             };
26240         }else if(Roo.isSafari){
26241             return function(e){
26242                 var k = e.getKey();
26243                 
26244                 if(k == e.TAB){
26245                     e.stopEvent();
26246                     this.execCmd('InsertText','\t');
26247                     this.deferFocus();
26248                     return;
26249                 }
26250                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26251                     this.cleanUpPaste.defer(100, this);
26252                     return;
26253                 }
26254                 
26255              };
26256         }
26257     }(),
26258     
26259     getAllAncestors: function()
26260     {
26261         var p = this.getSelectedNode();
26262         var a = [];
26263         if (!p) {
26264             a.push(p); // push blank onto stack..
26265             p = this.getParentElement();
26266         }
26267         
26268         
26269         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26270             a.push(p);
26271             p = p.parentNode;
26272         }
26273         a.push(this.doc.body);
26274         return a;
26275     },
26276     lastSel : false,
26277     lastSelNode : false,
26278     
26279     
26280     getSelection : function() 
26281     {
26282         this.assignDocWin();
26283         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26284     },
26285     
26286     getSelectedNode: function() 
26287     {
26288         // this may only work on Gecko!!!
26289         
26290         // should we cache this!!!!
26291         
26292         
26293         
26294          
26295         var range = this.createRange(this.getSelection()).cloneRange();
26296         
26297         if (Roo.isIE) {
26298             var parent = range.parentElement();
26299             while (true) {
26300                 var testRange = range.duplicate();
26301                 testRange.moveToElementText(parent);
26302                 if (testRange.inRange(range)) {
26303                     break;
26304                 }
26305                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26306                     break;
26307                 }
26308                 parent = parent.parentElement;
26309             }
26310             return parent;
26311         }
26312         
26313         // is ancestor a text element.
26314         var ac =  range.commonAncestorContainer;
26315         if (ac.nodeType == 3) {
26316             ac = ac.parentNode;
26317         }
26318         
26319         var ar = ac.childNodes;
26320          
26321         var nodes = [];
26322         var other_nodes = [];
26323         var has_other_nodes = false;
26324         for (var i=0;i<ar.length;i++) {
26325             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26326                 continue;
26327             }
26328             // fullly contained node.
26329             
26330             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26331                 nodes.push(ar[i]);
26332                 continue;
26333             }
26334             
26335             // probably selected..
26336             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26337                 other_nodes.push(ar[i]);
26338                 continue;
26339             }
26340             // outer..
26341             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26342                 continue;
26343             }
26344             
26345             
26346             has_other_nodes = true;
26347         }
26348         if (!nodes.length && other_nodes.length) {
26349             nodes= other_nodes;
26350         }
26351         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26352             return false;
26353         }
26354         
26355         return nodes[0];
26356     },
26357     createRange: function(sel)
26358     {
26359         // this has strange effects when using with 
26360         // top toolbar - not sure if it's a great idea.
26361         //this.editor.contentWindow.focus();
26362         if (typeof sel != "undefined") {
26363             try {
26364                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26365             } catch(e) {
26366                 return this.doc.createRange();
26367             }
26368         } else {
26369             return this.doc.createRange();
26370         }
26371     },
26372     getParentElement: function()
26373     {
26374         
26375         this.assignDocWin();
26376         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26377         
26378         var range = this.createRange(sel);
26379          
26380         try {
26381             var p = range.commonAncestorContainer;
26382             while (p.nodeType == 3) { // text node
26383                 p = p.parentNode;
26384             }
26385             return p;
26386         } catch (e) {
26387             return null;
26388         }
26389     
26390     },
26391     /***
26392      *
26393      * Range intersection.. the hard stuff...
26394      *  '-1' = before
26395      *  '0' = hits..
26396      *  '1' = after.
26397      *         [ -- selected range --- ]
26398      *   [fail]                        [fail]
26399      *
26400      *    basically..
26401      *      if end is before start or  hits it. fail.
26402      *      if start is after end or hits it fail.
26403      *
26404      *   if either hits (but other is outside. - then it's not 
26405      *   
26406      *    
26407      **/
26408     
26409     
26410     // @see http://www.thismuchiknow.co.uk/?p=64.
26411     rangeIntersectsNode : function(range, node)
26412     {
26413         var nodeRange = node.ownerDocument.createRange();
26414         try {
26415             nodeRange.selectNode(node);
26416         } catch (e) {
26417             nodeRange.selectNodeContents(node);
26418         }
26419     
26420         var rangeStartRange = range.cloneRange();
26421         rangeStartRange.collapse(true);
26422     
26423         var rangeEndRange = range.cloneRange();
26424         rangeEndRange.collapse(false);
26425     
26426         var nodeStartRange = nodeRange.cloneRange();
26427         nodeStartRange.collapse(true);
26428     
26429         var nodeEndRange = nodeRange.cloneRange();
26430         nodeEndRange.collapse(false);
26431     
26432         return rangeStartRange.compareBoundaryPoints(
26433                  Range.START_TO_START, nodeEndRange) == -1 &&
26434                rangeEndRange.compareBoundaryPoints(
26435                  Range.START_TO_START, nodeStartRange) == 1;
26436         
26437          
26438     },
26439     rangeCompareNode : function(range, node)
26440     {
26441         var nodeRange = node.ownerDocument.createRange();
26442         try {
26443             nodeRange.selectNode(node);
26444         } catch (e) {
26445             nodeRange.selectNodeContents(node);
26446         }
26447         
26448         
26449         range.collapse(true);
26450     
26451         nodeRange.collapse(true);
26452      
26453         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26454         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26455          
26456         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26457         
26458         var nodeIsBefore   =  ss == 1;
26459         var nodeIsAfter    = ee == -1;
26460         
26461         if (nodeIsBefore && nodeIsAfter) {
26462             return 0; // outer
26463         }
26464         if (!nodeIsBefore && nodeIsAfter) {
26465             return 1; //right trailed.
26466         }
26467         
26468         if (nodeIsBefore && !nodeIsAfter) {
26469             return 2;  // left trailed.
26470         }
26471         // fully contined.
26472         return 3;
26473     },
26474
26475     // private? - in a new class?
26476     cleanUpPaste :  function()
26477     {
26478         // cleans up the whole document..
26479         Roo.log('cleanuppaste');
26480         
26481         this.cleanUpChildren(this.doc.body);
26482         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26483         if (clean != this.doc.body.innerHTML) {
26484             this.doc.body.innerHTML = clean;
26485         }
26486         
26487     },
26488     
26489     cleanWordChars : function(input) {// change the chars to hex code
26490         var he = Roo.HtmlEditorCore;
26491         
26492         var output = input;
26493         Roo.each(he.swapCodes, function(sw) { 
26494             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26495             
26496             output = output.replace(swapper, sw[1]);
26497         });
26498         
26499         return output;
26500     },
26501     
26502     
26503     cleanUpChildren : function (n)
26504     {
26505         if (!n.childNodes.length) {
26506             return;
26507         }
26508         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26509            this.cleanUpChild(n.childNodes[i]);
26510         }
26511     },
26512     
26513     
26514         
26515     
26516     cleanUpChild : function (node)
26517     {
26518         var ed = this;
26519         //console.log(node);
26520         if (node.nodeName == "#text") {
26521             // clean up silly Windows -- stuff?
26522             return; 
26523         }
26524         if (node.nodeName == "#comment") {
26525             node.parentNode.removeChild(node);
26526             // clean up silly Windows -- stuff?
26527             return; 
26528         }
26529         var lcname = node.tagName.toLowerCase();
26530         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26531         // whitelist of tags..
26532         
26533         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26534             // remove node.
26535             node.parentNode.removeChild(node);
26536             return;
26537             
26538         }
26539         
26540         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26541         
26542         // spans with no attributes - just remove them..
26543         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26544             remove_keep_children = true;
26545         }
26546         
26547         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26548         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26549         
26550         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26551         //    remove_keep_children = true;
26552         //}
26553         
26554         if (remove_keep_children) {
26555             this.cleanUpChildren(node);
26556             // inserts everything just before this node...
26557             while (node.childNodes.length) {
26558                 var cn = node.childNodes[0];
26559                 node.removeChild(cn);
26560                 node.parentNode.insertBefore(cn, node);
26561             }
26562             node.parentNode.removeChild(node);
26563             return;
26564         }
26565         
26566         if (!node.attributes || !node.attributes.length) {
26567             
26568           
26569             
26570             
26571             this.cleanUpChildren(node);
26572             return;
26573         }
26574         
26575         function cleanAttr(n,v)
26576         {
26577             
26578             if (v.match(/^\./) || v.match(/^\//)) {
26579                 return;
26580             }
26581             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26582                 return;
26583             }
26584             if (v.match(/^#/)) {
26585                 return;
26586             }
26587             if (v.match(/^\{/)) { // allow template editing.
26588                 return;
26589             }
26590 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26591             node.removeAttribute(n);
26592             
26593         }
26594         
26595         var cwhite = this.cwhite;
26596         var cblack = this.cblack;
26597             
26598         function cleanStyle(n,v)
26599         {
26600             if (v.match(/expression/)) { //XSS?? should we even bother..
26601                 node.removeAttribute(n);
26602                 return;
26603             }
26604             
26605             var parts = v.split(/;/);
26606             var clean = [];
26607             
26608             Roo.each(parts, function(p) {
26609                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26610                 if (!p.length) {
26611                     return true;
26612                 }
26613                 var l = p.split(':').shift().replace(/\s+/g,'');
26614                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26615                 
26616                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26617 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26618                     //node.removeAttribute(n);
26619                     return true;
26620                 }
26621                 //Roo.log()
26622                 // only allow 'c whitelisted system attributes'
26623                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26624 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26625                     //node.removeAttribute(n);
26626                     return true;
26627                 }
26628                 
26629                 
26630                  
26631                 
26632                 clean.push(p);
26633                 return true;
26634             });
26635             if (clean.length) { 
26636                 node.setAttribute(n, clean.join(';'));
26637             } else {
26638                 node.removeAttribute(n);
26639             }
26640             
26641         }
26642         
26643         
26644         for (var i = node.attributes.length-1; i > -1 ; i--) {
26645             var a = node.attributes[i];
26646             //console.log(a);
26647             
26648             if (a.name.toLowerCase().substr(0,2)=='on')  {
26649                 node.removeAttribute(a.name);
26650                 continue;
26651             }
26652             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26653                 node.removeAttribute(a.name);
26654                 continue;
26655             }
26656             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26657                 cleanAttr(a.name,a.value); // fixme..
26658                 continue;
26659             }
26660             if (a.name == 'style') {
26661                 cleanStyle(a.name,a.value);
26662                 continue;
26663             }
26664             /// clean up MS crap..
26665             // tecnically this should be a list of valid class'es..
26666             
26667             
26668             if (a.name == 'class') {
26669                 if (a.value.match(/^Mso/)) {
26670                     node.removeAttribute('class');
26671                 }
26672                 
26673                 if (a.value.match(/^body$/)) {
26674                     node.removeAttribute('class');
26675                 }
26676                 continue;
26677             }
26678             
26679             // style cleanup!?
26680             // class cleanup?
26681             
26682         }
26683         
26684         
26685         this.cleanUpChildren(node);
26686         
26687         
26688     },
26689     
26690     /**
26691      * Clean up MS wordisms...
26692      */
26693     cleanWord : function(node)
26694     {
26695         if (!node) {
26696             this.cleanWord(this.doc.body);
26697             return;
26698         }
26699         
26700         if(
26701                 node.nodeName == 'SPAN' &&
26702                 !node.hasAttributes() &&
26703                 node.childNodes.length == 1 &&
26704                 node.firstChild.nodeName == "#text"  
26705         ) {
26706             var textNode = node.firstChild;
26707             node.removeChild(textNode);
26708             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26709                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26710             }
26711             node.parentNode.insertBefore(textNode, node);
26712             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26713                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26714             }
26715             node.parentNode.removeChild(node);
26716         }
26717         
26718         if (node.nodeName == "#text") {
26719             // clean up silly Windows -- stuff?
26720             return; 
26721         }
26722         if (node.nodeName == "#comment") {
26723             node.parentNode.removeChild(node);
26724             // clean up silly Windows -- stuff?
26725             return; 
26726         }
26727         
26728         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26729             node.parentNode.removeChild(node);
26730             return;
26731         }
26732         //Roo.log(node.tagName);
26733         // remove - but keep children..
26734         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26735             //Roo.log('-- removed');
26736             while (node.childNodes.length) {
26737                 var cn = node.childNodes[0];
26738                 node.removeChild(cn);
26739                 node.parentNode.insertBefore(cn, node);
26740                 // move node to parent - and clean it..
26741                 this.cleanWord(cn);
26742             }
26743             node.parentNode.removeChild(node);
26744             /// no need to iterate chidlren = it's got none..
26745             //this.iterateChildren(node, this.cleanWord);
26746             return;
26747         }
26748         // clean styles
26749         if (node.className.length) {
26750             
26751             var cn = node.className.split(/\W+/);
26752             var cna = [];
26753             Roo.each(cn, function(cls) {
26754                 if (cls.match(/Mso[a-zA-Z]+/)) {
26755                     return;
26756                 }
26757                 cna.push(cls);
26758             });
26759             node.className = cna.length ? cna.join(' ') : '';
26760             if (!cna.length) {
26761                 node.removeAttribute("class");
26762             }
26763         }
26764         
26765         if (node.hasAttribute("lang")) {
26766             node.removeAttribute("lang");
26767         }
26768         
26769         if (node.hasAttribute("style")) {
26770             
26771             var styles = node.getAttribute("style").split(";");
26772             var nstyle = [];
26773             Roo.each(styles, function(s) {
26774                 if (!s.match(/:/)) {
26775                     return;
26776                 }
26777                 var kv = s.split(":");
26778                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26779                     return;
26780                 }
26781                 // what ever is left... we allow.
26782                 nstyle.push(s);
26783             });
26784             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26785             if (!nstyle.length) {
26786                 node.removeAttribute('style');
26787             }
26788         }
26789         this.iterateChildren(node, this.cleanWord);
26790         
26791         
26792         
26793     },
26794     /**
26795      * iterateChildren of a Node, calling fn each time, using this as the scole..
26796      * @param {DomNode} node node to iterate children of.
26797      * @param {Function} fn method of this class to call on each item.
26798      */
26799     iterateChildren : function(node, fn)
26800     {
26801         if (!node.childNodes.length) {
26802                 return;
26803         }
26804         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26805            fn.call(this, node.childNodes[i])
26806         }
26807     },
26808     
26809     
26810     /**
26811      * cleanTableWidths.
26812      *
26813      * Quite often pasting from word etc.. results in tables with column and widths.
26814      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26815      *
26816      */
26817     cleanTableWidths : function(node)
26818     {
26819          
26820          
26821         if (!node) {
26822             this.cleanTableWidths(this.doc.body);
26823             return;
26824         }
26825         
26826         // ignore list...
26827         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26828             return; 
26829         }
26830         Roo.log(node.tagName);
26831         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26832             this.iterateChildren(node, this.cleanTableWidths);
26833             return;
26834         }
26835         if (node.hasAttribute('width')) {
26836             node.removeAttribute('width');
26837         }
26838         
26839          
26840         if (node.hasAttribute("style")) {
26841             // pretty basic...
26842             
26843             var styles = node.getAttribute("style").split(";");
26844             var nstyle = [];
26845             Roo.each(styles, function(s) {
26846                 if (!s.match(/:/)) {
26847                     return;
26848                 }
26849                 var kv = s.split(":");
26850                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26851                     return;
26852                 }
26853                 // what ever is left... we allow.
26854                 nstyle.push(s);
26855             });
26856             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26857             if (!nstyle.length) {
26858                 node.removeAttribute('style');
26859             }
26860         }
26861         
26862         this.iterateChildren(node, this.cleanTableWidths);
26863         
26864         
26865     },
26866     
26867     
26868     
26869     
26870     domToHTML : function(currentElement, depth, nopadtext) {
26871         
26872         depth = depth || 0;
26873         nopadtext = nopadtext || false;
26874     
26875         if (!currentElement) {
26876             return this.domToHTML(this.doc.body);
26877         }
26878         
26879         //Roo.log(currentElement);
26880         var j;
26881         var allText = false;
26882         var nodeName = currentElement.nodeName;
26883         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26884         
26885         if  (nodeName == '#text') {
26886             
26887             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26888         }
26889         
26890         
26891         var ret = '';
26892         if (nodeName != 'BODY') {
26893              
26894             var i = 0;
26895             // Prints the node tagName, such as <A>, <IMG>, etc
26896             if (tagName) {
26897                 var attr = [];
26898                 for(i = 0; i < currentElement.attributes.length;i++) {
26899                     // quoting?
26900                     var aname = currentElement.attributes.item(i).name;
26901                     if (!currentElement.attributes.item(i).value.length) {
26902                         continue;
26903                     }
26904                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26905                 }
26906                 
26907                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26908             } 
26909             else {
26910                 
26911                 // eack
26912             }
26913         } else {
26914             tagName = false;
26915         }
26916         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26917             return ret;
26918         }
26919         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26920             nopadtext = true;
26921         }
26922         
26923         
26924         // Traverse the tree
26925         i = 0;
26926         var currentElementChild = currentElement.childNodes.item(i);
26927         var allText = true;
26928         var innerHTML  = '';
26929         lastnode = '';
26930         while (currentElementChild) {
26931             // Formatting code (indent the tree so it looks nice on the screen)
26932             var nopad = nopadtext;
26933             if (lastnode == 'SPAN') {
26934                 nopad  = true;
26935             }
26936             // text
26937             if  (currentElementChild.nodeName == '#text') {
26938                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26939                 toadd = nopadtext ? toadd : toadd.trim();
26940                 if (!nopad && toadd.length > 80) {
26941                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26942                 }
26943                 innerHTML  += toadd;
26944                 
26945                 i++;
26946                 currentElementChild = currentElement.childNodes.item(i);
26947                 lastNode = '';
26948                 continue;
26949             }
26950             allText = false;
26951             
26952             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26953                 
26954             // Recursively traverse the tree structure of the child node
26955             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26956             lastnode = currentElementChild.nodeName;
26957             i++;
26958             currentElementChild=currentElement.childNodes.item(i);
26959         }
26960         
26961         ret += innerHTML;
26962         
26963         if (!allText) {
26964                 // The remaining code is mostly for formatting the tree
26965             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26966         }
26967         
26968         
26969         if (tagName) {
26970             ret+= "</"+tagName+">";
26971         }
26972         return ret;
26973         
26974     },
26975         
26976     applyBlacklists : function()
26977     {
26978         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26979         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26980         
26981         this.white = [];
26982         this.black = [];
26983         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26984             if (b.indexOf(tag) > -1) {
26985                 return;
26986             }
26987             this.white.push(tag);
26988             
26989         }, this);
26990         
26991         Roo.each(w, function(tag) {
26992             if (b.indexOf(tag) > -1) {
26993                 return;
26994             }
26995             if (this.white.indexOf(tag) > -1) {
26996                 return;
26997             }
26998             this.white.push(tag);
26999             
27000         }, this);
27001         
27002         
27003         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27004             if (w.indexOf(tag) > -1) {
27005                 return;
27006             }
27007             this.black.push(tag);
27008             
27009         }, this);
27010         
27011         Roo.each(b, function(tag) {
27012             if (w.indexOf(tag) > -1) {
27013                 return;
27014             }
27015             if (this.black.indexOf(tag) > -1) {
27016                 return;
27017             }
27018             this.black.push(tag);
27019             
27020         }, this);
27021         
27022         
27023         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27024         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27025         
27026         this.cwhite = [];
27027         this.cblack = [];
27028         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27029             if (b.indexOf(tag) > -1) {
27030                 return;
27031             }
27032             this.cwhite.push(tag);
27033             
27034         }, this);
27035         
27036         Roo.each(w, function(tag) {
27037             if (b.indexOf(tag) > -1) {
27038                 return;
27039             }
27040             if (this.cwhite.indexOf(tag) > -1) {
27041                 return;
27042             }
27043             this.cwhite.push(tag);
27044             
27045         }, this);
27046         
27047         
27048         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27049             if (w.indexOf(tag) > -1) {
27050                 return;
27051             }
27052             this.cblack.push(tag);
27053             
27054         }, this);
27055         
27056         Roo.each(b, function(tag) {
27057             if (w.indexOf(tag) > -1) {
27058                 return;
27059             }
27060             if (this.cblack.indexOf(tag) > -1) {
27061                 return;
27062             }
27063             this.cblack.push(tag);
27064             
27065         }, this);
27066     },
27067     
27068     setStylesheets : function(stylesheets)
27069     {
27070         if(typeof(stylesheets) == 'string'){
27071             Roo.get(this.iframe.contentDocument.head).createChild({
27072                 tag : 'link',
27073                 rel : 'stylesheet',
27074                 type : 'text/css',
27075                 href : stylesheets
27076             });
27077             
27078             return;
27079         }
27080         var _this = this;
27081      
27082         Roo.each(stylesheets, function(s) {
27083             if(!s.length){
27084                 return;
27085             }
27086             
27087             Roo.get(_this.iframe.contentDocument.head).createChild({
27088                 tag : 'link',
27089                 rel : 'stylesheet',
27090                 type : 'text/css',
27091                 href : s
27092             });
27093         });
27094
27095         
27096     },
27097     
27098     removeStylesheets : function()
27099     {
27100         var _this = this;
27101         
27102         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27103             s.remove();
27104         });
27105     },
27106     
27107     setStyle : function(style)
27108     {
27109         Roo.get(this.iframe.contentDocument.head).createChild({
27110             tag : 'style',
27111             type : 'text/css',
27112             html : style
27113         });
27114
27115         return;
27116     }
27117     
27118     // hide stuff that is not compatible
27119     /**
27120      * @event blur
27121      * @hide
27122      */
27123     /**
27124      * @event change
27125      * @hide
27126      */
27127     /**
27128      * @event focus
27129      * @hide
27130      */
27131     /**
27132      * @event specialkey
27133      * @hide
27134      */
27135     /**
27136      * @cfg {String} fieldClass @hide
27137      */
27138     /**
27139      * @cfg {String} focusClass @hide
27140      */
27141     /**
27142      * @cfg {String} autoCreate @hide
27143      */
27144     /**
27145      * @cfg {String} inputType @hide
27146      */
27147     /**
27148      * @cfg {String} invalidClass @hide
27149      */
27150     /**
27151      * @cfg {String} invalidText @hide
27152      */
27153     /**
27154      * @cfg {String} msgFx @hide
27155      */
27156     /**
27157      * @cfg {String} validateOnBlur @hide
27158      */
27159 });
27160
27161 Roo.HtmlEditorCore.white = [
27162         'area', 'br', 'img', 'input', 'hr', 'wbr',
27163         
27164        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27165        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27166        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27167        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27168        'table',   'ul',         'xmp', 
27169        
27170        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27171       'thead',   'tr', 
27172      
27173       'dir', 'menu', 'ol', 'ul', 'dl',
27174        
27175       'embed',  'object'
27176 ];
27177
27178
27179 Roo.HtmlEditorCore.black = [
27180     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27181         'applet', // 
27182         'base',   'basefont', 'bgsound', 'blink',  'body', 
27183         'frame',  'frameset', 'head',    'html',   'ilayer', 
27184         'iframe', 'layer',  'link',     'meta',    'object',   
27185         'script', 'style' ,'title',  'xml' // clean later..
27186 ];
27187 Roo.HtmlEditorCore.clean = [
27188     'script', 'style', 'title', 'xml'
27189 ];
27190 Roo.HtmlEditorCore.remove = [
27191     'font'
27192 ];
27193 // attributes..
27194
27195 Roo.HtmlEditorCore.ablack = [
27196     'on'
27197 ];
27198     
27199 Roo.HtmlEditorCore.aclean = [ 
27200     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27201 ];
27202
27203 // protocols..
27204 Roo.HtmlEditorCore.pwhite= [
27205         'http',  'https',  'mailto'
27206 ];
27207
27208 // white listed style attributes.
27209 Roo.HtmlEditorCore.cwhite= [
27210       //  'text-align', /// default is to allow most things..
27211       
27212          
27213 //        'font-size'//??
27214 ];
27215
27216 // black listed style attributes.
27217 Roo.HtmlEditorCore.cblack= [
27218       //  'font-size' -- this can be set by the project 
27219 ];
27220
27221
27222 Roo.HtmlEditorCore.swapCodes   =[ 
27223     [    8211, "&#8211;" ], 
27224     [    8212, "&#8212;" ], 
27225     [    8216,  "'" ],  
27226     [    8217, "'" ],  
27227     [    8220, '"' ],  
27228     [    8221, '"' ],  
27229     [    8226, "*" ],  
27230     [    8230, "..." ]
27231 ]; 
27232
27233     /*
27234  * - LGPL
27235  *
27236  * HtmlEditor
27237  * 
27238  */
27239
27240 /**
27241  * @class Roo.bootstrap.HtmlEditor
27242  * @extends Roo.bootstrap.TextArea
27243  * Bootstrap HtmlEditor class
27244
27245  * @constructor
27246  * Create a new HtmlEditor
27247  * @param {Object} config The config object
27248  */
27249
27250 Roo.bootstrap.HtmlEditor = function(config){
27251     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27252     if (!this.toolbars) {
27253         this.toolbars = [];
27254     }
27255     
27256     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27257     this.addEvents({
27258             /**
27259              * @event initialize
27260              * Fires when the editor is fully initialized (including the iframe)
27261              * @param {HtmlEditor} this
27262              */
27263             initialize: true,
27264             /**
27265              * @event activate
27266              * Fires when the editor is first receives the focus. Any insertion must wait
27267              * until after this event.
27268              * @param {HtmlEditor} this
27269              */
27270             activate: true,
27271              /**
27272              * @event beforesync
27273              * Fires before the textarea is updated with content from the editor iframe. Return false
27274              * to cancel the sync.
27275              * @param {HtmlEditor} this
27276              * @param {String} html
27277              */
27278             beforesync: true,
27279              /**
27280              * @event beforepush
27281              * Fires before the iframe editor is updated with content from the textarea. Return false
27282              * to cancel the push.
27283              * @param {HtmlEditor} this
27284              * @param {String} html
27285              */
27286             beforepush: true,
27287              /**
27288              * @event sync
27289              * Fires when the textarea is updated with content from the editor iframe.
27290              * @param {HtmlEditor} this
27291              * @param {String} html
27292              */
27293             sync: true,
27294              /**
27295              * @event push
27296              * Fires when the iframe editor is updated with content from the textarea.
27297              * @param {HtmlEditor} this
27298              * @param {String} html
27299              */
27300             push: true,
27301              /**
27302              * @event editmodechange
27303              * Fires when the editor switches edit modes
27304              * @param {HtmlEditor} this
27305              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27306              */
27307             editmodechange: true,
27308             /**
27309              * @event editorevent
27310              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27311              * @param {HtmlEditor} this
27312              */
27313             editorevent: true,
27314             /**
27315              * @event firstfocus
27316              * Fires when on first focus - needed by toolbars..
27317              * @param {HtmlEditor} this
27318              */
27319             firstfocus: true,
27320             /**
27321              * @event autosave
27322              * Auto save the htmlEditor value as a file into Events
27323              * @param {HtmlEditor} this
27324              */
27325             autosave: true,
27326             /**
27327              * @event savedpreview
27328              * preview the saved version of htmlEditor
27329              * @param {HtmlEditor} this
27330              */
27331             savedpreview: true
27332         });
27333 };
27334
27335
27336 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27337     
27338     
27339       /**
27340      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27341      */
27342     toolbars : false,
27343     
27344      /**
27345     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27346     */
27347     btns : [],
27348    
27349      /**
27350      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27351      *                        Roo.resizable.
27352      */
27353     resizable : false,
27354      /**
27355      * @cfg {Number} height (in pixels)
27356      */   
27357     height: 300,
27358    /**
27359      * @cfg {Number} width (in pixels)
27360      */   
27361     width: false,
27362     
27363     /**
27364      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27365      * 
27366      */
27367     stylesheets: false,
27368     
27369     // id of frame..
27370     frameId: false,
27371     
27372     // private properties
27373     validationEvent : false,
27374     deferHeight: true,
27375     initialized : false,
27376     activated : false,
27377     
27378     onFocus : Roo.emptyFn,
27379     iframePad:3,
27380     hideMode:'offsets',
27381     
27382     tbContainer : false,
27383     
27384     bodyCls : '',
27385     
27386     toolbarContainer :function() {
27387         return this.wrap.select('.x-html-editor-tb',true).first();
27388     },
27389
27390     /**
27391      * Protected method that will not generally be called directly. It
27392      * is called when the editor creates its toolbar. Override this method if you need to
27393      * add custom toolbar buttons.
27394      * @param {HtmlEditor} editor
27395      */
27396     createToolbar : function(){
27397         Roo.log('renewing');
27398         Roo.log("create toolbars");
27399         
27400         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27401         this.toolbars[0].render(this.toolbarContainer());
27402         
27403         return;
27404         
27405 //        if (!editor.toolbars || !editor.toolbars.length) {
27406 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27407 //        }
27408 //        
27409 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27410 //            editor.toolbars[i] = Roo.factory(
27411 //                    typeof(editor.toolbars[i]) == 'string' ?
27412 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27413 //                Roo.bootstrap.HtmlEditor);
27414 //            editor.toolbars[i].init(editor);
27415 //        }
27416     },
27417
27418      
27419     // private
27420     onRender : function(ct, position)
27421     {
27422        // Roo.log("Call onRender: " + this.xtype);
27423         var _t = this;
27424         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27425       
27426         this.wrap = this.inputEl().wrap({
27427             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27428         });
27429         
27430         this.editorcore.onRender(ct, position);
27431          
27432         if (this.resizable) {
27433             this.resizeEl = new Roo.Resizable(this.wrap, {
27434                 pinned : true,
27435                 wrap: true,
27436                 dynamic : true,
27437                 minHeight : this.height,
27438                 height: this.height,
27439                 handles : this.resizable,
27440                 width: this.width,
27441                 listeners : {
27442                     resize : function(r, w, h) {
27443                         _t.onResize(w,h); // -something
27444                     }
27445                 }
27446             });
27447             
27448         }
27449         this.createToolbar(this);
27450        
27451         
27452         if(!this.width && this.resizable){
27453             this.setSize(this.wrap.getSize());
27454         }
27455         if (this.resizeEl) {
27456             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27457             // should trigger onReize..
27458         }
27459         
27460     },
27461
27462     // private
27463     onResize : function(w, h)
27464     {
27465         Roo.log('resize: ' +w + ',' + h );
27466         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27467         var ew = false;
27468         var eh = false;
27469         
27470         if(this.inputEl() ){
27471             if(typeof w == 'number'){
27472                 var aw = w - this.wrap.getFrameWidth('lr');
27473                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27474                 ew = aw;
27475             }
27476             if(typeof h == 'number'){
27477                  var tbh = -11;  // fixme it needs to tool bar size!
27478                 for (var i =0; i < this.toolbars.length;i++) {
27479                     // fixme - ask toolbars for heights?
27480                     tbh += this.toolbars[i].el.getHeight();
27481                     //if (this.toolbars[i].footer) {
27482                     //    tbh += this.toolbars[i].footer.el.getHeight();
27483                     //}
27484                 }
27485               
27486                 
27487                 
27488                 
27489                 
27490                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27491                 ah -= 5; // knock a few pixes off for look..
27492                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27493                 var eh = ah;
27494             }
27495         }
27496         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27497         this.editorcore.onResize(ew,eh);
27498         
27499     },
27500
27501     /**
27502      * Toggles the editor between standard and source edit mode.
27503      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27504      */
27505     toggleSourceEdit : function(sourceEditMode)
27506     {
27507         this.editorcore.toggleSourceEdit(sourceEditMode);
27508         
27509         if(this.editorcore.sourceEditMode){
27510             Roo.log('editor - showing textarea');
27511             
27512 //            Roo.log('in');
27513 //            Roo.log(this.syncValue());
27514             this.syncValue();
27515             this.inputEl().removeClass(['hide', 'x-hidden']);
27516             this.inputEl().dom.removeAttribute('tabIndex');
27517             this.inputEl().focus();
27518         }else{
27519             Roo.log('editor - hiding textarea');
27520 //            Roo.log('out')
27521 //            Roo.log(this.pushValue()); 
27522             this.pushValue();
27523             
27524             this.inputEl().addClass(['hide', 'x-hidden']);
27525             this.inputEl().dom.setAttribute('tabIndex', -1);
27526             //this.deferFocus();
27527         }
27528          
27529         if(this.resizable){
27530             this.setSize(this.wrap.getSize());
27531         }
27532         
27533         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27534     },
27535  
27536     // private (for BoxComponent)
27537     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27538
27539     // private (for BoxComponent)
27540     getResizeEl : function(){
27541         return this.wrap;
27542     },
27543
27544     // private (for BoxComponent)
27545     getPositionEl : function(){
27546         return this.wrap;
27547     },
27548
27549     // private
27550     initEvents : function(){
27551         this.originalValue = this.getValue();
27552     },
27553
27554 //    /**
27555 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27556 //     * @method
27557 //     */
27558 //    markInvalid : Roo.emptyFn,
27559 //    /**
27560 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27561 //     * @method
27562 //     */
27563 //    clearInvalid : Roo.emptyFn,
27564
27565     setValue : function(v){
27566         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27567         this.editorcore.pushValue();
27568     },
27569
27570      
27571     // private
27572     deferFocus : function(){
27573         this.focus.defer(10, this);
27574     },
27575
27576     // doc'ed in Field
27577     focus : function(){
27578         this.editorcore.focus();
27579         
27580     },
27581       
27582
27583     // private
27584     onDestroy : function(){
27585         
27586         
27587         
27588         if(this.rendered){
27589             
27590             for (var i =0; i < this.toolbars.length;i++) {
27591                 // fixme - ask toolbars for heights?
27592                 this.toolbars[i].onDestroy();
27593             }
27594             
27595             this.wrap.dom.innerHTML = '';
27596             this.wrap.remove();
27597         }
27598     },
27599
27600     // private
27601     onFirstFocus : function(){
27602         //Roo.log("onFirstFocus");
27603         this.editorcore.onFirstFocus();
27604          for (var i =0; i < this.toolbars.length;i++) {
27605             this.toolbars[i].onFirstFocus();
27606         }
27607         
27608     },
27609     
27610     // private
27611     syncValue : function()
27612     {   
27613         this.editorcore.syncValue();
27614     },
27615     
27616     pushValue : function()
27617     {   
27618         this.editorcore.pushValue();
27619     }
27620      
27621     
27622     // hide stuff that is not compatible
27623     /**
27624      * @event blur
27625      * @hide
27626      */
27627     /**
27628      * @event change
27629      * @hide
27630      */
27631     /**
27632      * @event focus
27633      * @hide
27634      */
27635     /**
27636      * @event specialkey
27637      * @hide
27638      */
27639     /**
27640      * @cfg {String} fieldClass @hide
27641      */
27642     /**
27643      * @cfg {String} focusClass @hide
27644      */
27645     /**
27646      * @cfg {String} autoCreate @hide
27647      */
27648     /**
27649      * @cfg {String} inputType @hide
27650      */
27651      
27652     /**
27653      * @cfg {String} invalidText @hide
27654      */
27655     /**
27656      * @cfg {String} msgFx @hide
27657      */
27658     /**
27659      * @cfg {String} validateOnBlur @hide
27660      */
27661 });
27662  
27663     
27664    
27665    
27666    
27667       
27668 Roo.namespace('Roo.bootstrap.htmleditor');
27669 /**
27670  * @class Roo.bootstrap.HtmlEditorToolbar1
27671  * Basic Toolbar
27672  * 
27673  * @example
27674  * Usage:
27675  *
27676  new Roo.bootstrap.HtmlEditor({
27677     ....
27678     toolbars : [
27679         new Roo.bootstrap.HtmlEditorToolbar1({
27680             disable : { fonts: 1 , format: 1, ..., ... , ...],
27681             btns : [ .... ]
27682         })
27683     }
27684      
27685  * 
27686  * @cfg {Object} disable List of elements to disable..
27687  * @cfg {Array} btns List of additional buttons.
27688  * 
27689  * 
27690  * NEEDS Extra CSS? 
27691  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27692  */
27693  
27694 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27695 {
27696     
27697     Roo.apply(this, config);
27698     
27699     // default disabled, based on 'good practice'..
27700     this.disable = this.disable || {};
27701     Roo.applyIf(this.disable, {
27702         fontSize : true,
27703         colors : true,
27704         specialElements : true
27705     });
27706     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27707     
27708     this.editor = config.editor;
27709     this.editorcore = config.editor.editorcore;
27710     
27711     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27712     
27713     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27714     // dont call parent... till later.
27715 }
27716 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27717      
27718     bar : true,
27719     
27720     editor : false,
27721     editorcore : false,
27722     
27723     
27724     formats : [
27725         "p" ,  
27726         "h1","h2","h3","h4","h5","h6", 
27727         "pre", "code", 
27728         "abbr", "acronym", "address", "cite", "samp", "var",
27729         'div','span'
27730     ],
27731     
27732     onRender : function(ct, position)
27733     {
27734        // Roo.log("Call onRender: " + this.xtype);
27735         
27736        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27737        Roo.log(this.el);
27738        this.el.dom.style.marginBottom = '0';
27739        var _this = this;
27740        var editorcore = this.editorcore;
27741        var editor= this.editor;
27742        
27743        var children = [];
27744        var btn = function(id,cmd , toggle, handler, html){
27745        
27746             var  event = toggle ? 'toggle' : 'click';
27747        
27748             var a = {
27749                 size : 'sm',
27750                 xtype: 'Button',
27751                 xns: Roo.bootstrap,
27752                 //glyphicon : id,
27753                 fa: id,
27754                 cmd : id || cmd,
27755                 enableToggle:toggle !== false,
27756                 html : html || '',
27757                 pressed : toggle ? false : null,
27758                 listeners : {}
27759             };
27760             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27761                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27762             };
27763             children.push(a);
27764             return a;
27765        }
27766        
27767     //    var cb_box = function...
27768         
27769         var style = {
27770                 xtype: 'Button',
27771                 size : 'sm',
27772                 xns: Roo.bootstrap,
27773                 fa : 'font',
27774                 //html : 'submit'
27775                 menu : {
27776                     xtype: 'Menu',
27777                     xns: Roo.bootstrap,
27778                     items:  []
27779                 }
27780         };
27781         Roo.each(this.formats, function(f) {
27782             style.menu.items.push({
27783                 xtype :'MenuItem',
27784                 xns: Roo.bootstrap,
27785                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27786                 tagname : f,
27787                 listeners : {
27788                     click : function()
27789                     {
27790                         editorcore.insertTag(this.tagname);
27791                         editor.focus();
27792                     }
27793                 }
27794                 
27795             });
27796         });
27797         children.push(style);   
27798         
27799         btn('bold',false,true);
27800         btn('italic',false,true);
27801         btn('align-left', 'justifyleft',true);
27802         btn('align-center', 'justifycenter',true);
27803         btn('align-right' , 'justifyright',true);
27804         btn('link', false, false, function(btn) {
27805             //Roo.log("create link?");
27806             var url = prompt(this.createLinkText, this.defaultLinkValue);
27807             if(url && url != 'http:/'+'/'){
27808                 this.editorcore.relayCmd('createlink', url);
27809             }
27810         }),
27811         btn('list','insertunorderedlist',true);
27812         btn('pencil', false,true, function(btn){
27813                 Roo.log(this);
27814                 this.toggleSourceEdit(btn.pressed);
27815         });
27816         
27817         if (this.editor.btns.length > 0) {
27818             for (var i = 0; i<this.editor.btns.length; i++) {
27819                 children.push(this.editor.btns[i]);
27820             }
27821         }
27822         
27823         /*
27824         var cog = {
27825                 xtype: 'Button',
27826                 size : 'sm',
27827                 xns: Roo.bootstrap,
27828                 glyphicon : 'cog',
27829                 //html : 'submit'
27830                 menu : {
27831                     xtype: 'Menu',
27832                     xns: Roo.bootstrap,
27833                     items:  []
27834                 }
27835         };
27836         
27837         cog.menu.items.push({
27838             xtype :'MenuItem',
27839             xns: Roo.bootstrap,
27840             html : Clean styles,
27841             tagname : f,
27842             listeners : {
27843                 click : function()
27844                 {
27845                     editorcore.insertTag(this.tagname);
27846                     editor.focus();
27847                 }
27848             }
27849             
27850         });
27851        */
27852         
27853          
27854        this.xtype = 'NavSimplebar';
27855         
27856         for(var i=0;i< children.length;i++) {
27857             
27858             this.buttons.add(this.addxtypeChild(children[i]));
27859             
27860         }
27861         
27862         editor.on('editorevent', this.updateToolbar, this);
27863     },
27864     onBtnClick : function(id)
27865     {
27866        this.editorcore.relayCmd(id);
27867        this.editorcore.focus();
27868     },
27869     
27870     /**
27871      * Protected method that will not generally be called directly. It triggers
27872      * a toolbar update by reading the markup state of the current selection in the editor.
27873      */
27874     updateToolbar: function(){
27875
27876         if(!this.editorcore.activated){
27877             this.editor.onFirstFocus(); // is this neeed?
27878             return;
27879         }
27880
27881         var btns = this.buttons; 
27882         var doc = this.editorcore.doc;
27883         btns.get('bold').setActive(doc.queryCommandState('bold'));
27884         btns.get('italic').setActive(doc.queryCommandState('italic'));
27885         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27886         
27887         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27888         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27889         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27890         
27891         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27892         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27893          /*
27894         
27895         var ans = this.editorcore.getAllAncestors();
27896         if (this.formatCombo) {
27897             
27898             
27899             var store = this.formatCombo.store;
27900             this.formatCombo.setValue("");
27901             for (var i =0; i < ans.length;i++) {
27902                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27903                     // select it..
27904                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27905                     break;
27906                 }
27907             }
27908         }
27909         
27910         
27911         
27912         // hides menus... - so this cant be on a menu...
27913         Roo.bootstrap.MenuMgr.hideAll();
27914         */
27915         Roo.bootstrap.MenuMgr.hideAll();
27916         //this.editorsyncValue();
27917     },
27918     onFirstFocus: function() {
27919         this.buttons.each(function(item){
27920            item.enable();
27921         });
27922     },
27923     toggleSourceEdit : function(sourceEditMode){
27924         
27925           
27926         if(sourceEditMode){
27927             Roo.log("disabling buttons");
27928            this.buttons.each( function(item){
27929                 if(item.cmd != 'pencil'){
27930                     item.disable();
27931                 }
27932             });
27933           
27934         }else{
27935             Roo.log("enabling buttons");
27936             if(this.editorcore.initialized){
27937                 this.buttons.each( function(item){
27938                     item.enable();
27939                 });
27940             }
27941             
27942         }
27943         Roo.log("calling toggole on editor");
27944         // tell the editor that it's been pressed..
27945         this.editor.toggleSourceEdit(sourceEditMode);
27946        
27947     }
27948 });
27949
27950
27951
27952
27953  
27954 /*
27955  * - LGPL
27956  */
27957
27958 /**
27959  * @class Roo.bootstrap.Markdown
27960  * @extends Roo.bootstrap.TextArea
27961  * Bootstrap Showdown editable area
27962  * @cfg {string} content
27963  * 
27964  * @constructor
27965  * Create a new Showdown
27966  */
27967
27968 Roo.bootstrap.Markdown = function(config){
27969     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27970    
27971 };
27972
27973 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27974     
27975     editing :false,
27976     
27977     initEvents : function()
27978     {
27979         
27980         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27981         this.markdownEl = this.el.createChild({
27982             cls : 'roo-markdown-area'
27983         });
27984         this.inputEl().addClass('d-none');
27985         if (this.getValue() == '') {
27986             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27987             
27988         } else {
27989             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27990         }
27991         this.markdownEl.on('click', this.toggleTextEdit, this);
27992         this.on('blur', this.toggleTextEdit, this);
27993         this.on('specialkey', this.resizeTextArea, this);
27994     },
27995     
27996     toggleTextEdit : function()
27997     {
27998         var sh = this.markdownEl.getHeight();
27999         this.inputEl().addClass('d-none');
28000         this.markdownEl.addClass('d-none');
28001         if (!this.editing) {
28002             // show editor?
28003             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28004             this.inputEl().removeClass('d-none');
28005             this.inputEl().focus();
28006             this.editing = true;
28007             return;
28008         }
28009         // show showdown...
28010         this.updateMarkdown();
28011         this.markdownEl.removeClass('d-none');
28012         this.editing = false;
28013         return;
28014     },
28015     updateMarkdown : function()
28016     {
28017         if (this.getValue() == '') {
28018             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28019             return;
28020         }
28021  
28022         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28023     },
28024     
28025     resizeTextArea: function () {
28026         
28027         var sh = 100;
28028         Roo.log([sh, this.getValue().split("\n").length * 30]);
28029         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28030     },
28031     setValue : function(val)
28032     {
28033         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28034         if (!this.editing) {
28035             this.updateMarkdown();
28036         }
28037         
28038     },
28039     focus : function()
28040     {
28041         if (!this.editing) {
28042             this.toggleTextEdit();
28043         }
28044         
28045     }
28046
28047
28048 });/*
28049  * Based on:
28050  * Ext JS Library 1.1.1
28051  * Copyright(c) 2006-2007, Ext JS, LLC.
28052  *
28053  * Originally Released Under LGPL - original licence link has changed is not relivant.
28054  *
28055  * Fork - LGPL
28056  * <script type="text/javascript">
28057  */
28058  
28059 /**
28060  * @class Roo.bootstrap.PagingToolbar
28061  * @extends Roo.bootstrap.NavSimplebar
28062  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28063  * @constructor
28064  * Create a new PagingToolbar
28065  * @param {Object} config The config object
28066  * @param {Roo.data.Store} store
28067  */
28068 Roo.bootstrap.PagingToolbar = function(config)
28069 {
28070     // old args format still supported... - xtype is prefered..
28071         // created from xtype...
28072     
28073     this.ds = config.dataSource;
28074     
28075     if (config.store && !this.ds) {
28076         this.store= Roo.factory(config.store, Roo.data);
28077         this.ds = this.store;
28078         this.ds.xmodule = this.xmodule || false;
28079     }
28080     
28081     this.toolbarItems = [];
28082     if (config.items) {
28083         this.toolbarItems = config.items;
28084     }
28085     
28086     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28087     
28088     this.cursor = 0;
28089     
28090     if (this.ds) { 
28091         this.bind(this.ds);
28092     }
28093     
28094     if (Roo.bootstrap.version == 4) {
28095         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28096     } else {
28097         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28098     }
28099     
28100 };
28101
28102 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28103     /**
28104      * @cfg {Roo.data.Store} dataSource
28105      * The underlying data store providing the paged data
28106      */
28107     /**
28108      * @cfg {String/HTMLElement/Element} container
28109      * container The id or element that will contain the toolbar
28110      */
28111     /**
28112      * @cfg {Boolean} displayInfo
28113      * True to display the displayMsg (defaults to false)
28114      */
28115     /**
28116      * @cfg {Number} pageSize
28117      * The number of records to display per page (defaults to 20)
28118      */
28119     pageSize: 20,
28120     /**
28121      * @cfg {String} displayMsg
28122      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28123      */
28124     displayMsg : 'Displaying {0} - {1} of {2}',
28125     /**
28126      * @cfg {String} emptyMsg
28127      * The message to display when no records are found (defaults to "No data to display")
28128      */
28129     emptyMsg : 'No data to display',
28130     /**
28131      * Customizable piece of the default paging text (defaults to "Page")
28132      * @type String
28133      */
28134     beforePageText : "Page",
28135     /**
28136      * Customizable piece of the default paging text (defaults to "of %0")
28137      * @type String
28138      */
28139     afterPageText : "of {0}",
28140     /**
28141      * Customizable piece of the default paging text (defaults to "First Page")
28142      * @type String
28143      */
28144     firstText : "First Page",
28145     /**
28146      * Customizable piece of the default paging text (defaults to "Previous Page")
28147      * @type String
28148      */
28149     prevText : "Previous Page",
28150     /**
28151      * Customizable piece of the default paging text (defaults to "Next Page")
28152      * @type String
28153      */
28154     nextText : "Next Page",
28155     /**
28156      * Customizable piece of the default paging text (defaults to "Last Page")
28157      * @type String
28158      */
28159     lastText : "Last Page",
28160     /**
28161      * Customizable piece of the default paging text (defaults to "Refresh")
28162      * @type String
28163      */
28164     refreshText : "Refresh",
28165
28166     buttons : false,
28167     // private
28168     onRender : function(ct, position) 
28169     {
28170         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28171         this.navgroup.parentId = this.id;
28172         this.navgroup.onRender(this.el, null);
28173         // add the buttons to the navgroup
28174         
28175         if(this.displayInfo){
28176             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28177             this.displayEl = this.el.select('.x-paging-info', true).first();
28178 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28179 //            this.displayEl = navel.el.select('span',true).first();
28180         }
28181         
28182         var _this = this;
28183         
28184         if(this.buttons){
28185             Roo.each(_this.buttons, function(e){ // this might need to use render????
28186                Roo.factory(e).render(_this.el);
28187             });
28188         }
28189             
28190         Roo.each(_this.toolbarItems, function(e) {
28191             _this.navgroup.addItem(e);
28192         });
28193         
28194         
28195         this.first = this.navgroup.addItem({
28196             tooltip: this.firstText,
28197             cls: "prev btn-outline-secondary",
28198             html : ' <i class="fa fa-step-backward"></i>',
28199             disabled: true,
28200             preventDefault: true,
28201             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28202         });
28203         
28204         this.prev =  this.navgroup.addItem({
28205             tooltip: this.prevText,
28206             cls: "prev btn-outline-secondary",
28207             html : ' <i class="fa fa-backward"></i>',
28208             disabled: true,
28209             preventDefault: true,
28210             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28211         });
28212     //this.addSeparator();
28213         
28214         
28215         var field = this.navgroup.addItem( {
28216             tagtype : 'span',
28217             cls : 'x-paging-position  btn-outline-secondary',
28218              disabled: true,
28219             html : this.beforePageText  +
28220                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28221                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28222          } ); //?? escaped?
28223         
28224         this.field = field.el.select('input', true).first();
28225         this.field.on("keydown", this.onPagingKeydown, this);
28226         this.field.on("focus", function(){this.dom.select();});
28227     
28228     
28229         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28230         //this.field.setHeight(18);
28231         //this.addSeparator();
28232         this.next = this.navgroup.addItem({
28233             tooltip: this.nextText,
28234             cls: "next btn-outline-secondary",
28235             html : ' <i class="fa fa-forward"></i>',
28236             disabled: true,
28237             preventDefault: true,
28238             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28239         });
28240         this.last = this.navgroup.addItem({
28241             tooltip: this.lastText,
28242             html : ' <i class="fa fa-step-forward"></i>',
28243             cls: "next btn-outline-secondary",
28244             disabled: true,
28245             preventDefault: true,
28246             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28247         });
28248     //this.addSeparator();
28249         this.loading = this.navgroup.addItem({
28250             tooltip: this.refreshText,
28251             cls: "btn-outline-secondary",
28252             html : ' <i class="fa fa-refresh"></i>',
28253             preventDefault: true,
28254             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28255         });
28256         
28257     },
28258
28259     // private
28260     updateInfo : function(){
28261         if(this.displayEl){
28262             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28263             var msg = count == 0 ?
28264                 this.emptyMsg :
28265                 String.format(
28266                     this.displayMsg,
28267                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28268                 );
28269             this.displayEl.update(msg);
28270         }
28271     },
28272
28273     // private
28274     onLoad : function(ds, r, o)
28275     {
28276         this.cursor = o.params && o.params.start ? o.params.start : 0;
28277         
28278         var d = this.getPageData(),
28279             ap = d.activePage,
28280             ps = d.pages;
28281         
28282         
28283         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28284         this.field.dom.value = ap;
28285         this.first.setDisabled(ap == 1);
28286         this.prev.setDisabled(ap == 1);
28287         this.next.setDisabled(ap == ps);
28288         this.last.setDisabled(ap == ps);
28289         this.loading.enable();
28290         this.updateInfo();
28291     },
28292
28293     // private
28294     getPageData : function(){
28295         var total = this.ds.getTotalCount();
28296         return {
28297             total : total,
28298             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28299             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28300         };
28301     },
28302
28303     // private
28304     onLoadError : function(){
28305         this.loading.enable();
28306     },
28307
28308     // private
28309     onPagingKeydown : function(e){
28310         var k = e.getKey();
28311         var d = this.getPageData();
28312         if(k == e.RETURN){
28313             var v = this.field.dom.value, pageNum;
28314             if(!v || isNaN(pageNum = parseInt(v, 10))){
28315                 this.field.dom.value = d.activePage;
28316                 return;
28317             }
28318             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28319             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28320             e.stopEvent();
28321         }
28322         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))
28323         {
28324           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28325           this.field.dom.value = pageNum;
28326           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28327           e.stopEvent();
28328         }
28329         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28330         {
28331           var v = this.field.dom.value, pageNum; 
28332           var increment = (e.shiftKey) ? 10 : 1;
28333           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28334                 increment *= -1;
28335           }
28336           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28337             this.field.dom.value = d.activePage;
28338             return;
28339           }
28340           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28341           {
28342             this.field.dom.value = parseInt(v, 10) + increment;
28343             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28344             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28345           }
28346           e.stopEvent();
28347         }
28348     },
28349
28350     // private
28351     beforeLoad : function(){
28352         if(this.loading){
28353             this.loading.disable();
28354         }
28355     },
28356
28357     // private
28358     onClick : function(which){
28359         
28360         var ds = this.ds;
28361         if (!ds) {
28362             return;
28363         }
28364         
28365         switch(which){
28366             case "first":
28367                 ds.load({params:{start: 0, limit: this.pageSize}});
28368             break;
28369             case "prev":
28370                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28371             break;
28372             case "next":
28373                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28374             break;
28375             case "last":
28376                 var total = ds.getTotalCount();
28377                 var extra = total % this.pageSize;
28378                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28379                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28380             break;
28381             case "refresh":
28382                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28383             break;
28384         }
28385     },
28386
28387     /**
28388      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28389      * @param {Roo.data.Store} store The data store to unbind
28390      */
28391     unbind : function(ds){
28392         ds.un("beforeload", this.beforeLoad, this);
28393         ds.un("load", this.onLoad, this);
28394         ds.un("loadexception", this.onLoadError, this);
28395         ds.un("remove", this.updateInfo, this);
28396         ds.un("add", this.updateInfo, this);
28397         this.ds = undefined;
28398     },
28399
28400     /**
28401      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28402      * @param {Roo.data.Store} store The data store to bind
28403      */
28404     bind : function(ds){
28405         ds.on("beforeload", this.beforeLoad, this);
28406         ds.on("load", this.onLoad, this);
28407         ds.on("loadexception", this.onLoadError, this);
28408         ds.on("remove", this.updateInfo, this);
28409         ds.on("add", this.updateInfo, this);
28410         this.ds = ds;
28411     }
28412 });/*
28413  * - LGPL
28414  *
28415  * element
28416  * 
28417  */
28418
28419 /**
28420  * @class Roo.bootstrap.MessageBar
28421  * @extends Roo.bootstrap.Component
28422  * Bootstrap MessageBar class
28423  * @cfg {String} html contents of the MessageBar
28424  * @cfg {String} weight (info | success | warning | danger) default info
28425  * @cfg {String} beforeClass insert the bar before the given class
28426  * @cfg {Boolean} closable (true | false) default false
28427  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28428  * 
28429  * @constructor
28430  * Create a new Element
28431  * @param {Object} config The config object
28432  */
28433
28434 Roo.bootstrap.MessageBar = function(config){
28435     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28436 };
28437
28438 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28439     
28440     html: '',
28441     weight: 'info',
28442     closable: false,
28443     fixed: false,
28444     beforeClass: 'bootstrap-sticky-wrap',
28445     
28446     getAutoCreate : function(){
28447         
28448         var cfg = {
28449             tag: 'div',
28450             cls: 'alert alert-dismissable alert-' + this.weight,
28451             cn: [
28452                 {
28453                     tag: 'span',
28454                     cls: 'message',
28455                     html: this.html || ''
28456                 }
28457             ]
28458         };
28459         
28460         if(this.fixed){
28461             cfg.cls += ' alert-messages-fixed';
28462         }
28463         
28464         if(this.closable){
28465             cfg.cn.push({
28466                 tag: 'button',
28467                 cls: 'close',
28468                 html: 'x'
28469             });
28470         }
28471         
28472         return cfg;
28473     },
28474     
28475     onRender : function(ct, position)
28476     {
28477         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28478         
28479         if(!this.el){
28480             var cfg = Roo.apply({},  this.getAutoCreate());
28481             cfg.id = Roo.id();
28482             
28483             if (this.cls) {
28484                 cfg.cls += ' ' + this.cls;
28485             }
28486             if (this.style) {
28487                 cfg.style = this.style;
28488             }
28489             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28490             
28491             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28492         }
28493         
28494         this.el.select('>button.close').on('click', this.hide, this);
28495         
28496     },
28497     
28498     show : function()
28499     {
28500         if (!this.rendered) {
28501             this.render();
28502         }
28503         
28504         this.el.show();
28505         
28506         this.fireEvent('show', this);
28507         
28508     },
28509     
28510     hide : function()
28511     {
28512         if (!this.rendered) {
28513             this.render();
28514         }
28515         
28516         this.el.hide();
28517         
28518         this.fireEvent('hide', this);
28519     },
28520     
28521     update : function()
28522     {
28523 //        var e = this.el.dom.firstChild;
28524 //        
28525 //        if(this.closable){
28526 //            e = e.nextSibling;
28527 //        }
28528 //        
28529 //        e.data = this.html || '';
28530
28531         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28532     }
28533    
28534 });
28535
28536  
28537
28538      /*
28539  * - LGPL
28540  *
28541  * Graph
28542  * 
28543  */
28544
28545
28546 /**
28547  * @class Roo.bootstrap.Graph
28548  * @extends Roo.bootstrap.Component
28549  * Bootstrap Graph class
28550 > Prameters
28551  -sm {number} sm 4
28552  -md {number} md 5
28553  @cfg {String} graphtype  bar | vbar | pie
28554  @cfg {number} g_x coodinator | centre x (pie)
28555  @cfg {number} g_y coodinator | centre y (pie)
28556  @cfg {number} g_r radius (pie)
28557  @cfg {number} g_height height of the chart (respected by all elements in the set)
28558  @cfg {number} g_width width of the chart (respected by all elements in the set)
28559  @cfg {Object} title The title of the chart
28560     
28561  -{Array}  values
28562  -opts (object) options for the chart 
28563      o {
28564      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28565      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28566      o vgutter (number)
28567      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.
28568      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28569      o to
28570      o stretch (boolean)
28571      o }
28572  -opts (object) options for the pie
28573      o{
28574      o cut
28575      o startAngle (number)
28576      o endAngle (number)
28577      } 
28578  *
28579  * @constructor
28580  * Create a new Input
28581  * @param {Object} config The config object
28582  */
28583
28584 Roo.bootstrap.Graph = function(config){
28585     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28586     
28587     this.addEvents({
28588         // img events
28589         /**
28590          * @event click
28591          * The img click event for the img.
28592          * @param {Roo.EventObject} e
28593          */
28594         "click" : true
28595     });
28596 };
28597
28598 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28599     
28600     sm: 4,
28601     md: 5,
28602     graphtype: 'bar',
28603     g_height: 250,
28604     g_width: 400,
28605     g_x: 50,
28606     g_y: 50,
28607     g_r: 30,
28608     opts:{
28609         //g_colors: this.colors,
28610         g_type: 'soft',
28611         g_gutter: '20%'
28612
28613     },
28614     title : false,
28615
28616     getAutoCreate : function(){
28617         
28618         var cfg = {
28619             tag: 'div',
28620             html : null
28621         };
28622         
28623         
28624         return  cfg;
28625     },
28626
28627     onRender : function(ct,position){
28628         
28629         
28630         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28631         
28632         if (typeof(Raphael) == 'undefined') {
28633             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28634             return;
28635         }
28636         
28637         this.raphael = Raphael(this.el.dom);
28638         
28639                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28640                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28641                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28642                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28643                 /*
28644                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28645                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28646                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28647                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28648                 
28649                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28650                 r.barchart(330, 10, 300, 220, data1);
28651                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28652                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28653                 */
28654                 
28655                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28656                 // r.barchart(30, 30, 560, 250,  xdata, {
28657                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28658                 //     axis : "0 0 1 1",
28659                 //     axisxlabels :  xdata
28660                 //     //yvalues : cols,
28661                    
28662                 // });
28663 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28664 //        
28665 //        this.load(null,xdata,{
28666 //                axis : "0 0 1 1",
28667 //                axisxlabels :  xdata
28668 //                });
28669
28670     },
28671
28672     load : function(graphtype,xdata,opts)
28673     {
28674         this.raphael.clear();
28675         if(!graphtype) {
28676             graphtype = this.graphtype;
28677         }
28678         if(!opts){
28679             opts = this.opts;
28680         }
28681         var r = this.raphael,
28682             fin = function () {
28683                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28684             },
28685             fout = function () {
28686                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28687             },
28688             pfin = function() {
28689                 this.sector.stop();
28690                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28691
28692                 if (this.label) {
28693                     this.label[0].stop();
28694                     this.label[0].attr({ r: 7.5 });
28695                     this.label[1].attr({ "font-weight": 800 });
28696                 }
28697             },
28698             pfout = function() {
28699                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28700
28701                 if (this.label) {
28702                     this.label[0].animate({ r: 5 }, 500, "bounce");
28703                     this.label[1].attr({ "font-weight": 400 });
28704                 }
28705             };
28706
28707         switch(graphtype){
28708             case 'bar':
28709                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28710                 break;
28711             case 'hbar':
28712                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28713                 break;
28714             case 'pie':
28715 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28716 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28717 //            
28718                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28719                 
28720                 break;
28721
28722         }
28723         
28724         if(this.title){
28725             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28726         }
28727         
28728     },
28729     
28730     setTitle: function(o)
28731     {
28732         this.title = o;
28733     },
28734     
28735     initEvents: function() {
28736         
28737         if(!this.href){
28738             this.el.on('click', this.onClick, this);
28739         }
28740     },
28741     
28742     onClick : function(e)
28743     {
28744         Roo.log('img onclick');
28745         this.fireEvent('click', this, e);
28746     }
28747    
28748 });
28749
28750  
28751 /*
28752  * - LGPL
28753  *
28754  * numberBox
28755  * 
28756  */
28757 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28758
28759 /**
28760  * @class Roo.bootstrap.dash.NumberBox
28761  * @extends Roo.bootstrap.Component
28762  * Bootstrap NumberBox class
28763  * @cfg {String} headline Box headline
28764  * @cfg {String} content Box content
28765  * @cfg {String} icon Box icon
28766  * @cfg {String} footer Footer text
28767  * @cfg {String} fhref Footer href
28768  * 
28769  * @constructor
28770  * Create a new NumberBox
28771  * @param {Object} config The config object
28772  */
28773
28774
28775 Roo.bootstrap.dash.NumberBox = function(config){
28776     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28777     
28778 };
28779
28780 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28781     
28782     headline : '',
28783     content : '',
28784     icon : '',
28785     footer : '',
28786     fhref : '',
28787     ficon : '',
28788     
28789     getAutoCreate : function(){
28790         
28791         var cfg = {
28792             tag : 'div',
28793             cls : 'small-box ',
28794             cn : [
28795                 {
28796                     tag : 'div',
28797                     cls : 'inner',
28798                     cn :[
28799                         {
28800                             tag : 'h3',
28801                             cls : 'roo-headline',
28802                             html : this.headline
28803                         },
28804                         {
28805                             tag : 'p',
28806                             cls : 'roo-content',
28807                             html : this.content
28808                         }
28809                     ]
28810                 }
28811             ]
28812         };
28813         
28814         if(this.icon){
28815             cfg.cn.push({
28816                 tag : 'div',
28817                 cls : 'icon',
28818                 cn :[
28819                     {
28820                         tag : 'i',
28821                         cls : 'ion ' + this.icon
28822                     }
28823                 ]
28824             });
28825         }
28826         
28827         if(this.footer){
28828             var footer = {
28829                 tag : 'a',
28830                 cls : 'small-box-footer',
28831                 href : this.fhref || '#',
28832                 html : this.footer
28833             };
28834             
28835             cfg.cn.push(footer);
28836             
28837         }
28838         
28839         return  cfg;
28840     },
28841
28842     onRender : function(ct,position){
28843         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28844
28845
28846        
28847                 
28848     },
28849
28850     setHeadline: function (value)
28851     {
28852         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28853     },
28854     
28855     setFooter: function (value, href)
28856     {
28857         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28858         
28859         if(href){
28860             this.el.select('a.small-box-footer',true).first().attr('href', href);
28861         }
28862         
28863     },
28864
28865     setContent: function (value)
28866     {
28867         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28868     },
28869
28870     initEvents: function() 
28871     {   
28872         
28873     }
28874     
28875 });
28876
28877  
28878 /*
28879  * - LGPL
28880  *
28881  * TabBox
28882  * 
28883  */
28884 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28885
28886 /**
28887  * @class Roo.bootstrap.dash.TabBox
28888  * @extends Roo.bootstrap.Component
28889  * Bootstrap TabBox class
28890  * @cfg {String} title Title of the TabBox
28891  * @cfg {String} icon Icon of the TabBox
28892  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28893  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28894  * 
28895  * @constructor
28896  * Create a new TabBox
28897  * @param {Object} config The config object
28898  */
28899
28900
28901 Roo.bootstrap.dash.TabBox = function(config){
28902     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28903     this.addEvents({
28904         // raw events
28905         /**
28906          * @event addpane
28907          * When a pane is added
28908          * @param {Roo.bootstrap.dash.TabPane} pane
28909          */
28910         "addpane" : true,
28911         /**
28912          * @event activatepane
28913          * When a pane is activated
28914          * @param {Roo.bootstrap.dash.TabPane} pane
28915          */
28916         "activatepane" : true
28917         
28918          
28919     });
28920     
28921     this.panes = [];
28922 };
28923
28924 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28925
28926     title : '',
28927     icon : false,
28928     showtabs : true,
28929     tabScrollable : false,
28930     
28931     getChildContainer : function()
28932     {
28933         return this.el.select('.tab-content', true).first();
28934     },
28935     
28936     getAutoCreate : function(){
28937         
28938         var header = {
28939             tag: 'li',
28940             cls: 'pull-left header',
28941             html: this.title,
28942             cn : []
28943         };
28944         
28945         if(this.icon){
28946             header.cn.push({
28947                 tag: 'i',
28948                 cls: 'fa ' + this.icon
28949             });
28950         }
28951         
28952         var h = {
28953             tag: 'ul',
28954             cls: 'nav nav-tabs pull-right',
28955             cn: [
28956                 header
28957             ]
28958         };
28959         
28960         if(this.tabScrollable){
28961             h = {
28962                 tag: 'div',
28963                 cls: 'tab-header',
28964                 cn: [
28965                     {
28966                         tag: 'ul',
28967                         cls: 'nav nav-tabs pull-right',
28968                         cn: [
28969                             header
28970                         ]
28971                     }
28972                 ]
28973             };
28974         }
28975         
28976         var cfg = {
28977             tag: 'div',
28978             cls: 'nav-tabs-custom',
28979             cn: [
28980                 h,
28981                 {
28982                     tag: 'div',
28983                     cls: 'tab-content no-padding',
28984                     cn: []
28985                 }
28986             ]
28987         };
28988
28989         return  cfg;
28990     },
28991     initEvents : function()
28992     {
28993         //Roo.log('add add pane handler');
28994         this.on('addpane', this.onAddPane, this);
28995     },
28996      /**
28997      * Updates the box title
28998      * @param {String} html to set the title to.
28999      */
29000     setTitle : function(value)
29001     {
29002         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29003     },
29004     onAddPane : function(pane)
29005     {
29006         this.panes.push(pane);
29007         //Roo.log('addpane');
29008         //Roo.log(pane);
29009         // tabs are rendere left to right..
29010         if(!this.showtabs){
29011             return;
29012         }
29013         
29014         var ctr = this.el.select('.nav-tabs', true).first();
29015          
29016          
29017         var existing = ctr.select('.nav-tab',true);
29018         var qty = existing.getCount();;
29019         
29020         
29021         var tab = ctr.createChild({
29022             tag : 'li',
29023             cls : 'nav-tab' + (qty ? '' : ' active'),
29024             cn : [
29025                 {
29026                     tag : 'a',
29027                     href:'#',
29028                     html : pane.title
29029                 }
29030             ]
29031         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29032         pane.tab = tab;
29033         
29034         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29035         if (!qty) {
29036             pane.el.addClass('active');
29037         }
29038         
29039                 
29040     },
29041     onTabClick : function(ev,un,ob,pane)
29042     {
29043         //Roo.log('tab - prev default');
29044         ev.preventDefault();
29045         
29046         
29047         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29048         pane.tab.addClass('active');
29049         //Roo.log(pane.title);
29050         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29051         // technically we should have a deactivate event.. but maybe add later.
29052         // and it should not de-activate the selected tab...
29053         this.fireEvent('activatepane', pane);
29054         pane.el.addClass('active');
29055         pane.fireEvent('activate');
29056         
29057         
29058     },
29059     
29060     getActivePane : function()
29061     {
29062         var r = false;
29063         Roo.each(this.panes, function(p) {
29064             if(p.el.hasClass('active')){
29065                 r = p;
29066                 return false;
29067             }
29068             
29069             return;
29070         });
29071         
29072         return r;
29073     }
29074     
29075     
29076 });
29077
29078  
29079 /*
29080  * - LGPL
29081  *
29082  * Tab pane
29083  * 
29084  */
29085 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29086 /**
29087  * @class Roo.bootstrap.TabPane
29088  * @extends Roo.bootstrap.Component
29089  * Bootstrap TabPane class
29090  * @cfg {Boolean} active (false | true) Default false
29091  * @cfg {String} title title of panel
29092
29093  * 
29094  * @constructor
29095  * Create a new TabPane
29096  * @param {Object} config The config object
29097  */
29098
29099 Roo.bootstrap.dash.TabPane = function(config){
29100     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29101     
29102     this.addEvents({
29103         // raw events
29104         /**
29105          * @event activate
29106          * When a pane is activated
29107          * @param {Roo.bootstrap.dash.TabPane} pane
29108          */
29109         "activate" : true
29110          
29111     });
29112 };
29113
29114 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29115     
29116     active : false,
29117     title : '',
29118     
29119     // the tabBox that this is attached to.
29120     tab : false,
29121      
29122     getAutoCreate : function() 
29123     {
29124         var cfg = {
29125             tag: 'div',
29126             cls: 'tab-pane'
29127         };
29128         
29129         if(this.active){
29130             cfg.cls += ' active';
29131         }
29132         
29133         return cfg;
29134     },
29135     initEvents  : function()
29136     {
29137         //Roo.log('trigger add pane handler');
29138         this.parent().fireEvent('addpane', this)
29139     },
29140     
29141      /**
29142      * Updates the tab title 
29143      * @param {String} html to set the title to.
29144      */
29145     setTitle: function(str)
29146     {
29147         if (!this.tab) {
29148             return;
29149         }
29150         this.title = str;
29151         this.tab.select('a', true).first().dom.innerHTML = str;
29152         
29153     }
29154     
29155     
29156     
29157 });
29158
29159  
29160
29161
29162  /*
29163  * - LGPL
29164  *
29165  * menu
29166  * 
29167  */
29168 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29169
29170 /**
29171  * @class Roo.bootstrap.menu.Menu
29172  * @extends Roo.bootstrap.Component
29173  * Bootstrap Menu class - container for Menu
29174  * @cfg {String} html Text of the menu
29175  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29176  * @cfg {String} icon Font awesome icon
29177  * @cfg {String} pos Menu align to (top | bottom) default bottom
29178  * 
29179  * 
29180  * @constructor
29181  * Create a new Menu
29182  * @param {Object} config The config object
29183  */
29184
29185
29186 Roo.bootstrap.menu.Menu = function(config){
29187     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29188     
29189     this.addEvents({
29190         /**
29191          * @event beforeshow
29192          * Fires before this menu is displayed
29193          * @param {Roo.bootstrap.menu.Menu} this
29194          */
29195         beforeshow : true,
29196         /**
29197          * @event beforehide
29198          * Fires before this menu is hidden
29199          * @param {Roo.bootstrap.menu.Menu} this
29200          */
29201         beforehide : true,
29202         /**
29203          * @event show
29204          * Fires after this menu is displayed
29205          * @param {Roo.bootstrap.menu.Menu} this
29206          */
29207         show : true,
29208         /**
29209          * @event hide
29210          * Fires after this menu is hidden
29211          * @param {Roo.bootstrap.menu.Menu} this
29212          */
29213         hide : true,
29214         /**
29215          * @event click
29216          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29217          * @param {Roo.bootstrap.menu.Menu} this
29218          * @param {Roo.EventObject} e
29219          */
29220         click : true
29221     });
29222     
29223 };
29224
29225 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29226     
29227     submenu : false,
29228     html : '',
29229     weight : 'default',
29230     icon : false,
29231     pos : 'bottom',
29232     
29233     
29234     getChildContainer : function() {
29235         if(this.isSubMenu){
29236             return this.el;
29237         }
29238         
29239         return this.el.select('ul.dropdown-menu', true).first();  
29240     },
29241     
29242     getAutoCreate : function()
29243     {
29244         var text = [
29245             {
29246                 tag : 'span',
29247                 cls : 'roo-menu-text',
29248                 html : this.html
29249             }
29250         ];
29251         
29252         if(this.icon){
29253             text.unshift({
29254                 tag : 'i',
29255                 cls : 'fa ' + this.icon
29256             })
29257         }
29258         
29259         
29260         var cfg = {
29261             tag : 'div',
29262             cls : 'btn-group',
29263             cn : [
29264                 {
29265                     tag : 'button',
29266                     cls : 'dropdown-button btn btn-' + this.weight,
29267                     cn : text
29268                 },
29269                 {
29270                     tag : 'button',
29271                     cls : 'dropdown-toggle btn btn-' + this.weight,
29272                     cn : [
29273                         {
29274                             tag : 'span',
29275                             cls : 'caret'
29276                         }
29277                     ]
29278                 },
29279                 {
29280                     tag : 'ul',
29281                     cls : 'dropdown-menu'
29282                 }
29283             ]
29284             
29285         };
29286         
29287         if(this.pos == 'top'){
29288             cfg.cls += ' dropup';
29289         }
29290         
29291         if(this.isSubMenu){
29292             cfg = {
29293                 tag : 'ul',
29294                 cls : 'dropdown-menu'
29295             }
29296         }
29297         
29298         return cfg;
29299     },
29300     
29301     onRender : function(ct, position)
29302     {
29303         this.isSubMenu = ct.hasClass('dropdown-submenu');
29304         
29305         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29306     },
29307     
29308     initEvents : function() 
29309     {
29310         if(this.isSubMenu){
29311             return;
29312         }
29313         
29314         this.hidden = true;
29315         
29316         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29317         this.triggerEl.on('click', this.onTriggerPress, this);
29318         
29319         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29320         this.buttonEl.on('click', this.onClick, this);
29321         
29322     },
29323     
29324     list : function()
29325     {
29326         if(this.isSubMenu){
29327             return this.el;
29328         }
29329         
29330         return this.el.select('ul.dropdown-menu', true).first();
29331     },
29332     
29333     onClick : function(e)
29334     {
29335         this.fireEvent("click", this, e);
29336     },
29337     
29338     onTriggerPress  : function(e)
29339     {   
29340         if (this.isVisible()) {
29341             this.hide();
29342         } else {
29343             this.show();
29344         }
29345     },
29346     
29347     isVisible : function(){
29348         return !this.hidden;
29349     },
29350     
29351     show : function()
29352     {
29353         this.fireEvent("beforeshow", this);
29354         
29355         this.hidden = false;
29356         this.el.addClass('open');
29357         
29358         Roo.get(document).on("mouseup", this.onMouseUp, this);
29359         
29360         this.fireEvent("show", this);
29361         
29362         
29363     },
29364     
29365     hide : function()
29366     {
29367         this.fireEvent("beforehide", this);
29368         
29369         this.hidden = true;
29370         this.el.removeClass('open');
29371         
29372         Roo.get(document).un("mouseup", this.onMouseUp);
29373         
29374         this.fireEvent("hide", this);
29375     },
29376     
29377     onMouseUp : function()
29378     {
29379         this.hide();
29380     }
29381     
29382 });
29383
29384  
29385  /*
29386  * - LGPL
29387  *
29388  * menu item
29389  * 
29390  */
29391 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29392
29393 /**
29394  * @class Roo.bootstrap.menu.Item
29395  * @extends Roo.bootstrap.Component
29396  * Bootstrap MenuItem class
29397  * @cfg {Boolean} submenu (true | false) default false
29398  * @cfg {String} html text of the item
29399  * @cfg {String} href the link
29400  * @cfg {Boolean} disable (true | false) default false
29401  * @cfg {Boolean} preventDefault (true | false) default true
29402  * @cfg {String} icon Font awesome icon
29403  * @cfg {String} pos Submenu align to (left | right) default right 
29404  * 
29405  * 
29406  * @constructor
29407  * Create a new Item
29408  * @param {Object} config The config object
29409  */
29410
29411
29412 Roo.bootstrap.menu.Item = function(config){
29413     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29414     this.addEvents({
29415         /**
29416          * @event mouseover
29417          * Fires when the mouse is hovering over this menu
29418          * @param {Roo.bootstrap.menu.Item} this
29419          * @param {Roo.EventObject} e
29420          */
29421         mouseover : true,
29422         /**
29423          * @event mouseout
29424          * Fires when the mouse exits this menu
29425          * @param {Roo.bootstrap.menu.Item} this
29426          * @param {Roo.EventObject} e
29427          */
29428         mouseout : true,
29429         // raw events
29430         /**
29431          * @event click
29432          * The raw click event for the entire grid.
29433          * @param {Roo.EventObject} e
29434          */
29435         click : true
29436     });
29437 };
29438
29439 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29440     
29441     submenu : false,
29442     href : '',
29443     html : '',
29444     preventDefault: true,
29445     disable : false,
29446     icon : false,
29447     pos : 'right',
29448     
29449     getAutoCreate : function()
29450     {
29451         var text = [
29452             {
29453                 tag : 'span',
29454                 cls : 'roo-menu-item-text',
29455                 html : this.html
29456             }
29457         ];
29458         
29459         if(this.icon){
29460             text.unshift({
29461                 tag : 'i',
29462                 cls : 'fa ' + this.icon
29463             })
29464         }
29465         
29466         var cfg = {
29467             tag : 'li',
29468             cn : [
29469                 {
29470                     tag : 'a',
29471                     href : this.href || '#',
29472                     cn : text
29473                 }
29474             ]
29475         };
29476         
29477         if(this.disable){
29478             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29479         }
29480         
29481         if(this.submenu){
29482             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29483             
29484             if(this.pos == 'left'){
29485                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29486             }
29487         }
29488         
29489         return cfg;
29490     },
29491     
29492     initEvents : function() 
29493     {
29494         this.el.on('mouseover', this.onMouseOver, this);
29495         this.el.on('mouseout', this.onMouseOut, this);
29496         
29497         this.el.select('a', true).first().on('click', this.onClick, this);
29498         
29499     },
29500     
29501     onClick : function(e)
29502     {
29503         if(this.preventDefault){
29504             e.preventDefault();
29505         }
29506         
29507         this.fireEvent("click", this, e);
29508     },
29509     
29510     onMouseOver : function(e)
29511     {
29512         if(this.submenu && this.pos == 'left'){
29513             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29514         }
29515         
29516         this.fireEvent("mouseover", this, e);
29517     },
29518     
29519     onMouseOut : function(e)
29520     {
29521         this.fireEvent("mouseout", this, e);
29522     }
29523 });
29524
29525  
29526
29527  /*
29528  * - LGPL
29529  *
29530  * menu separator
29531  * 
29532  */
29533 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29534
29535 /**
29536  * @class Roo.bootstrap.menu.Separator
29537  * @extends Roo.bootstrap.Component
29538  * Bootstrap Separator class
29539  * 
29540  * @constructor
29541  * Create a new Separator
29542  * @param {Object} config The config object
29543  */
29544
29545
29546 Roo.bootstrap.menu.Separator = function(config){
29547     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29548 };
29549
29550 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29551     
29552     getAutoCreate : function(){
29553         var cfg = {
29554             tag : 'li',
29555             cls: 'dropdown-divider divider'
29556         };
29557         
29558         return cfg;
29559     }
29560    
29561 });
29562
29563  
29564
29565  /*
29566  * - LGPL
29567  *
29568  * Tooltip
29569  * 
29570  */
29571
29572 /**
29573  * @class Roo.bootstrap.Tooltip
29574  * Bootstrap Tooltip class
29575  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29576  * to determine which dom element triggers the tooltip.
29577  * 
29578  * It needs to add support for additional attributes like tooltip-position
29579  * 
29580  * @constructor
29581  * Create a new Toolti
29582  * @param {Object} config The config object
29583  */
29584
29585 Roo.bootstrap.Tooltip = function(config){
29586     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29587     
29588     this.alignment = Roo.bootstrap.Tooltip.alignment;
29589     
29590     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29591         this.alignment = config.alignment;
29592     }
29593     
29594 };
29595
29596 Roo.apply(Roo.bootstrap.Tooltip, {
29597     /**
29598      * @function init initialize tooltip monitoring.
29599      * @static
29600      */
29601     currentEl : false,
29602     currentTip : false,
29603     currentRegion : false,
29604     
29605     //  init : delay?
29606     
29607     init : function()
29608     {
29609         Roo.get(document).on('mouseover', this.enter ,this);
29610         Roo.get(document).on('mouseout', this.leave, this);
29611          
29612         
29613         this.currentTip = new Roo.bootstrap.Tooltip();
29614     },
29615     
29616     enter : function(ev)
29617     {
29618         var dom = ev.getTarget();
29619         
29620         //Roo.log(['enter',dom]);
29621         var el = Roo.fly(dom);
29622         if (this.currentEl) {
29623             //Roo.log(dom);
29624             //Roo.log(this.currentEl);
29625             //Roo.log(this.currentEl.contains(dom));
29626             if (this.currentEl == el) {
29627                 return;
29628             }
29629             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29630                 return;
29631             }
29632
29633         }
29634         
29635         if (this.currentTip.el) {
29636             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29637         }    
29638         //Roo.log(ev);
29639         
29640         if(!el || el.dom == document){
29641             return;
29642         }
29643         
29644         var bindEl = el; 
29645         var pel = false;
29646         if (!el.attr('tooltip')) {
29647             pel = el.findParent("[tooltip]");
29648             if (pel) {
29649                 bindEl = Roo.get(pel);
29650             }
29651         }
29652         
29653        
29654         
29655         // you can not look for children, as if el is the body.. then everythign is the child..
29656         if (!pel && !el.attr('tooltip')) { //
29657             if (!el.select("[tooltip]").elements.length) {
29658                 return;
29659             }
29660             // is the mouse over this child...?
29661             bindEl = el.select("[tooltip]").first();
29662             var xy = ev.getXY();
29663             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29664                 //Roo.log("not in region.");
29665                 return;
29666             }
29667             //Roo.log("child element over..");
29668             
29669         }
29670         this.currentEl = el;
29671         this.currentTip.bind(bindEl);
29672         this.currentRegion = Roo.lib.Region.getRegion(dom);
29673         this.currentTip.enter();
29674         
29675     },
29676     leave : function(ev)
29677     {
29678         var dom = ev.getTarget();
29679         //Roo.log(['leave',dom]);
29680         if (!this.currentEl) {
29681             return;
29682         }
29683         
29684         
29685         if (dom != this.currentEl.dom) {
29686             return;
29687         }
29688         var xy = ev.getXY();
29689         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29690             return;
29691         }
29692         // only activate leave if mouse cursor is outside... bounding box..
29693         
29694         
29695         
29696         
29697         if (this.currentTip) {
29698             this.currentTip.leave();
29699         }
29700         //Roo.log('clear currentEl');
29701         this.currentEl = false;
29702         
29703         
29704     },
29705     alignment : {
29706         'left' : ['r-l', [-2,0], 'right'],
29707         'right' : ['l-r', [2,0], 'left'],
29708         'bottom' : ['t-b', [0,2], 'top'],
29709         'top' : [ 'b-t', [0,-2], 'bottom']
29710     }
29711     
29712 });
29713
29714
29715 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29716     
29717     
29718     bindEl : false,
29719     
29720     delay : null, // can be { show : 300 , hide: 500}
29721     
29722     timeout : null,
29723     
29724     hoverState : null, //???
29725     
29726     placement : 'bottom', 
29727     
29728     alignment : false,
29729     
29730     getAutoCreate : function(){
29731     
29732         var cfg = {
29733            cls : 'tooltip',   
29734            role : 'tooltip',
29735            cn : [
29736                 {
29737                     cls : 'tooltip-arrow arrow'
29738                 },
29739                 {
29740                     cls : 'tooltip-inner'
29741                 }
29742            ]
29743         };
29744         
29745         return cfg;
29746     },
29747     bind : function(el)
29748     {
29749         this.bindEl = el;
29750     },
29751     
29752     initEvents : function()
29753     {
29754         this.arrowEl = this.el.select('.arrow', true).first();
29755         this.innerEl = this.el.select('.tooltip-inner', true).first();
29756     },
29757     
29758     enter : function () {
29759        
29760         if (this.timeout != null) {
29761             clearTimeout(this.timeout);
29762         }
29763         
29764         this.hoverState = 'in';
29765          //Roo.log("enter - show");
29766         if (!this.delay || !this.delay.show) {
29767             this.show();
29768             return;
29769         }
29770         var _t = this;
29771         this.timeout = setTimeout(function () {
29772             if (_t.hoverState == 'in') {
29773                 _t.show();
29774             }
29775         }, this.delay.show);
29776     },
29777     leave : function()
29778     {
29779         clearTimeout(this.timeout);
29780     
29781         this.hoverState = 'out';
29782          if (!this.delay || !this.delay.hide) {
29783             this.hide();
29784             return;
29785         }
29786        
29787         var _t = this;
29788         this.timeout = setTimeout(function () {
29789             //Roo.log("leave - timeout");
29790             
29791             if (_t.hoverState == 'out') {
29792                 _t.hide();
29793                 Roo.bootstrap.Tooltip.currentEl = false;
29794             }
29795         }, delay);
29796     },
29797     
29798     show : function (msg)
29799     {
29800         if (!this.el) {
29801             this.render(document.body);
29802         }
29803         // set content.
29804         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29805         
29806         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29807         
29808         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29809         
29810         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29811                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29812         
29813         var placement = typeof this.placement == 'function' ?
29814             this.placement.call(this, this.el, on_el) :
29815             this.placement;
29816             
29817         var autoToken = /\s?auto?\s?/i;
29818         var autoPlace = autoToken.test(placement);
29819         if (autoPlace) {
29820             placement = placement.replace(autoToken, '') || 'top';
29821         }
29822         
29823         //this.el.detach()
29824         //this.el.setXY([0,0]);
29825         this.el.show();
29826         //this.el.dom.style.display='block';
29827         
29828         //this.el.appendTo(on_el);
29829         
29830         var p = this.getPosition();
29831         var box = this.el.getBox();
29832         
29833         if (autoPlace) {
29834             // fixme..
29835         }
29836         
29837         var align = this.alignment[placement];
29838         
29839         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29840         
29841         if(placement == 'top' || placement == 'bottom'){
29842             if(xy[0] < 0){
29843                 placement = 'right';
29844             }
29845             
29846             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29847                 placement = 'left';
29848             }
29849             
29850             var scroll = Roo.select('body', true).first().getScroll();
29851             
29852             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29853                 placement = 'top';
29854             }
29855             
29856             align = this.alignment[placement];
29857             
29858             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29859             
29860         }
29861         
29862         var elems = document.getElementsByTagName('div');
29863         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29864         for (var i = 0; i < elems.length; i++) {
29865           var zindex = Number.parseInt(
29866                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29867                 10
29868           );
29869           if (zindex > highest) {
29870             highest = zindex;
29871           }
29872         }
29873         
29874         
29875         
29876         this.el.dom.style.zIndex = highest;
29877         
29878         this.el.alignTo(this.bindEl, align[0],align[1]);
29879         //var arrow = this.el.select('.arrow',true).first();
29880         //arrow.set(align[2], 
29881         
29882         this.el.addClass(placement);
29883         this.el.addClass("bs-tooltip-"+ placement);
29884         
29885         this.el.addClass('in fade show');
29886         
29887         this.hoverState = null;
29888         
29889         if (this.el.hasClass('fade')) {
29890             // fade it?
29891         }
29892         
29893         
29894         
29895         
29896         
29897     },
29898     hide : function()
29899     {
29900          
29901         if (!this.el) {
29902             return;
29903         }
29904         //this.el.setXY([0,0]);
29905         this.el.removeClass(['show', 'in']);
29906         //this.el.hide();
29907         
29908     }
29909     
29910 });
29911  
29912
29913  /*
29914  * - LGPL
29915  *
29916  * Location Picker
29917  * 
29918  */
29919
29920 /**
29921  * @class Roo.bootstrap.LocationPicker
29922  * @extends Roo.bootstrap.Component
29923  * Bootstrap LocationPicker class
29924  * @cfg {Number} latitude Position when init default 0
29925  * @cfg {Number} longitude Position when init default 0
29926  * @cfg {Number} zoom default 15
29927  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29928  * @cfg {Boolean} mapTypeControl default false
29929  * @cfg {Boolean} disableDoubleClickZoom default false
29930  * @cfg {Boolean} scrollwheel default true
29931  * @cfg {Boolean} streetViewControl default false
29932  * @cfg {Number} radius default 0
29933  * @cfg {String} locationName
29934  * @cfg {Boolean} draggable default true
29935  * @cfg {Boolean} enableAutocomplete default false
29936  * @cfg {Boolean} enableReverseGeocode default true
29937  * @cfg {String} markerTitle
29938  * 
29939  * @constructor
29940  * Create a new LocationPicker
29941  * @param {Object} config The config object
29942  */
29943
29944
29945 Roo.bootstrap.LocationPicker = function(config){
29946     
29947     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29948     
29949     this.addEvents({
29950         /**
29951          * @event initial
29952          * Fires when the picker initialized.
29953          * @param {Roo.bootstrap.LocationPicker} this
29954          * @param {Google Location} location
29955          */
29956         initial : true,
29957         /**
29958          * @event positionchanged
29959          * Fires when the picker position changed.
29960          * @param {Roo.bootstrap.LocationPicker} this
29961          * @param {Google Location} location
29962          */
29963         positionchanged : true,
29964         /**
29965          * @event resize
29966          * Fires when the map resize.
29967          * @param {Roo.bootstrap.LocationPicker} this
29968          */
29969         resize : true,
29970         /**
29971          * @event show
29972          * Fires when the map show.
29973          * @param {Roo.bootstrap.LocationPicker} this
29974          */
29975         show : true,
29976         /**
29977          * @event hide
29978          * Fires when the map hide.
29979          * @param {Roo.bootstrap.LocationPicker} this
29980          */
29981         hide : true,
29982         /**
29983          * @event mapClick
29984          * Fires when click the map.
29985          * @param {Roo.bootstrap.LocationPicker} this
29986          * @param {Map event} e
29987          */
29988         mapClick : true,
29989         /**
29990          * @event mapRightClick
29991          * Fires when right click the map.
29992          * @param {Roo.bootstrap.LocationPicker} this
29993          * @param {Map event} e
29994          */
29995         mapRightClick : true,
29996         /**
29997          * @event markerClick
29998          * Fires when click the marker.
29999          * @param {Roo.bootstrap.LocationPicker} this
30000          * @param {Map event} e
30001          */
30002         markerClick : true,
30003         /**
30004          * @event markerRightClick
30005          * Fires when right click the marker.
30006          * @param {Roo.bootstrap.LocationPicker} this
30007          * @param {Map event} e
30008          */
30009         markerRightClick : true,
30010         /**
30011          * @event OverlayViewDraw
30012          * Fires when OverlayView Draw
30013          * @param {Roo.bootstrap.LocationPicker} this
30014          */
30015         OverlayViewDraw : true,
30016         /**
30017          * @event OverlayViewOnAdd
30018          * Fires when OverlayView Draw
30019          * @param {Roo.bootstrap.LocationPicker} this
30020          */
30021         OverlayViewOnAdd : true,
30022         /**
30023          * @event OverlayViewOnRemove
30024          * Fires when OverlayView Draw
30025          * @param {Roo.bootstrap.LocationPicker} this
30026          */
30027         OverlayViewOnRemove : true,
30028         /**
30029          * @event OverlayViewShow
30030          * Fires when OverlayView Draw
30031          * @param {Roo.bootstrap.LocationPicker} this
30032          * @param {Pixel} cpx
30033          */
30034         OverlayViewShow : true,
30035         /**
30036          * @event OverlayViewHide
30037          * Fires when OverlayView Draw
30038          * @param {Roo.bootstrap.LocationPicker} this
30039          */
30040         OverlayViewHide : true,
30041         /**
30042          * @event loadexception
30043          * Fires when load google lib failed.
30044          * @param {Roo.bootstrap.LocationPicker} this
30045          */
30046         loadexception : true
30047     });
30048         
30049 };
30050
30051 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30052     
30053     gMapContext: false,
30054     
30055     latitude: 0,
30056     longitude: 0,
30057     zoom: 15,
30058     mapTypeId: false,
30059     mapTypeControl: false,
30060     disableDoubleClickZoom: false,
30061     scrollwheel: true,
30062     streetViewControl: false,
30063     radius: 0,
30064     locationName: '',
30065     draggable: true,
30066     enableAutocomplete: false,
30067     enableReverseGeocode: true,
30068     markerTitle: '',
30069     
30070     getAutoCreate: function()
30071     {
30072
30073         var cfg = {
30074             tag: 'div',
30075             cls: 'roo-location-picker'
30076         };
30077         
30078         return cfg
30079     },
30080     
30081     initEvents: function(ct, position)
30082     {       
30083         if(!this.el.getWidth() || this.isApplied()){
30084             return;
30085         }
30086         
30087         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30088         
30089         this.initial();
30090     },
30091     
30092     initial: function()
30093     {
30094         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30095             this.fireEvent('loadexception', this);
30096             return;
30097         }
30098         
30099         if(!this.mapTypeId){
30100             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30101         }
30102         
30103         this.gMapContext = this.GMapContext();
30104         
30105         this.initOverlayView();
30106         
30107         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30108         
30109         var _this = this;
30110                 
30111         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30112             _this.setPosition(_this.gMapContext.marker.position);
30113         });
30114         
30115         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30116             _this.fireEvent('mapClick', this, event);
30117             
30118         });
30119
30120         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30121             _this.fireEvent('mapRightClick', this, event);
30122             
30123         });
30124         
30125         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30126             _this.fireEvent('markerClick', this, event);
30127             
30128         });
30129
30130         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30131             _this.fireEvent('markerRightClick', this, event);
30132             
30133         });
30134         
30135         this.setPosition(this.gMapContext.location);
30136         
30137         this.fireEvent('initial', this, this.gMapContext.location);
30138     },
30139     
30140     initOverlayView: function()
30141     {
30142         var _this = this;
30143         
30144         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30145             
30146             draw: function()
30147             {
30148                 _this.fireEvent('OverlayViewDraw', _this);
30149             },
30150             
30151             onAdd: function()
30152             {
30153                 _this.fireEvent('OverlayViewOnAdd', _this);
30154             },
30155             
30156             onRemove: function()
30157             {
30158                 _this.fireEvent('OverlayViewOnRemove', _this);
30159             },
30160             
30161             show: function(cpx)
30162             {
30163                 _this.fireEvent('OverlayViewShow', _this, cpx);
30164             },
30165             
30166             hide: function()
30167             {
30168                 _this.fireEvent('OverlayViewHide', _this);
30169             }
30170             
30171         });
30172     },
30173     
30174     fromLatLngToContainerPixel: function(event)
30175     {
30176         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30177     },
30178     
30179     isApplied: function() 
30180     {
30181         return this.getGmapContext() == false ? false : true;
30182     },
30183     
30184     getGmapContext: function() 
30185     {
30186         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30187     },
30188     
30189     GMapContext: function() 
30190     {
30191         var position = new google.maps.LatLng(this.latitude, this.longitude);
30192         
30193         var _map = new google.maps.Map(this.el.dom, {
30194             center: position,
30195             zoom: this.zoom,
30196             mapTypeId: this.mapTypeId,
30197             mapTypeControl: this.mapTypeControl,
30198             disableDoubleClickZoom: this.disableDoubleClickZoom,
30199             scrollwheel: this.scrollwheel,
30200             streetViewControl: this.streetViewControl,
30201             locationName: this.locationName,
30202             draggable: this.draggable,
30203             enableAutocomplete: this.enableAutocomplete,
30204             enableReverseGeocode: this.enableReverseGeocode
30205         });
30206         
30207         var _marker = new google.maps.Marker({
30208             position: position,
30209             map: _map,
30210             title: this.markerTitle,
30211             draggable: this.draggable
30212         });
30213         
30214         return {
30215             map: _map,
30216             marker: _marker,
30217             circle: null,
30218             location: position,
30219             radius: this.radius,
30220             locationName: this.locationName,
30221             addressComponents: {
30222                 formatted_address: null,
30223                 addressLine1: null,
30224                 addressLine2: null,
30225                 streetName: null,
30226                 streetNumber: null,
30227                 city: null,
30228                 district: null,
30229                 state: null,
30230                 stateOrProvince: null
30231             },
30232             settings: this,
30233             domContainer: this.el.dom,
30234             geodecoder: new google.maps.Geocoder()
30235         };
30236     },
30237     
30238     drawCircle: function(center, radius, options) 
30239     {
30240         if (this.gMapContext.circle != null) {
30241             this.gMapContext.circle.setMap(null);
30242         }
30243         if (radius > 0) {
30244             radius *= 1;
30245             options = Roo.apply({}, options, {
30246                 strokeColor: "#0000FF",
30247                 strokeOpacity: .35,
30248                 strokeWeight: 2,
30249                 fillColor: "#0000FF",
30250                 fillOpacity: .2
30251             });
30252             
30253             options.map = this.gMapContext.map;
30254             options.radius = radius;
30255             options.center = center;
30256             this.gMapContext.circle = new google.maps.Circle(options);
30257             return this.gMapContext.circle;
30258         }
30259         
30260         return null;
30261     },
30262     
30263     setPosition: function(location) 
30264     {
30265         this.gMapContext.location = location;
30266         this.gMapContext.marker.setPosition(location);
30267         this.gMapContext.map.panTo(location);
30268         this.drawCircle(location, this.gMapContext.radius, {});
30269         
30270         var _this = this;
30271         
30272         if (this.gMapContext.settings.enableReverseGeocode) {
30273             this.gMapContext.geodecoder.geocode({
30274                 latLng: this.gMapContext.location
30275             }, function(results, status) {
30276                 
30277                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30278                     _this.gMapContext.locationName = results[0].formatted_address;
30279                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30280                     
30281                     _this.fireEvent('positionchanged', this, location);
30282                 }
30283             });
30284             
30285             return;
30286         }
30287         
30288         this.fireEvent('positionchanged', this, location);
30289     },
30290     
30291     resize: function()
30292     {
30293         google.maps.event.trigger(this.gMapContext.map, "resize");
30294         
30295         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30296         
30297         this.fireEvent('resize', this);
30298     },
30299     
30300     setPositionByLatLng: function(latitude, longitude)
30301     {
30302         this.setPosition(new google.maps.LatLng(latitude, longitude));
30303     },
30304     
30305     getCurrentPosition: function() 
30306     {
30307         return {
30308             latitude: this.gMapContext.location.lat(),
30309             longitude: this.gMapContext.location.lng()
30310         };
30311     },
30312     
30313     getAddressName: function() 
30314     {
30315         return this.gMapContext.locationName;
30316     },
30317     
30318     getAddressComponents: function() 
30319     {
30320         return this.gMapContext.addressComponents;
30321     },
30322     
30323     address_component_from_google_geocode: function(address_components) 
30324     {
30325         var result = {};
30326         
30327         for (var i = 0; i < address_components.length; i++) {
30328             var component = address_components[i];
30329             if (component.types.indexOf("postal_code") >= 0) {
30330                 result.postalCode = component.short_name;
30331             } else if (component.types.indexOf("street_number") >= 0) {
30332                 result.streetNumber = component.short_name;
30333             } else if (component.types.indexOf("route") >= 0) {
30334                 result.streetName = component.short_name;
30335             } else if (component.types.indexOf("neighborhood") >= 0) {
30336                 result.city = component.short_name;
30337             } else if (component.types.indexOf("locality") >= 0) {
30338                 result.city = component.short_name;
30339             } else if (component.types.indexOf("sublocality") >= 0) {
30340                 result.district = component.short_name;
30341             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30342                 result.stateOrProvince = component.short_name;
30343             } else if (component.types.indexOf("country") >= 0) {
30344                 result.country = component.short_name;
30345             }
30346         }
30347         
30348         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30349         result.addressLine2 = "";
30350         return result;
30351     },
30352     
30353     setZoomLevel: function(zoom)
30354     {
30355         this.gMapContext.map.setZoom(zoom);
30356     },
30357     
30358     show: function()
30359     {
30360         if(!this.el){
30361             return;
30362         }
30363         
30364         this.el.show();
30365         
30366         this.resize();
30367         
30368         this.fireEvent('show', this);
30369     },
30370     
30371     hide: function()
30372     {
30373         if(!this.el){
30374             return;
30375         }
30376         
30377         this.el.hide();
30378         
30379         this.fireEvent('hide', this);
30380     }
30381     
30382 });
30383
30384 Roo.apply(Roo.bootstrap.LocationPicker, {
30385     
30386     OverlayView : function(map, options)
30387     {
30388         options = options || {};
30389         
30390         this.setMap(map);
30391     }
30392     
30393     
30394 });/**
30395  * @class Roo.bootstrap.Alert
30396  * @extends Roo.bootstrap.Component
30397  * Bootstrap Alert class - shows an alert area box
30398  * eg
30399  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30400   Enter a valid email address
30401 </div>
30402  * @licence LGPL
30403  * @cfg {String} title The title of alert
30404  * @cfg {String} html The content of alert
30405  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30406  * @cfg {String} fa font-awesomeicon
30407  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30408  * @cfg {Boolean} close true to show a x closer
30409  * 
30410  * 
30411  * @constructor
30412  * Create a new alert
30413  * @param {Object} config The config object
30414  */
30415
30416
30417 Roo.bootstrap.Alert = function(config){
30418     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30419     
30420 };
30421
30422 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30423     
30424     title: '',
30425     html: '',
30426     weight: false,
30427     fa: false,
30428     faicon: false, // BC
30429     close : false,
30430     
30431     
30432     getAutoCreate : function()
30433     {
30434         
30435         var cfg = {
30436             tag : 'div',
30437             cls : 'alert',
30438             cn : [
30439                 {
30440                     tag: 'button',
30441                     type :  "button",
30442                     cls: "close",
30443                     html : '×',
30444                     style : this.close ? '' : 'display:none'
30445                 },
30446                 {
30447                     tag : 'i',
30448                     cls : 'roo-alert-icon'
30449                     
30450                 },
30451                 {
30452                     tag : 'b',
30453                     cls : 'roo-alert-title',
30454                     html : this.title
30455                 },
30456                 {
30457                     tag : 'span',
30458                     cls : 'roo-alert-text',
30459                     html : this.html
30460                 }
30461             ]
30462         };
30463         
30464         if(this.faicon){
30465             cfg.cn[0].cls += ' fa ' + this.faicon;
30466         }
30467         if(this.fa){
30468             cfg.cn[0].cls += ' fa ' + this.fa;
30469         }
30470         
30471         if(this.weight){
30472             cfg.cls += ' alert-' + this.weight;
30473         }
30474         
30475         return cfg;
30476     },
30477     
30478     initEvents: function() 
30479     {
30480         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30481         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30482         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30483         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30484         if (this.seconds > 0) {
30485             this.hide.defer(this.seconds, this);
30486         }
30487     },
30488     /**
30489      * Set the Title Message HTML
30490      * @param {String} html
30491      */
30492     setTitle : function(str)
30493     {
30494         this.titleEl.dom.innerHTML = str;
30495     },
30496      
30497      /**
30498      * Set the Body Message HTML
30499      * @param {String} html
30500      */
30501     setHtml : function(str)
30502     {
30503         this.htmlEl.dom.innerHTML = str;
30504     },
30505     /**
30506      * Set the Weight of the alert
30507      * @param {String} (success|info|warning|danger) weight
30508      */
30509     
30510     setWeight : function(weight)
30511     {
30512         if(this.weight){
30513             this.el.removeClass('alert-' + this.weight);
30514         }
30515         
30516         this.weight = weight;
30517         
30518         this.el.addClass('alert-' + this.weight);
30519     },
30520       /**
30521      * Set the Icon of the alert
30522      * @param {String} see fontawsome names (name without the 'fa-' bit)
30523      */
30524     setIcon : function(icon)
30525     {
30526         if(this.faicon){
30527             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30528         }
30529         
30530         this.faicon = icon;
30531         
30532         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30533     },
30534     /**
30535      * Hide the Alert
30536      */
30537     hide: function() 
30538     {
30539         this.el.hide();   
30540     },
30541     /**
30542      * Show the Alert
30543      */
30544     show: function() 
30545     {  
30546         this.el.show();   
30547     }
30548     
30549 });
30550
30551  
30552 /*
30553 * Licence: LGPL
30554 */
30555
30556 /**
30557  * @class Roo.bootstrap.UploadCropbox
30558  * @extends Roo.bootstrap.Component
30559  * Bootstrap UploadCropbox class
30560  * @cfg {String} emptyText show when image has been loaded
30561  * @cfg {String} rotateNotify show when image too small to rotate
30562  * @cfg {Number} errorTimeout default 3000
30563  * @cfg {Number} minWidth default 300
30564  * @cfg {Number} minHeight default 300
30565  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30566  * @cfg {Boolean} isDocument (true|false) default false
30567  * @cfg {String} url action url
30568  * @cfg {String} paramName default 'imageUpload'
30569  * @cfg {String} method default POST
30570  * @cfg {Boolean} loadMask (true|false) default true
30571  * @cfg {Boolean} loadingText default 'Loading...'
30572  * 
30573  * @constructor
30574  * Create a new UploadCropbox
30575  * @param {Object} config The config object
30576  */
30577
30578 Roo.bootstrap.UploadCropbox = function(config){
30579     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30580     
30581     this.addEvents({
30582         /**
30583          * @event beforeselectfile
30584          * Fire before select file
30585          * @param {Roo.bootstrap.UploadCropbox} this
30586          */
30587         "beforeselectfile" : true,
30588         /**
30589          * @event initial
30590          * Fire after initEvent
30591          * @param {Roo.bootstrap.UploadCropbox} this
30592          */
30593         "initial" : true,
30594         /**
30595          * @event crop
30596          * Fire after initEvent
30597          * @param {Roo.bootstrap.UploadCropbox} this
30598          * @param {String} data
30599          */
30600         "crop" : true,
30601         /**
30602          * @event prepare
30603          * Fire when preparing the file data
30604          * @param {Roo.bootstrap.UploadCropbox} this
30605          * @param {Object} file
30606          */
30607         "prepare" : true,
30608         /**
30609          * @event exception
30610          * Fire when get exception
30611          * @param {Roo.bootstrap.UploadCropbox} this
30612          * @param {XMLHttpRequest} xhr
30613          */
30614         "exception" : true,
30615         /**
30616          * @event beforeloadcanvas
30617          * Fire before load the canvas
30618          * @param {Roo.bootstrap.UploadCropbox} this
30619          * @param {String} src
30620          */
30621         "beforeloadcanvas" : true,
30622         /**
30623          * @event trash
30624          * Fire when trash image
30625          * @param {Roo.bootstrap.UploadCropbox} this
30626          */
30627         "trash" : true,
30628         /**
30629          * @event download
30630          * Fire when download the image
30631          * @param {Roo.bootstrap.UploadCropbox} this
30632          */
30633         "download" : true,
30634         /**
30635          * @event footerbuttonclick
30636          * Fire when footerbuttonclick
30637          * @param {Roo.bootstrap.UploadCropbox} this
30638          * @param {String} type
30639          */
30640         "footerbuttonclick" : true,
30641         /**
30642          * @event resize
30643          * Fire when resize
30644          * @param {Roo.bootstrap.UploadCropbox} this
30645          */
30646         "resize" : true,
30647         /**
30648          * @event rotate
30649          * Fire when rotate the image
30650          * @param {Roo.bootstrap.UploadCropbox} this
30651          * @param {String} pos
30652          */
30653         "rotate" : true,
30654         /**
30655          * @event inspect
30656          * Fire when inspect the file
30657          * @param {Roo.bootstrap.UploadCropbox} this
30658          * @param {Object} file
30659          */
30660         "inspect" : true,
30661         /**
30662          * @event upload
30663          * Fire when xhr upload the file
30664          * @param {Roo.bootstrap.UploadCropbox} this
30665          * @param {Object} data
30666          */
30667         "upload" : true,
30668         /**
30669          * @event arrange
30670          * Fire when arrange the file data
30671          * @param {Roo.bootstrap.UploadCropbox} this
30672          * @param {Object} formData
30673          */
30674         "arrange" : true
30675     });
30676     
30677     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30678 };
30679
30680 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30681     
30682     emptyText : 'Click to upload image',
30683     rotateNotify : 'Image is too small to rotate',
30684     errorTimeout : 3000,
30685     scale : 0,
30686     baseScale : 1,
30687     rotate : 0,
30688     dragable : false,
30689     pinching : false,
30690     mouseX : 0,
30691     mouseY : 0,
30692     cropData : false,
30693     minWidth : 300,
30694     minHeight : 300,
30695     file : false,
30696     exif : {},
30697     baseRotate : 1,
30698     cropType : 'image/jpeg',
30699     buttons : false,
30700     canvasLoaded : false,
30701     isDocument : false,
30702     method : 'POST',
30703     paramName : 'imageUpload',
30704     loadMask : true,
30705     loadingText : 'Loading...',
30706     maskEl : false,
30707     
30708     getAutoCreate : function()
30709     {
30710         var cfg = {
30711             tag : 'div',
30712             cls : 'roo-upload-cropbox',
30713             cn : [
30714                 {
30715                     tag : 'input',
30716                     cls : 'roo-upload-cropbox-selector',
30717                     type : 'file'
30718                 },
30719                 {
30720                     tag : 'div',
30721                     cls : 'roo-upload-cropbox-body',
30722                     style : 'cursor:pointer',
30723                     cn : [
30724                         {
30725                             tag : 'div',
30726                             cls : 'roo-upload-cropbox-preview'
30727                         },
30728                         {
30729                             tag : 'div',
30730                             cls : 'roo-upload-cropbox-thumb'
30731                         },
30732                         {
30733                             tag : 'div',
30734                             cls : 'roo-upload-cropbox-empty-notify',
30735                             html : this.emptyText
30736                         },
30737                         {
30738                             tag : 'div',
30739                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30740                             html : this.rotateNotify
30741                         }
30742                     ]
30743                 },
30744                 {
30745                     tag : 'div',
30746                     cls : 'roo-upload-cropbox-footer',
30747                     cn : {
30748                         tag : 'div',
30749                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30750                         cn : []
30751                     }
30752                 }
30753             ]
30754         };
30755         
30756         return cfg;
30757     },
30758     
30759     onRender : function(ct, position)
30760     {
30761         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30762         
30763         if (this.buttons.length) {
30764             
30765             Roo.each(this.buttons, function(bb) {
30766                 
30767                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30768                 
30769                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30770                 
30771             }, this);
30772         }
30773         
30774         if(this.loadMask){
30775             this.maskEl = this.el;
30776         }
30777     },
30778     
30779     initEvents : function()
30780     {
30781         this.urlAPI = (window.createObjectURL && window) || 
30782                                 (window.URL && URL.revokeObjectURL && URL) || 
30783                                 (window.webkitURL && webkitURL);
30784                         
30785         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30786         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30787         
30788         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30789         this.selectorEl.hide();
30790         
30791         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30792         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30793         
30794         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30795         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30796         this.thumbEl.hide();
30797         
30798         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30799         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30800         
30801         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30802         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30803         this.errorEl.hide();
30804         
30805         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30806         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30807         this.footerEl.hide();
30808         
30809         this.setThumbBoxSize();
30810         
30811         this.bind();
30812         
30813         this.resize();
30814         
30815         this.fireEvent('initial', this);
30816     },
30817
30818     bind : function()
30819     {
30820         var _this = this;
30821         
30822         window.addEventListener("resize", function() { _this.resize(); } );
30823         
30824         this.bodyEl.on('click', this.beforeSelectFile, this);
30825         
30826         if(Roo.isTouch){
30827             this.bodyEl.on('touchstart', this.onTouchStart, this);
30828             this.bodyEl.on('touchmove', this.onTouchMove, this);
30829             this.bodyEl.on('touchend', this.onTouchEnd, this);
30830         }
30831         
30832         if(!Roo.isTouch){
30833             this.bodyEl.on('mousedown', this.onMouseDown, this);
30834             this.bodyEl.on('mousemove', this.onMouseMove, this);
30835             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30836             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30837             Roo.get(document).on('mouseup', this.onMouseUp, this);
30838         }
30839         
30840         this.selectorEl.on('change', this.onFileSelected, this);
30841     },
30842     
30843     reset : function()
30844     {    
30845         this.scale = 0;
30846         this.baseScale = 1;
30847         this.rotate = 0;
30848         this.baseRotate = 1;
30849         this.dragable = false;
30850         this.pinching = false;
30851         this.mouseX = 0;
30852         this.mouseY = 0;
30853         this.cropData = false;
30854         this.notifyEl.dom.innerHTML = this.emptyText;
30855         
30856         this.selectorEl.dom.value = '';
30857         
30858     },
30859     
30860     resize : function()
30861     {
30862         if(this.fireEvent('resize', this) != false){
30863             this.setThumbBoxPosition();
30864             this.setCanvasPosition();
30865         }
30866     },
30867     
30868     onFooterButtonClick : function(e, el, o, type)
30869     {
30870         switch (type) {
30871             case 'rotate-left' :
30872                 this.onRotateLeft(e);
30873                 break;
30874             case 'rotate-right' :
30875                 this.onRotateRight(e);
30876                 break;
30877             case 'picture' :
30878                 this.beforeSelectFile(e);
30879                 break;
30880             case 'trash' :
30881                 this.trash(e);
30882                 break;
30883             case 'crop' :
30884                 this.crop(e);
30885                 break;
30886             case 'download' :
30887                 this.download(e);
30888                 break;
30889             default :
30890                 break;
30891         }
30892         
30893         this.fireEvent('footerbuttonclick', this, type);
30894     },
30895     
30896     beforeSelectFile : function(e)
30897     {
30898         e.preventDefault();
30899         
30900         if(this.fireEvent('beforeselectfile', this) != false){
30901             this.selectorEl.dom.click();
30902         }
30903     },
30904     
30905     onFileSelected : function(e)
30906     {
30907         e.preventDefault();
30908         
30909         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30910             return;
30911         }
30912         
30913         var file = this.selectorEl.dom.files[0];
30914         
30915         if(this.fireEvent('inspect', this, file) != false){
30916             this.prepare(file);
30917         }
30918         
30919     },
30920     
30921     trash : function(e)
30922     {
30923         this.fireEvent('trash', this);
30924     },
30925     
30926     download : function(e)
30927     {
30928         this.fireEvent('download', this);
30929     },
30930     
30931     loadCanvas : function(src)
30932     {   
30933         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30934             
30935             this.reset();
30936             
30937             this.imageEl = document.createElement('img');
30938             
30939             var _this = this;
30940             
30941             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30942             
30943             this.imageEl.src = src;
30944         }
30945     },
30946     
30947     onLoadCanvas : function()
30948     {   
30949         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30950         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30951         
30952         this.bodyEl.un('click', this.beforeSelectFile, this);
30953         
30954         this.notifyEl.hide();
30955         this.thumbEl.show();
30956         this.footerEl.show();
30957         
30958         this.baseRotateLevel();
30959         
30960         if(this.isDocument){
30961             this.setThumbBoxSize();
30962         }
30963         
30964         this.setThumbBoxPosition();
30965         
30966         this.baseScaleLevel();
30967         
30968         this.draw();
30969         
30970         this.resize();
30971         
30972         this.canvasLoaded = true;
30973         
30974         if(this.loadMask){
30975             this.maskEl.unmask();
30976         }
30977         
30978     },
30979     
30980     setCanvasPosition : function()
30981     {   
30982         if(!this.canvasEl){
30983             return;
30984         }
30985         
30986         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30987         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30988         
30989         this.previewEl.setLeft(pw);
30990         this.previewEl.setTop(ph);
30991         
30992     },
30993     
30994     onMouseDown : function(e)
30995     {   
30996         e.stopEvent();
30997         
30998         this.dragable = true;
30999         this.pinching = false;
31000         
31001         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31002             this.dragable = false;
31003             return;
31004         }
31005         
31006         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31007         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31008         
31009     },
31010     
31011     onMouseMove : function(e)
31012     {   
31013         e.stopEvent();
31014         
31015         if(!this.canvasLoaded){
31016             return;
31017         }
31018         
31019         if (!this.dragable){
31020             return;
31021         }
31022         
31023         var minX = Math.ceil(this.thumbEl.getLeft(true));
31024         var minY = Math.ceil(this.thumbEl.getTop(true));
31025         
31026         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31027         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31028         
31029         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31030         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31031         
31032         x = x - this.mouseX;
31033         y = y - this.mouseY;
31034         
31035         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31036         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31037         
31038         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31039         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31040         
31041         this.previewEl.setLeft(bgX);
31042         this.previewEl.setTop(bgY);
31043         
31044         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31045         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31046     },
31047     
31048     onMouseUp : function(e)
31049     {   
31050         e.stopEvent();
31051         
31052         this.dragable = false;
31053     },
31054     
31055     onMouseWheel : function(e)
31056     {   
31057         e.stopEvent();
31058         
31059         this.startScale = this.scale;
31060         
31061         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31062         
31063         if(!this.zoomable()){
31064             this.scale = this.startScale;
31065             return;
31066         }
31067         
31068         this.draw();
31069         
31070         return;
31071     },
31072     
31073     zoomable : function()
31074     {
31075         var minScale = this.thumbEl.getWidth() / this.minWidth;
31076         
31077         if(this.minWidth < this.minHeight){
31078             minScale = this.thumbEl.getHeight() / this.minHeight;
31079         }
31080         
31081         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31082         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31083         
31084         if(
31085                 this.isDocument &&
31086                 (this.rotate == 0 || this.rotate == 180) && 
31087                 (
31088                     width > this.imageEl.OriginWidth || 
31089                     height > this.imageEl.OriginHeight ||
31090                     (width < this.minWidth && height < this.minHeight)
31091                 )
31092         ){
31093             return false;
31094         }
31095         
31096         if(
31097                 this.isDocument &&
31098                 (this.rotate == 90 || this.rotate == 270) && 
31099                 (
31100                     width > this.imageEl.OriginWidth || 
31101                     height > this.imageEl.OriginHeight ||
31102                     (width < this.minHeight && height < this.minWidth)
31103                 )
31104         ){
31105             return false;
31106         }
31107         
31108         if(
31109                 !this.isDocument &&
31110                 (this.rotate == 0 || this.rotate == 180) && 
31111                 (
31112                     width < this.minWidth || 
31113                     width > this.imageEl.OriginWidth || 
31114                     height < this.minHeight || 
31115                     height > this.imageEl.OriginHeight
31116                 )
31117         ){
31118             return false;
31119         }
31120         
31121         if(
31122                 !this.isDocument &&
31123                 (this.rotate == 90 || this.rotate == 270) && 
31124                 (
31125                     width < this.minHeight || 
31126                     width > this.imageEl.OriginWidth || 
31127                     height < this.minWidth || 
31128                     height > this.imageEl.OriginHeight
31129                 )
31130         ){
31131             return false;
31132         }
31133         
31134         return true;
31135         
31136     },
31137     
31138     onRotateLeft : function(e)
31139     {   
31140         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31141             
31142             var minScale = this.thumbEl.getWidth() / this.minWidth;
31143             
31144             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31145             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31146             
31147             this.startScale = this.scale;
31148             
31149             while (this.getScaleLevel() < minScale){
31150             
31151                 this.scale = this.scale + 1;
31152                 
31153                 if(!this.zoomable()){
31154                     break;
31155                 }
31156                 
31157                 if(
31158                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31159                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31160                 ){
31161                     continue;
31162                 }
31163                 
31164                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31165
31166                 this.draw();
31167                 
31168                 return;
31169             }
31170             
31171             this.scale = this.startScale;
31172             
31173             this.onRotateFail();
31174             
31175             return false;
31176         }
31177         
31178         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31179
31180         if(this.isDocument){
31181             this.setThumbBoxSize();
31182             this.setThumbBoxPosition();
31183             this.setCanvasPosition();
31184         }
31185         
31186         this.draw();
31187         
31188         this.fireEvent('rotate', this, 'left');
31189         
31190     },
31191     
31192     onRotateRight : function(e)
31193     {
31194         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31195             
31196             var minScale = this.thumbEl.getWidth() / this.minWidth;
31197         
31198             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31199             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31200             
31201             this.startScale = this.scale;
31202             
31203             while (this.getScaleLevel() < minScale){
31204             
31205                 this.scale = this.scale + 1;
31206                 
31207                 if(!this.zoomable()){
31208                     break;
31209                 }
31210                 
31211                 if(
31212                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31213                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31214                 ){
31215                     continue;
31216                 }
31217                 
31218                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31219
31220                 this.draw();
31221                 
31222                 return;
31223             }
31224             
31225             this.scale = this.startScale;
31226             
31227             this.onRotateFail();
31228             
31229             return false;
31230         }
31231         
31232         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31233
31234         if(this.isDocument){
31235             this.setThumbBoxSize();
31236             this.setThumbBoxPosition();
31237             this.setCanvasPosition();
31238         }
31239         
31240         this.draw();
31241         
31242         this.fireEvent('rotate', this, 'right');
31243     },
31244     
31245     onRotateFail : function()
31246     {
31247         this.errorEl.show(true);
31248         
31249         var _this = this;
31250         
31251         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31252     },
31253     
31254     draw : function()
31255     {
31256         this.previewEl.dom.innerHTML = '';
31257         
31258         var canvasEl = document.createElement("canvas");
31259         
31260         var contextEl = canvasEl.getContext("2d");
31261         
31262         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31263         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31264         var center = this.imageEl.OriginWidth / 2;
31265         
31266         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31267             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31268             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31269             center = this.imageEl.OriginHeight / 2;
31270         }
31271         
31272         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31273         
31274         contextEl.translate(center, center);
31275         contextEl.rotate(this.rotate * Math.PI / 180);
31276
31277         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31278         
31279         this.canvasEl = document.createElement("canvas");
31280         
31281         this.contextEl = this.canvasEl.getContext("2d");
31282         
31283         switch (this.rotate) {
31284             case 0 :
31285                 
31286                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31287                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31288                 
31289                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31290                 
31291                 break;
31292             case 90 : 
31293                 
31294                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31295                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31296                 
31297                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31298                     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);
31299                     break;
31300                 }
31301                 
31302                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31303                 
31304                 break;
31305             case 180 :
31306                 
31307                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31308                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31309                 
31310                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31311                     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);
31312                     break;
31313                 }
31314                 
31315                 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);
31316                 
31317                 break;
31318             case 270 :
31319                 
31320                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31321                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31322         
31323                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31324                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31325                     break;
31326                 }
31327                 
31328                 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);
31329                 
31330                 break;
31331             default : 
31332                 break;
31333         }
31334         
31335         this.previewEl.appendChild(this.canvasEl);
31336         
31337         this.setCanvasPosition();
31338     },
31339     
31340     crop : function()
31341     {
31342         if(!this.canvasLoaded){
31343             return;
31344         }
31345         
31346         var imageCanvas = document.createElement("canvas");
31347         
31348         var imageContext = imageCanvas.getContext("2d");
31349         
31350         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31351         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31352         
31353         var center = imageCanvas.width / 2;
31354         
31355         imageContext.translate(center, center);
31356         
31357         imageContext.rotate(this.rotate * Math.PI / 180);
31358         
31359         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31360         
31361         var canvas = document.createElement("canvas");
31362         
31363         var context = canvas.getContext("2d");
31364                 
31365         canvas.width = this.minWidth;
31366         canvas.height = this.minHeight;
31367
31368         switch (this.rotate) {
31369             case 0 :
31370                 
31371                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31372                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31373                 
31374                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31375                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31376                 
31377                 var targetWidth = this.minWidth - 2 * x;
31378                 var targetHeight = this.minHeight - 2 * y;
31379                 
31380                 var scale = 1;
31381                 
31382                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31383                     scale = targetWidth / width;
31384                 }
31385                 
31386                 if(x > 0 && y == 0){
31387                     scale = targetHeight / height;
31388                 }
31389                 
31390                 if(x > 0 && y > 0){
31391                     scale = targetWidth / width;
31392                     
31393                     if(width < height){
31394                         scale = targetHeight / height;
31395                     }
31396                 }
31397                 
31398                 context.scale(scale, scale);
31399                 
31400                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31401                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31402
31403                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31404                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31405
31406                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31407                 
31408                 break;
31409             case 90 : 
31410                 
31411                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31412                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31413                 
31414                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31415                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31416                 
31417                 var targetWidth = this.minWidth - 2 * x;
31418                 var targetHeight = this.minHeight - 2 * y;
31419                 
31420                 var scale = 1;
31421                 
31422                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31423                     scale = targetWidth / width;
31424                 }
31425                 
31426                 if(x > 0 && y == 0){
31427                     scale = targetHeight / height;
31428                 }
31429                 
31430                 if(x > 0 && y > 0){
31431                     scale = targetWidth / width;
31432                     
31433                     if(width < height){
31434                         scale = targetHeight / height;
31435                     }
31436                 }
31437                 
31438                 context.scale(scale, scale);
31439                 
31440                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31441                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31442
31443                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31444                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31445                 
31446                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31447                 
31448                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31449                 
31450                 break;
31451             case 180 :
31452                 
31453                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31454                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31455                 
31456                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31457                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31458                 
31459                 var targetWidth = this.minWidth - 2 * x;
31460                 var targetHeight = this.minHeight - 2 * y;
31461                 
31462                 var scale = 1;
31463                 
31464                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31465                     scale = targetWidth / width;
31466                 }
31467                 
31468                 if(x > 0 && y == 0){
31469                     scale = targetHeight / height;
31470                 }
31471                 
31472                 if(x > 0 && y > 0){
31473                     scale = targetWidth / width;
31474                     
31475                     if(width < height){
31476                         scale = targetHeight / height;
31477                     }
31478                 }
31479                 
31480                 context.scale(scale, scale);
31481                 
31482                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31483                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31484
31485                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31486                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31487
31488                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31489                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31490                 
31491                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31492                 
31493                 break;
31494             case 270 :
31495                 
31496                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31497                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31498                 
31499                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31500                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31501                 
31502                 var targetWidth = this.minWidth - 2 * x;
31503                 var targetHeight = this.minHeight - 2 * y;
31504                 
31505                 var scale = 1;
31506                 
31507                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31508                     scale = targetWidth / width;
31509                 }
31510                 
31511                 if(x > 0 && y == 0){
31512                     scale = targetHeight / height;
31513                 }
31514                 
31515                 if(x > 0 && y > 0){
31516                     scale = targetWidth / width;
31517                     
31518                     if(width < height){
31519                         scale = targetHeight / height;
31520                     }
31521                 }
31522                 
31523                 context.scale(scale, scale);
31524                 
31525                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31526                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31527
31528                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31529                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31530                 
31531                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31532                 
31533                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31534                 
31535                 break;
31536             default : 
31537                 break;
31538         }
31539         
31540         this.cropData = canvas.toDataURL(this.cropType);
31541         
31542         if(this.fireEvent('crop', this, this.cropData) !== false){
31543             this.process(this.file, this.cropData);
31544         }
31545         
31546         return;
31547         
31548     },
31549     
31550     setThumbBoxSize : function()
31551     {
31552         var width, height;
31553         
31554         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31555             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31556             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31557             
31558             this.minWidth = width;
31559             this.minHeight = height;
31560             
31561             if(this.rotate == 90 || this.rotate == 270){
31562                 this.minWidth = height;
31563                 this.minHeight = width;
31564             }
31565         }
31566         
31567         height = 300;
31568         width = Math.ceil(this.minWidth * height / this.minHeight);
31569         
31570         if(this.minWidth > this.minHeight){
31571             width = 300;
31572             height = Math.ceil(this.minHeight * width / this.minWidth);
31573         }
31574         
31575         this.thumbEl.setStyle({
31576             width : width + 'px',
31577             height : height + 'px'
31578         });
31579
31580         return;
31581             
31582     },
31583     
31584     setThumbBoxPosition : function()
31585     {
31586         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31587         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31588         
31589         this.thumbEl.setLeft(x);
31590         this.thumbEl.setTop(y);
31591         
31592     },
31593     
31594     baseRotateLevel : function()
31595     {
31596         this.baseRotate = 1;
31597         
31598         if(
31599                 typeof(this.exif) != 'undefined' &&
31600                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31601                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31602         ){
31603             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31604         }
31605         
31606         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31607         
31608     },
31609     
31610     baseScaleLevel : function()
31611     {
31612         var width, height;
31613         
31614         if(this.isDocument){
31615             
31616             if(this.baseRotate == 6 || this.baseRotate == 8){
31617             
31618                 height = this.thumbEl.getHeight();
31619                 this.baseScale = height / this.imageEl.OriginWidth;
31620
31621                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31622                     width = this.thumbEl.getWidth();
31623                     this.baseScale = width / this.imageEl.OriginHeight;
31624                 }
31625
31626                 return;
31627             }
31628
31629             height = this.thumbEl.getHeight();
31630             this.baseScale = height / this.imageEl.OriginHeight;
31631
31632             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31633                 width = this.thumbEl.getWidth();
31634                 this.baseScale = width / this.imageEl.OriginWidth;
31635             }
31636
31637             return;
31638         }
31639         
31640         if(this.baseRotate == 6 || this.baseRotate == 8){
31641             
31642             width = this.thumbEl.getHeight();
31643             this.baseScale = width / this.imageEl.OriginHeight;
31644             
31645             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31646                 height = this.thumbEl.getWidth();
31647                 this.baseScale = height / this.imageEl.OriginHeight;
31648             }
31649             
31650             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31651                 height = this.thumbEl.getWidth();
31652                 this.baseScale = height / this.imageEl.OriginHeight;
31653                 
31654                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31655                     width = this.thumbEl.getHeight();
31656                     this.baseScale = width / this.imageEl.OriginWidth;
31657                 }
31658             }
31659             
31660             return;
31661         }
31662         
31663         width = this.thumbEl.getWidth();
31664         this.baseScale = width / this.imageEl.OriginWidth;
31665         
31666         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31667             height = this.thumbEl.getHeight();
31668             this.baseScale = height / this.imageEl.OriginHeight;
31669         }
31670         
31671         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31672             
31673             height = this.thumbEl.getHeight();
31674             this.baseScale = height / this.imageEl.OriginHeight;
31675             
31676             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31677                 width = this.thumbEl.getWidth();
31678                 this.baseScale = width / this.imageEl.OriginWidth;
31679             }
31680             
31681         }
31682         
31683         return;
31684     },
31685     
31686     getScaleLevel : function()
31687     {
31688         return this.baseScale * Math.pow(1.1, this.scale);
31689     },
31690     
31691     onTouchStart : function(e)
31692     {
31693         if(!this.canvasLoaded){
31694             this.beforeSelectFile(e);
31695             return;
31696         }
31697         
31698         var touches = e.browserEvent.touches;
31699         
31700         if(!touches){
31701             return;
31702         }
31703         
31704         if(touches.length == 1){
31705             this.onMouseDown(e);
31706             return;
31707         }
31708         
31709         if(touches.length != 2){
31710             return;
31711         }
31712         
31713         var coords = [];
31714         
31715         for(var i = 0, finger; finger = touches[i]; i++){
31716             coords.push(finger.pageX, finger.pageY);
31717         }
31718         
31719         var x = Math.pow(coords[0] - coords[2], 2);
31720         var y = Math.pow(coords[1] - coords[3], 2);
31721         
31722         this.startDistance = Math.sqrt(x + y);
31723         
31724         this.startScale = this.scale;
31725         
31726         this.pinching = true;
31727         this.dragable = false;
31728         
31729     },
31730     
31731     onTouchMove : function(e)
31732     {
31733         if(!this.pinching && !this.dragable){
31734             return;
31735         }
31736         
31737         var touches = e.browserEvent.touches;
31738         
31739         if(!touches){
31740             return;
31741         }
31742         
31743         if(this.dragable){
31744             this.onMouseMove(e);
31745             return;
31746         }
31747         
31748         var coords = [];
31749         
31750         for(var i = 0, finger; finger = touches[i]; i++){
31751             coords.push(finger.pageX, finger.pageY);
31752         }
31753         
31754         var x = Math.pow(coords[0] - coords[2], 2);
31755         var y = Math.pow(coords[1] - coords[3], 2);
31756         
31757         this.endDistance = Math.sqrt(x + y);
31758         
31759         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31760         
31761         if(!this.zoomable()){
31762             this.scale = this.startScale;
31763             return;
31764         }
31765         
31766         this.draw();
31767         
31768     },
31769     
31770     onTouchEnd : function(e)
31771     {
31772         this.pinching = false;
31773         this.dragable = false;
31774         
31775     },
31776     
31777     process : function(file, crop)
31778     {
31779         if(this.loadMask){
31780             this.maskEl.mask(this.loadingText);
31781         }
31782         
31783         this.xhr = new XMLHttpRequest();
31784         
31785         file.xhr = this.xhr;
31786
31787         this.xhr.open(this.method, this.url, true);
31788         
31789         var headers = {
31790             "Accept": "application/json",
31791             "Cache-Control": "no-cache",
31792             "X-Requested-With": "XMLHttpRequest"
31793         };
31794         
31795         for (var headerName in headers) {
31796             var headerValue = headers[headerName];
31797             if (headerValue) {
31798                 this.xhr.setRequestHeader(headerName, headerValue);
31799             }
31800         }
31801         
31802         var _this = this;
31803         
31804         this.xhr.onload = function()
31805         {
31806             _this.xhrOnLoad(_this.xhr);
31807         }
31808         
31809         this.xhr.onerror = function()
31810         {
31811             _this.xhrOnError(_this.xhr);
31812         }
31813         
31814         var formData = new FormData();
31815
31816         formData.append('returnHTML', 'NO');
31817         
31818         if(crop){
31819             formData.append('crop', crop);
31820         }
31821         
31822         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31823             formData.append(this.paramName, file, file.name);
31824         }
31825         
31826         if(typeof(file.filename) != 'undefined'){
31827             formData.append('filename', file.filename);
31828         }
31829         
31830         if(typeof(file.mimetype) != 'undefined'){
31831             formData.append('mimetype', file.mimetype);
31832         }
31833         
31834         if(this.fireEvent('arrange', this, formData) != false){
31835             this.xhr.send(formData);
31836         };
31837     },
31838     
31839     xhrOnLoad : function(xhr)
31840     {
31841         if(this.loadMask){
31842             this.maskEl.unmask();
31843         }
31844         
31845         if (xhr.readyState !== 4) {
31846             this.fireEvent('exception', this, xhr);
31847             return;
31848         }
31849
31850         var response = Roo.decode(xhr.responseText);
31851         
31852         if(!response.success){
31853             this.fireEvent('exception', this, xhr);
31854             return;
31855         }
31856         
31857         var response = Roo.decode(xhr.responseText);
31858         
31859         this.fireEvent('upload', this, response);
31860         
31861     },
31862     
31863     xhrOnError : function()
31864     {
31865         if(this.loadMask){
31866             this.maskEl.unmask();
31867         }
31868         
31869         Roo.log('xhr on error');
31870         
31871         var response = Roo.decode(xhr.responseText);
31872           
31873         Roo.log(response);
31874         
31875     },
31876     
31877     prepare : function(file)
31878     {   
31879         if(this.loadMask){
31880             this.maskEl.mask(this.loadingText);
31881         }
31882         
31883         this.file = false;
31884         this.exif = {};
31885         
31886         if(typeof(file) === 'string'){
31887             this.loadCanvas(file);
31888             return;
31889         }
31890         
31891         if(!file || !this.urlAPI){
31892             return;
31893         }
31894         
31895         this.file = file;
31896         this.cropType = file.type;
31897         
31898         var _this = this;
31899         
31900         if(this.fireEvent('prepare', this, this.file) != false){
31901             
31902             var reader = new FileReader();
31903             
31904             reader.onload = function (e) {
31905                 if (e.target.error) {
31906                     Roo.log(e.target.error);
31907                     return;
31908                 }
31909                 
31910                 var buffer = e.target.result,
31911                     dataView = new DataView(buffer),
31912                     offset = 2,
31913                     maxOffset = dataView.byteLength - 4,
31914                     markerBytes,
31915                     markerLength;
31916                 
31917                 if (dataView.getUint16(0) === 0xffd8) {
31918                     while (offset < maxOffset) {
31919                         markerBytes = dataView.getUint16(offset);
31920                         
31921                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31922                             markerLength = dataView.getUint16(offset + 2) + 2;
31923                             if (offset + markerLength > dataView.byteLength) {
31924                                 Roo.log('Invalid meta data: Invalid segment size.');
31925                                 break;
31926                             }
31927                             
31928                             if(markerBytes == 0xffe1){
31929                                 _this.parseExifData(
31930                                     dataView,
31931                                     offset,
31932                                     markerLength
31933                                 );
31934                             }
31935                             
31936                             offset += markerLength;
31937                             
31938                             continue;
31939                         }
31940                         
31941                         break;
31942                     }
31943                     
31944                 }
31945                 
31946                 var url = _this.urlAPI.createObjectURL(_this.file);
31947                 
31948                 _this.loadCanvas(url);
31949                 
31950                 return;
31951             }
31952             
31953             reader.readAsArrayBuffer(this.file);
31954             
31955         }
31956         
31957     },
31958     
31959     parseExifData : function(dataView, offset, length)
31960     {
31961         var tiffOffset = offset + 10,
31962             littleEndian,
31963             dirOffset;
31964     
31965         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31966             // No Exif data, might be XMP data instead
31967             return;
31968         }
31969         
31970         // Check for the ASCII code for "Exif" (0x45786966):
31971         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31972             // No Exif data, might be XMP data instead
31973             return;
31974         }
31975         if (tiffOffset + 8 > dataView.byteLength) {
31976             Roo.log('Invalid Exif data: Invalid segment size.');
31977             return;
31978         }
31979         // Check for the two null bytes:
31980         if (dataView.getUint16(offset + 8) !== 0x0000) {
31981             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31982             return;
31983         }
31984         // Check the byte alignment:
31985         switch (dataView.getUint16(tiffOffset)) {
31986         case 0x4949:
31987             littleEndian = true;
31988             break;
31989         case 0x4D4D:
31990             littleEndian = false;
31991             break;
31992         default:
31993             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31994             return;
31995         }
31996         // Check for the TIFF tag marker (0x002A):
31997         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31998             Roo.log('Invalid Exif data: Missing TIFF marker.');
31999             return;
32000         }
32001         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32002         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32003         
32004         this.parseExifTags(
32005             dataView,
32006             tiffOffset,
32007             tiffOffset + dirOffset,
32008             littleEndian
32009         );
32010     },
32011     
32012     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32013     {
32014         var tagsNumber,
32015             dirEndOffset,
32016             i;
32017         if (dirOffset + 6 > dataView.byteLength) {
32018             Roo.log('Invalid Exif data: Invalid directory offset.');
32019             return;
32020         }
32021         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32022         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32023         if (dirEndOffset + 4 > dataView.byteLength) {
32024             Roo.log('Invalid Exif data: Invalid directory size.');
32025             return;
32026         }
32027         for (i = 0; i < tagsNumber; i += 1) {
32028             this.parseExifTag(
32029                 dataView,
32030                 tiffOffset,
32031                 dirOffset + 2 + 12 * i, // tag offset
32032                 littleEndian
32033             );
32034         }
32035         // Return the offset to the next directory:
32036         return dataView.getUint32(dirEndOffset, littleEndian);
32037     },
32038     
32039     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32040     {
32041         var tag = dataView.getUint16(offset, littleEndian);
32042         
32043         this.exif[tag] = this.getExifValue(
32044             dataView,
32045             tiffOffset,
32046             offset,
32047             dataView.getUint16(offset + 2, littleEndian), // tag type
32048             dataView.getUint32(offset + 4, littleEndian), // tag length
32049             littleEndian
32050         );
32051     },
32052     
32053     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32054     {
32055         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32056             tagSize,
32057             dataOffset,
32058             values,
32059             i,
32060             str,
32061             c;
32062     
32063         if (!tagType) {
32064             Roo.log('Invalid Exif data: Invalid tag type.');
32065             return;
32066         }
32067         
32068         tagSize = tagType.size * length;
32069         // Determine if the value is contained in the dataOffset bytes,
32070         // or if the value at the dataOffset is a pointer to the actual data:
32071         dataOffset = tagSize > 4 ?
32072                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32073         if (dataOffset + tagSize > dataView.byteLength) {
32074             Roo.log('Invalid Exif data: Invalid data offset.');
32075             return;
32076         }
32077         if (length === 1) {
32078             return tagType.getValue(dataView, dataOffset, littleEndian);
32079         }
32080         values = [];
32081         for (i = 0; i < length; i += 1) {
32082             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32083         }
32084         
32085         if (tagType.ascii) {
32086             str = '';
32087             // Concatenate the chars:
32088             for (i = 0; i < values.length; i += 1) {
32089                 c = values[i];
32090                 // Ignore the terminating NULL byte(s):
32091                 if (c === '\u0000') {
32092                     break;
32093                 }
32094                 str += c;
32095             }
32096             return str;
32097         }
32098         return values;
32099     }
32100     
32101 });
32102
32103 Roo.apply(Roo.bootstrap.UploadCropbox, {
32104     tags : {
32105         'Orientation': 0x0112
32106     },
32107     
32108     Orientation: {
32109             1: 0, //'top-left',
32110 //            2: 'top-right',
32111             3: 180, //'bottom-right',
32112 //            4: 'bottom-left',
32113 //            5: 'left-top',
32114             6: 90, //'right-top',
32115 //            7: 'right-bottom',
32116             8: 270 //'left-bottom'
32117     },
32118     
32119     exifTagTypes : {
32120         // byte, 8-bit unsigned int:
32121         1: {
32122             getValue: function (dataView, dataOffset) {
32123                 return dataView.getUint8(dataOffset);
32124             },
32125             size: 1
32126         },
32127         // ascii, 8-bit byte:
32128         2: {
32129             getValue: function (dataView, dataOffset) {
32130                 return String.fromCharCode(dataView.getUint8(dataOffset));
32131             },
32132             size: 1,
32133             ascii: true
32134         },
32135         // short, 16 bit int:
32136         3: {
32137             getValue: function (dataView, dataOffset, littleEndian) {
32138                 return dataView.getUint16(dataOffset, littleEndian);
32139             },
32140             size: 2
32141         },
32142         // long, 32 bit int:
32143         4: {
32144             getValue: function (dataView, dataOffset, littleEndian) {
32145                 return dataView.getUint32(dataOffset, littleEndian);
32146             },
32147             size: 4
32148         },
32149         // rational = two long values, first is numerator, second is denominator:
32150         5: {
32151             getValue: function (dataView, dataOffset, littleEndian) {
32152                 return dataView.getUint32(dataOffset, littleEndian) /
32153                     dataView.getUint32(dataOffset + 4, littleEndian);
32154             },
32155             size: 8
32156         },
32157         // slong, 32 bit signed int:
32158         9: {
32159             getValue: function (dataView, dataOffset, littleEndian) {
32160                 return dataView.getInt32(dataOffset, littleEndian);
32161             },
32162             size: 4
32163         },
32164         // srational, two slongs, first is numerator, second is denominator:
32165         10: {
32166             getValue: function (dataView, dataOffset, littleEndian) {
32167                 return dataView.getInt32(dataOffset, littleEndian) /
32168                     dataView.getInt32(dataOffset + 4, littleEndian);
32169             },
32170             size: 8
32171         }
32172     },
32173     
32174     footer : {
32175         STANDARD : [
32176             {
32177                 tag : 'div',
32178                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32179                 action : 'rotate-left',
32180                 cn : [
32181                     {
32182                         tag : 'button',
32183                         cls : 'btn btn-default',
32184                         html : '<i class="fa fa-undo"></i>'
32185                     }
32186                 ]
32187             },
32188             {
32189                 tag : 'div',
32190                 cls : 'btn-group roo-upload-cropbox-picture',
32191                 action : 'picture',
32192                 cn : [
32193                     {
32194                         tag : 'button',
32195                         cls : 'btn btn-default',
32196                         html : '<i class="fa fa-picture-o"></i>'
32197                     }
32198                 ]
32199             },
32200             {
32201                 tag : 'div',
32202                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32203                 action : 'rotate-right',
32204                 cn : [
32205                     {
32206                         tag : 'button',
32207                         cls : 'btn btn-default',
32208                         html : '<i class="fa fa-repeat"></i>'
32209                     }
32210                 ]
32211             }
32212         ],
32213         DOCUMENT : [
32214             {
32215                 tag : 'div',
32216                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32217                 action : 'rotate-left',
32218                 cn : [
32219                     {
32220                         tag : 'button',
32221                         cls : 'btn btn-default',
32222                         html : '<i class="fa fa-undo"></i>'
32223                     }
32224                 ]
32225             },
32226             {
32227                 tag : 'div',
32228                 cls : 'btn-group roo-upload-cropbox-download',
32229                 action : 'download',
32230                 cn : [
32231                     {
32232                         tag : 'button',
32233                         cls : 'btn btn-default',
32234                         html : '<i class="fa fa-download"></i>'
32235                     }
32236                 ]
32237             },
32238             {
32239                 tag : 'div',
32240                 cls : 'btn-group roo-upload-cropbox-crop',
32241                 action : 'crop',
32242                 cn : [
32243                     {
32244                         tag : 'button',
32245                         cls : 'btn btn-default',
32246                         html : '<i class="fa fa-crop"></i>'
32247                     }
32248                 ]
32249             },
32250             {
32251                 tag : 'div',
32252                 cls : 'btn-group roo-upload-cropbox-trash',
32253                 action : 'trash',
32254                 cn : [
32255                     {
32256                         tag : 'button',
32257                         cls : 'btn btn-default',
32258                         html : '<i class="fa fa-trash"></i>'
32259                     }
32260                 ]
32261             },
32262             {
32263                 tag : 'div',
32264                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32265                 action : 'rotate-right',
32266                 cn : [
32267                     {
32268                         tag : 'button',
32269                         cls : 'btn btn-default',
32270                         html : '<i class="fa fa-repeat"></i>'
32271                     }
32272                 ]
32273             }
32274         ],
32275         ROTATOR : [
32276             {
32277                 tag : 'div',
32278                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32279                 action : 'rotate-left',
32280                 cn : [
32281                     {
32282                         tag : 'button',
32283                         cls : 'btn btn-default',
32284                         html : '<i class="fa fa-undo"></i>'
32285                     }
32286                 ]
32287             },
32288             {
32289                 tag : 'div',
32290                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32291                 action : 'rotate-right',
32292                 cn : [
32293                     {
32294                         tag : 'button',
32295                         cls : 'btn btn-default',
32296                         html : '<i class="fa fa-repeat"></i>'
32297                     }
32298                 ]
32299             }
32300         ]
32301     }
32302 });
32303
32304 /*
32305 * Licence: LGPL
32306 */
32307
32308 /**
32309  * @class Roo.bootstrap.DocumentManager
32310  * @extends Roo.bootstrap.Component
32311  * Bootstrap DocumentManager class
32312  * @cfg {String} paramName default 'imageUpload'
32313  * @cfg {String} toolTipName default 'filename'
32314  * @cfg {String} method default POST
32315  * @cfg {String} url action url
32316  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32317  * @cfg {Boolean} multiple multiple upload default true
32318  * @cfg {Number} thumbSize default 300
32319  * @cfg {String} fieldLabel
32320  * @cfg {Number} labelWidth default 4
32321  * @cfg {String} labelAlign (left|top) default left
32322  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32323 * @cfg {Number} labellg set the width of label (1-12)
32324  * @cfg {Number} labelmd set the width of label (1-12)
32325  * @cfg {Number} labelsm set the width of label (1-12)
32326  * @cfg {Number} labelxs set the width of label (1-12)
32327  * 
32328  * @constructor
32329  * Create a new DocumentManager
32330  * @param {Object} config The config object
32331  */
32332
32333 Roo.bootstrap.DocumentManager = function(config){
32334     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32335     
32336     this.files = [];
32337     this.delegates = [];
32338     
32339     this.addEvents({
32340         /**
32341          * @event initial
32342          * Fire when initial the DocumentManager
32343          * @param {Roo.bootstrap.DocumentManager} this
32344          */
32345         "initial" : true,
32346         /**
32347          * @event inspect
32348          * inspect selected file
32349          * @param {Roo.bootstrap.DocumentManager} this
32350          * @param {File} file
32351          */
32352         "inspect" : true,
32353         /**
32354          * @event exception
32355          * Fire when xhr load exception
32356          * @param {Roo.bootstrap.DocumentManager} this
32357          * @param {XMLHttpRequest} xhr
32358          */
32359         "exception" : true,
32360         /**
32361          * @event afterupload
32362          * Fire when xhr load exception
32363          * @param {Roo.bootstrap.DocumentManager} this
32364          * @param {XMLHttpRequest} xhr
32365          */
32366         "afterupload" : true,
32367         /**
32368          * @event prepare
32369          * prepare the form data
32370          * @param {Roo.bootstrap.DocumentManager} this
32371          * @param {Object} formData
32372          */
32373         "prepare" : true,
32374         /**
32375          * @event remove
32376          * Fire when remove the file
32377          * @param {Roo.bootstrap.DocumentManager} this
32378          * @param {Object} file
32379          */
32380         "remove" : true,
32381         /**
32382          * @event refresh
32383          * Fire after refresh the file
32384          * @param {Roo.bootstrap.DocumentManager} this
32385          */
32386         "refresh" : true,
32387         /**
32388          * @event click
32389          * Fire after click the image
32390          * @param {Roo.bootstrap.DocumentManager} this
32391          * @param {Object} file
32392          */
32393         "click" : true,
32394         /**
32395          * @event edit
32396          * Fire when upload a image and editable set to true
32397          * @param {Roo.bootstrap.DocumentManager} this
32398          * @param {Object} file
32399          */
32400         "edit" : true,
32401         /**
32402          * @event beforeselectfile
32403          * Fire before select file
32404          * @param {Roo.bootstrap.DocumentManager} this
32405          */
32406         "beforeselectfile" : true,
32407         /**
32408          * @event process
32409          * Fire before process file
32410          * @param {Roo.bootstrap.DocumentManager} this
32411          * @param {Object} file
32412          */
32413         "process" : true,
32414         /**
32415          * @event previewrendered
32416          * Fire when preview rendered
32417          * @param {Roo.bootstrap.DocumentManager} this
32418          * @param {Object} file
32419          */
32420         "previewrendered" : true,
32421         /**
32422          */
32423         "previewResize" : true
32424         
32425     });
32426 };
32427
32428 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32429     
32430     boxes : 0,
32431     inputName : '',
32432     thumbSize : 300,
32433     multiple : true,
32434     files : false,
32435     method : 'POST',
32436     url : '',
32437     paramName : 'imageUpload',
32438     toolTipName : 'filename',
32439     fieldLabel : '',
32440     labelWidth : 4,
32441     labelAlign : 'left',
32442     editable : true,
32443     delegates : false,
32444     xhr : false, 
32445     
32446     labellg : 0,
32447     labelmd : 0,
32448     labelsm : 0,
32449     labelxs : 0,
32450     
32451     getAutoCreate : function()
32452     {   
32453         var managerWidget = {
32454             tag : 'div',
32455             cls : 'roo-document-manager',
32456             cn : [
32457                 {
32458                     tag : 'input',
32459                     cls : 'roo-document-manager-selector',
32460                     type : 'file'
32461                 },
32462                 {
32463                     tag : 'div',
32464                     cls : 'roo-document-manager-uploader',
32465                     cn : [
32466                         {
32467                             tag : 'div',
32468                             cls : 'roo-document-manager-upload-btn',
32469                             html : '<i class="fa fa-plus"></i>'
32470                         }
32471                     ]
32472                     
32473                 }
32474             ]
32475         };
32476         
32477         var content = [
32478             {
32479                 tag : 'div',
32480                 cls : 'column col-md-12',
32481                 cn : managerWidget
32482             }
32483         ];
32484         
32485         if(this.fieldLabel.length){
32486             
32487             content = [
32488                 {
32489                     tag : 'div',
32490                     cls : 'column col-md-12',
32491                     html : this.fieldLabel
32492                 },
32493                 {
32494                     tag : 'div',
32495                     cls : 'column col-md-12',
32496                     cn : managerWidget
32497                 }
32498             ];
32499
32500             if(this.labelAlign == 'left'){
32501                 content = [
32502                     {
32503                         tag : 'div',
32504                         cls : 'column',
32505                         html : this.fieldLabel
32506                     },
32507                     {
32508                         tag : 'div',
32509                         cls : 'column',
32510                         cn : managerWidget
32511                     }
32512                 ];
32513                 
32514                 if(this.labelWidth > 12){
32515                     content[0].style = "width: " + this.labelWidth + 'px';
32516                 }
32517
32518                 if(this.labelWidth < 13 && this.labelmd == 0){
32519                     this.labelmd = this.labelWidth;
32520                 }
32521
32522                 if(this.labellg > 0){
32523                     content[0].cls += ' col-lg-' + this.labellg;
32524                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32525                 }
32526
32527                 if(this.labelmd > 0){
32528                     content[0].cls += ' col-md-' + this.labelmd;
32529                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32530                 }
32531
32532                 if(this.labelsm > 0){
32533                     content[0].cls += ' col-sm-' + this.labelsm;
32534                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32535                 }
32536
32537                 if(this.labelxs > 0){
32538                     content[0].cls += ' col-xs-' + this.labelxs;
32539                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32540                 }
32541                 
32542             }
32543         }
32544         
32545         var cfg = {
32546             tag : 'div',
32547             cls : 'row clearfix',
32548             cn : content
32549         };
32550         
32551         return cfg;
32552         
32553     },
32554     
32555     initEvents : function()
32556     {
32557         this.managerEl = this.el.select('.roo-document-manager', true).first();
32558         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32559         
32560         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32561         this.selectorEl.hide();
32562         
32563         if(this.multiple){
32564             this.selectorEl.attr('multiple', 'multiple');
32565         }
32566         
32567         this.selectorEl.on('change', this.onFileSelected, this);
32568         
32569         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32570         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32571         
32572         this.uploader.on('click', this.onUploaderClick, this);
32573         
32574         this.renderProgressDialog();
32575         
32576         var _this = this;
32577         
32578         window.addEventListener("resize", function() { _this.refresh(); } );
32579         
32580         this.fireEvent('initial', this);
32581     },
32582     
32583     renderProgressDialog : function()
32584     {
32585         var _this = this;
32586         
32587         this.progressDialog = new Roo.bootstrap.Modal({
32588             cls : 'roo-document-manager-progress-dialog',
32589             allow_close : false,
32590             animate : false,
32591             title : '',
32592             buttons : [
32593                 {
32594                     name  :'cancel',
32595                     weight : 'danger',
32596                     html : 'Cancel'
32597                 }
32598             ], 
32599             listeners : { 
32600                 btnclick : function() {
32601                     _this.uploadCancel();
32602                     this.hide();
32603                 }
32604             }
32605         });
32606          
32607         this.progressDialog.render(Roo.get(document.body));
32608          
32609         this.progress = new Roo.bootstrap.Progress({
32610             cls : 'roo-document-manager-progress',
32611             active : true,
32612             striped : true
32613         });
32614         
32615         this.progress.render(this.progressDialog.getChildContainer());
32616         
32617         this.progressBar = new Roo.bootstrap.ProgressBar({
32618             cls : 'roo-document-manager-progress-bar',
32619             aria_valuenow : 0,
32620             aria_valuemin : 0,
32621             aria_valuemax : 12,
32622             panel : 'success'
32623         });
32624         
32625         this.progressBar.render(this.progress.getChildContainer());
32626     },
32627     
32628     onUploaderClick : function(e)
32629     {
32630         e.preventDefault();
32631      
32632         if(this.fireEvent('beforeselectfile', this) != false){
32633             this.selectorEl.dom.click();
32634         }
32635         
32636     },
32637     
32638     onFileSelected : function(e)
32639     {
32640         e.preventDefault();
32641         
32642         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32643             return;
32644         }
32645         
32646         Roo.each(this.selectorEl.dom.files, function(file){
32647             if(this.fireEvent('inspect', this, file) != false){
32648                 this.files.push(file);
32649             }
32650         }, this);
32651         
32652         this.queue();
32653         
32654     },
32655     
32656     queue : function()
32657     {
32658         this.selectorEl.dom.value = '';
32659         
32660         if(!this.files || !this.files.length){
32661             return;
32662         }
32663         
32664         if(this.boxes > 0 && this.files.length > this.boxes){
32665             this.files = this.files.slice(0, this.boxes);
32666         }
32667         
32668         this.uploader.show();
32669         
32670         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32671             this.uploader.hide();
32672         }
32673         
32674         var _this = this;
32675         
32676         var files = [];
32677         
32678         var docs = [];
32679         
32680         Roo.each(this.files, function(file){
32681             
32682             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32683                 var f = this.renderPreview(file);
32684                 files.push(f);
32685                 return;
32686             }
32687             
32688             if(file.type.indexOf('image') != -1){
32689                 this.delegates.push(
32690                     (function(){
32691                         _this.process(file);
32692                     }).createDelegate(this)
32693                 );
32694         
32695                 return;
32696             }
32697             
32698             docs.push(
32699                 (function(){
32700                     _this.process(file);
32701                 }).createDelegate(this)
32702             );
32703             
32704         }, this);
32705         
32706         this.files = files;
32707         
32708         this.delegates = this.delegates.concat(docs);
32709         
32710         if(!this.delegates.length){
32711             this.refresh();
32712             return;
32713         }
32714         
32715         this.progressBar.aria_valuemax = this.delegates.length;
32716         
32717         this.arrange();
32718         
32719         return;
32720     },
32721     
32722     arrange : function()
32723     {
32724         if(!this.delegates.length){
32725             this.progressDialog.hide();
32726             this.refresh();
32727             return;
32728         }
32729         
32730         var delegate = this.delegates.shift();
32731         
32732         this.progressDialog.show();
32733         
32734         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32735         
32736         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32737         
32738         delegate();
32739     },
32740     
32741     refresh : function()
32742     {
32743         this.uploader.show();
32744         
32745         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32746             this.uploader.hide();
32747         }
32748         
32749         Roo.isTouch ? this.closable(false) : this.closable(true);
32750         
32751         this.fireEvent('refresh', this);
32752     },
32753     
32754     onRemove : function(e, el, o)
32755     {
32756         e.preventDefault();
32757         
32758         this.fireEvent('remove', this, o);
32759         
32760     },
32761     
32762     remove : function(o)
32763     {
32764         var files = [];
32765         
32766         Roo.each(this.files, function(file){
32767             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32768                 files.push(file);
32769                 return;
32770             }
32771
32772             o.target.remove();
32773
32774         }, this);
32775         
32776         this.files = files;
32777         
32778         this.refresh();
32779     },
32780     
32781     clear : function()
32782     {
32783         Roo.each(this.files, function(file){
32784             if(!file.target){
32785                 return;
32786             }
32787             
32788             file.target.remove();
32789
32790         }, this);
32791         
32792         this.files = [];
32793         
32794         this.refresh();
32795     },
32796     
32797     onClick : function(e, el, o)
32798     {
32799         e.preventDefault();
32800         
32801         this.fireEvent('click', this, o);
32802         
32803     },
32804     
32805     closable : function(closable)
32806     {
32807         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32808             
32809             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32810             
32811             if(closable){
32812                 el.show();
32813                 return;
32814             }
32815             
32816             el.hide();
32817             
32818         }, this);
32819     },
32820     
32821     xhrOnLoad : function(xhr)
32822     {
32823         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32824             el.remove();
32825         }, this);
32826         
32827         if (xhr.readyState !== 4) {
32828             this.arrange();
32829             this.fireEvent('exception', this, xhr);
32830             return;
32831         }
32832
32833         var response = Roo.decode(xhr.responseText);
32834         
32835         if(!response.success){
32836             this.arrange();
32837             this.fireEvent('exception', this, xhr);
32838             return;
32839         }
32840         
32841         var file = this.renderPreview(response.data);
32842         
32843         this.files.push(file);
32844         
32845         this.arrange();
32846         
32847         this.fireEvent('afterupload', this, xhr);
32848         
32849     },
32850     
32851     xhrOnError : function(xhr)
32852     {
32853         Roo.log('xhr on error');
32854         
32855         var response = Roo.decode(xhr.responseText);
32856           
32857         Roo.log(response);
32858         
32859         this.arrange();
32860     },
32861     
32862     process : function(file)
32863     {
32864         if(this.fireEvent('process', this, file) !== false){
32865             if(this.editable && file.type.indexOf('image') != -1){
32866                 this.fireEvent('edit', this, file);
32867                 return;
32868             }
32869
32870             this.uploadStart(file, false);
32871
32872             return;
32873         }
32874         
32875     },
32876     
32877     uploadStart : function(file, crop)
32878     {
32879         this.xhr = new XMLHttpRequest();
32880         
32881         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32882             this.arrange();
32883             return;
32884         }
32885         
32886         file.xhr = this.xhr;
32887             
32888         this.managerEl.createChild({
32889             tag : 'div',
32890             cls : 'roo-document-manager-loading',
32891             cn : [
32892                 {
32893                     tag : 'div',
32894                     tooltip : file.name,
32895                     cls : 'roo-document-manager-thumb',
32896                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32897                 }
32898             ]
32899
32900         });
32901
32902         this.xhr.open(this.method, this.url, true);
32903         
32904         var headers = {
32905             "Accept": "application/json",
32906             "Cache-Control": "no-cache",
32907             "X-Requested-With": "XMLHttpRequest"
32908         };
32909         
32910         for (var headerName in headers) {
32911             var headerValue = headers[headerName];
32912             if (headerValue) {
32913                 this.xhr.setRequestHeader(headerName, headerValue);
32914             }
32915         }
32916         
32917         var _this = this;
32918         
32919         this.xhr.onload = function()
32920         {
32921             _this.xhrOnLoad(_this.xhr);
32922         }
32923         
32924         this.xhr.onerror = function()
32925         {
32926             _this.xhrOnError(_this.xhr);
32927         }
32928         
32929         var formData = new FormData();
32930
32931         formData.append('returnHTML', 'NO');
32932         
32933         if(crop){
32934             formData.append('crop', crop);
32935         }
32936         
32937         formData.append(this.paramName, file, file.name);
32938         
32939         var options = {
32940             file : file, 
32941             manually : false
32942         };
32943         
32944         if(this.fireEvent('prepare', this, formData, options) != false){
32945             
32946             if(options.manually){
32947                 return;
32948             }
32949             
32950             this.xhr.send(formData);
32951             return;
32952         };
32953         
32954         this.uploadCancel();
32955     },
32956     
32957     uploadCancel : function()
32958     {
32959         if (this.xhr) {
32960             this.xhr.abort();
32961         }
32962         
32963         this.delegates = [];
32964         
32965         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32966             el.remove();
32967         }, this);
32968         
32969         this.arrange();
32970     },
32971     
32972     renderPreview : function(file)
32973     {
32974         if(typeof(file.target) != 'undefined' && file.target){
32975             return file;
32976         }
32977         
32978         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32979         
32980         var previewEl = this.managerEl.createChild({
32981             tag : 'div',
32982             cls : 'roo-document-manager-preview',
32983             cn : [
32984                 {
32985                     tag : 'div',
32986                     tooltip : file[this.toolTipName],
32987                     cls : 'roo-document-manager-thumb',
32988                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32989                 },
32990                 {
32991                     tag : 'button',
32992                     cls : 'close',
32993                     html : '<i class="fa fa-times-circle"></i>'
32994                 }
32995             ]
32996         });
32997
32998         var close = previewEl.select('button.close', true).first();
32999
33000         close.on('click', this.onRemove, this, file);
33001
33002         file.target = previewEl;
33003
33004         var image = previewEl.select('img', true).first();
33005         
33006         var _this = this;
33007         
33008         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33009         
33010         image.on('click', this.onClick, this, file);
33011         
33012         this.fireEvent('previewrendered', this, file);
33013         
33014         return file;
33015         
33016     },
33017     
33018     onPreviewLoad : function(file, image)
33019     {
33020         if(typeof(file.target) == 'undefined' || !file.target){
33021             return;
33022         }
33023         
33024         var width = image.dom.naturalWidth || image.dom.width;
33025         var height = image.dom.naturalHeight || image.dom.height;
33026         
33027         if(!this.previewResize) {
33028             return;
33029         }
33030         
33031         if(width > height){
33032             file.target.addClass('wide');
33033             return;
33034         }
33035         
33036         file.target.addClass('tall');
33037         return;
33038         
33039     },
33040     
33041     uploadFromSource : function(file, crop)
33042     {
33043         this.xhr = new XMLHttpRequest();
33044         
33045         this.managerEl.createChild({
33046             tag : 'div',
33047             cls : 'roo-document-manager-loading',
33048             cn : [
33049                 {
33050                     tag : 'div',
33051                     tooltip : file.name,
33052                     cls : 'roo-document-manager-thumb',
33053                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33054                 }
33055             ]
33056
33057         });
33058
33059         this.xhr.open(this.method, this.url, true);
33060         
33061         var headers = {
33062             "Accept": "application/json",
33063             "Cache-Control": "no-cache",
33064             "X-Requested-With": "XMLHttpRequest"
33065         };
33066         
33067         for (var headerName in headers) {
33068             var headerValue = headers[headerName];
33069             if (headerValue) {
33070                 this.xhr.setRequestHeader(headerName, headerValue);
33071             }
33072         }
33073         
33074         var _this = this;
33075         
33076         this.xhr.onload = function()
33077         {
33078             _this.xhrOnLoad(_this.xhr);
33079         }
33080         
33081         this.xhr.onerror = function()
33082         {
33083             _this.xhrOnError(_this.xhr);
33084         }
33085         
33086         var formData = new FormData();
33087
33088         formData.append('returnHTML', 'NO');
33089         
33090         formData.append('crop', crop);
33091         
33092         if(typeof(file.filename) != 'undefined'){
33093             formData.append('filename', file.filename);
33094         }
33095         
33096         if(typeof(file.mimetype) != 'undefined'){
33097             formData.append('mimetype', file.mimetype);
33098         }
33099         
33100         Roo.log(formData);
33101         
33102         if(this.fireEvent('prepare', this, formData) != false){
33103             this.xhr.send(formData);
33104         };
33105     }
33106 });
33107
33108 /*
33109 * Licence: LGPL
33110 */
33111
33112 /**
33113  * @class Roo.bootstrap.DocumentViewer
33114  * @extends Roo.bootstrap.Component
33115  * Bootstrap DocumentViewer class
33116  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33117  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33118  * 
33119  * @constructor
33120  * Create a new DocumentViewer
33121  * @param {Object} config The config object
33122  */
33123
33124 Roo.bootstrap.DocumentViewer = function(config){
33125     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33126     
33127     this.addEvents({
33128         /**
33129          * @event initial
33130          * Fire after initEvent
33131          * @param {Roo.bootstrap.DocumentViewer} this
33132          */
33133         "initial" : true,
33134         /**
33135          * @event click
33136          * Fire after click
33137          * @param {Roo.bootstrap.DocumentViewer} this
33138          */
33139         "click" : true,
33140         /**
33141          * @event download
33142          * Fire after download button
33143          * @param {Roo.bootstrap.DocumentViewer} this
33144          */
33145         "download" : true,
33146         /**
33147          * @event trash
33148          * Fire after trash button
33149          * @param {Roo.bootstrap.DocumentViewer} this
33150          */
33151         "trash" : true
33152         
33153     });
33154 };
33155
33156 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33157     
33158     showDownload : true,
33159     
33160     showTrash : true,
33161     
33162     getAutoCreate : function()
33163     {
33164         var cfg = {
33165             tag : 'div',
33166             cls : 'roo-document-viewer',
33167             cn : [
33168                 {
33169                     tag : 'div',
33170                     cls : 'roo-document-viewer-body',
33171                     cn : [
33172                         {
33173                             tag : 'div',
33174                             cls : 'roo-document-viewer-thumb',
33175                             cn : [
33176                                 {
33177                                     tag : 'img',
33178                                     cls : 'roo-document-viewer-image'
33179                                 }
33180                             ]
33181                         }
33182                     ]
33183                 },
33184                 {
33185                     tag : 'div',
33186                     cls : 'roo-document-viewer-footer',
33187                     cn : {
33188                         tag : 'div',
33189                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33190                         cn : [
33191                             {
33192                                 tag : 'div',
33193                                 cls : 'btn-group roo-document-viewer-download',
33194                                 cn : [
33195                                     {
33196                                         tag : 'button',
33197                                         cls : 'btn btn-default',
33198                                         html : '<i class="fa fa-download"></i>'
33199                                     }
33200                                 ]
33201                             },
33202                             {
33203                                 tag : 'div',
33204                                 cls : 'btn-group roo-document-viewer-trash',
33205                                 cn : [
33206                                     {
33207                                         tag : 'button',
33208                                         cls : 'btn btn-default',
33209                                         html : '<i class="fa fa-trash"></i>'
33210                                     }
33211                                 ]
33212                             }
33213                         ]
33214                     }
33215                 }
33216             ]
33217         };
33218         
33219         return cfg;
33220     },
33221     
33222     initEvents : function()
33223     {
33224         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33225         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33226         
33227         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33228         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33229         
33230         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33231         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33232         
33233         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33234         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33235         
33236         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33237         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33238         
33239         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33240         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33241         
33242         this.bodyEl.on('click', this.onClick, this);
33243         this.downloadBtn.on('click', this.onDownload, this);
33244         this.trashBtn.on('click', this.onTrash, this);
33245         
33246         this.downloadBtn.hide();
33247         this.trashBtn.hide();
33248         
33249         if(this.showDownload){
33250             this.downloadBtn.show();
33251         }
33252         
33253         if(this.showTrash){
33254             this.trashBtn.show();
33255         }
33256         
33257         if(!this.showDownload && !this.showTrash) {
33258             this.footerEl.hide();
33259         }
33260         
33261     },
33262     
33263     initial : function()
33264     {
33265         this.fireEvent('initial', this);
33266         
33267     },
33268     
33269     onClick : function(e)
33270     {
33271         e.preventDefault();
33272         
33273         this.fireEvent('click', this);
33274     },
33275     
33276     onDownload : function(e)
33277     {
33278         e.preventDefault();
33279         
33280         this.fireEvent('download', this);
33281     },
33282     
33283     onTrash : function(e)
33284     {
33285         e.preventDefault();
33286         
33287         this.fireEvent('trash', this);
33288     }
33289     
33290 });
33291 /*
33292  * - LGPL
33293  *
33294  * nav progress bar
33295  * 
33296  */
33297
33298 /**
33299  * @class Roo.bootstrap.NavProgressBar
33300  * @extends Roo.bootstrap.Component
33301  * Bootstrap NavProgressBar class
33302  * 
33303  * @constructor
33304  * Create a new nav progress bar
33305  * @param {Object} config The config object
33306  */
33307
33308 Roo.bootstrap.NavProgressBar = function(config){
33309     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33310
33311     this.bullets = this.bullets || [];
33312    
33313 //    Roo.bootstrap.NavProgressBar.register(this);
33314      this.addEvents({
33315         /**
33316              * @event changed
33317              * Fires when the active item changes
33318              * @param {Roo.bootstrap.NavProgressBar} this
33319              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33320              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33321          */
33322         'changed': true
33323      });
33324     
33325 };
33326
33327 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33328     
33329     bullets : [],
33330     barItems : [],
33331     
33332     getAutoCreate : function()
33333     {
33334         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33335         
33336         cfg = {
33337             tag : 'div',
33338             cls : 'roo-navigation-bar-group',
33339             cn : [
33340                 {
33341                     tag : 'div',
33342                     cls : 'roo-navigation-top-bar'
33343                 },
33344                 {
33345                     tag : 'div',
33346                     cls : 'roo-navigation-bullets-bar',
33347                     cn : [
33348                         {
33349                             tag : 'ul',
33350                             cls : 'roo-navigation-bar'
33351                         }
33352                     ]
33353                 },
33354                 
33355                 {
33356                     tag : 'div',
33357                     cls : 'roo-navigation-bottom-bar'
33358                 }
33359             ]
33360             
33361         };
33362         
33363         return cfg;
33364         
33365     },
33366     
33367     initEvents: function() 
33368     {
33369         
33370     },
33371     
33372     onRender : function(ct, position) 
33373     {
33374         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33375         
33376         if(this.bullets.length){
33377             Roo.each(this.bullets, function(b){
33378                this.addItem(b);
33379             }, this);
33380         }
33381         
33382         this.format();
33383         
33384     },
33385     
33386     addItem : function(cfg)
33387     {
33388         var item = new Roo.bootstrap.NavProgressItem(cfg);
33389         
33390         item.parentId = this.id;
33391         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33392         
33393         if(cfg.html){
33394             var top = new Roo.bootstrap.Element({
33395                 tag : 'div',
33396                 cls : 'roo-navigation-bar-text'
33397             });
33398             
33399             var bottom = new Roo.bootstrap.Element({
33400                 tag : 'div',
33401                 cls : 'roo-navigation-bar-text'
33402             });
33403             
33404             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33405             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33406             
33407             var topText = new Roo.bootstrap.Element({
33408                 tag : 'span',
33409                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33410             });
33411             
33412             var bottomText = new Roo.bootstrap.Element({
33413                 tag : 'span',
33414                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33415             });
33416             
33417             topText.onRender(top.el, null);
33418             bottomText.onRender(bottom.el, null);
33419             
33420             item.topEl = top;
33421             item.bottomEl = bottom;
33422         }
33423         
33424         this.barItems.push(item);
33425         
33426         return item;
33427     },
33428     
33429     getActive : function()
33430     {
33431         var active = false;
33432         
33433         Roo.each(this.barItems, function(v){
33434             
33435             if (!v.isActive()) {
33436                 return;
33437             }
33438             
33439             active = v;
33440             return false;
33441             
33442         });
33443         
33444         return active;
33445     },
33446     
33447     setActiveItem : function(item)
33448     {
33449         var prev = false;
33450         
33451         Roo.each(this.barItems, function(v){
33452             if (v.rid == item.rid) {
33453                 return ;
33454             }
33455             
33456             if (v.isActive()) {
33457                 v.setActive(false);
33458                 prev = v;
33459             }
33460         });
33461
33462         item.setActive(true);
33463         
33464         this.fireEvent('changed', this, item, prev);
33465     },
33466     
33467     getBarItem: function(rid)
33468     {
33469         var ret = false;
33470         
33471         Roo.each(this.barItems, function(e) {
33472             if (e.rid != rid) {
33473                 return;
33474             }
33475             
33476             ret =  e;
33477             return false;
33478         });
33479         
33480         return ret;
33481     },
33482     
33483     indexOfItem : function(item)
33484     {
33485         var index = false;
33486         
33487         Roo.each(this.barItems, function(v, i){
33488             
33489             if (v.rid != item.rid) {
33490                 return;
33491             }
33492             
33493             index = i;
33494             return false
33495         });
33496         
33497         return index;
33498     },
33499     
33500     setActiveNext : function()
33501     {
33502         var i = this.indexOfItem(this.getActive());
33503         
33504         if (i > this.barItems.length) {
33505             return;
33506         }
33507         
33508         this.setActiveItem(this.barItems[i+1]);
33509     },
33510     
33511     setActivePrev : function()
33512     {
33513         var i = this.indexOfItem(this.getActive());
33514         
33515         if (i  < 1) {
33516             return;
33517         }
33518         
33519         this.setActiveItem(this.barItems[i-1]);
33520     },
33521     
33522     format : function()
33523     {
33524         if(!this.barItems.length){
33525             return;
33526         }
33527      
33528         var width = 100 / this.barItems.length;
33529         
33530         Roo.each(this.barItems, function(i){
33531             i.el.setStyle('width', width + '%');
33532             i.topEl.el.setStyle('width', width + '%');
33533             i.bottomEl.el.setStyle('width', width + '%');
33534         }, this);
33535         
33536     }
33537     
33538 });
33539 /*
33540  * - LGPL
33541  *
33542  * Nav Progress Item
33543  * 
33544  */
33545
33546 /**
33547  * @class Roo.bootstrap.NavProgressItem
33548  * @extends Roo.bootstrap.Component
33549  * Bootstrap NavProgressItem class
33550  * @cfg {String} rid the reference id
33551  * @cfg {Boolean} active (true|false) Is item active default false
33552  * @cfg {Boolean} disabled (true|false) Is item active default false
33553  * @cfg {String} html
33554  * @cfg {String} position (top|bottom) text position default bottom
33555  * @cfg {String} icon show icon instead of number
33556  * 
33557  * @constructor
33558  * Create a new NavProgressItem
33559  * @param {Object} config The config object
33560  */
33561 Roo.bootstrap.NavProgressItem = function(config){
33562     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33563     this.addEvents({
33564         // raw events
33565         /**
33566          * @event click
33567          * The raw click event for the entire grid.
33568          * @param {Roo.bootstrap.NavProgressItem} this
33569          * @param {Roo.EventObject} e
33570          */
33571         "click" : true
33572     });
33573    
33574 };
33575
33576 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33577     
33578     rid : '',
33579     active : false,
33580     disabled : false,
33581     html : '',
33582     position : 'bottom',
33583     icon : false,
33584     
33585     getAutoCreate : function()
33586     {
33587         var iconCls = 'roo-navigation-bar-item-icon';
33588         
33589         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33590         
33591         var cfg = {
33592             tag: 'li',
33593             cls: 'roo-navigation-bar-item',
33594             cn : [
33595                 {
33596                     tag : 'i',
33597                     cls : iconCls
33598                 }
33599             ]
33600         };
33601         
33602         if(this.active){
33603             cfg.cls += ' active';
33604         }
33605         if(this.disabled){
33606             cfg.cls += ' disabled';
33607         }
33608         
33609         return cfg;
33610     },
33611     
33612     disable : function()
33613     {
33614         this.setDisabled(true);
33615     },
33616     
33617     enable : function()
33618     {
33619         this.setDisabled(false);
33620     },
33621     
33622     initEvents: function() 
33623     {
33624         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33625         
33626         this.iconEl.on('click', this.onClick, this);
33627     },
33628     
33629     onClick : function(e)
33630     {
33631         e.preventDefault();
33632         
33633         if(this.disabled){
33634             return;
33635         }
33636         
33637         if(this.fireEvent('click', this, e) === false){
33638             return;
33639         };
33640         
33641         this.parent().setActiveItem(this);
33642     },
33643     
33644     isActive: function () 
33645     {
33646         return this.active;
33647     },
33648     
33649     setActive : function(state)
33650     {
33651         if(this.active == state){
33652             return;
33653         }
33654         
33655         this.active = state;
33656         
33657         if (state) {
33658             this.el.addClass('active');
33659             return;
33660         }
33661         
33662         this.el.removeClass('active');
33663         
33664         return;
33665     },
33666     
33667     setDisabled : function(state)
33668     {
33669         if(this.disabled == state){
33670             return;
33671         }
33672         
33673         this.disabled = state;
33674         
33675         if (state) {
33676             this.el.addClass('disabled');
33677             return;
33678         }
33679         
33680         this.el.removeClass('disabled');
33681     },
33682     
33683     tooltipEl : function()
33684     {
33685         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33686     }
33687 });
33688  
33689
33690  /*
33691  * - LGPL
33692  *
33693  * FieldLabel
33694  * 
33695  */
33696
33697 /**
33698  * @class Roo.bootstrap.FieldLabel
33699  * @extends Roo.bootstrap.Component
33700  * Bootstrap FieldLabel class
33701  * @cfg {String} html contents of the element
33702  * @cfg {String} tag tag of the element default label
33703  * @cfg {String} cls class of the element
33704  * @cfg {String} target label target 
33705  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33706  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33707  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33708  * @cfg {String} iconTooltip default "This field is required"
33709  * @cfg {String} indicatorpos (left|right) default left
33710  * 
33711  * @constructor
33712  * Create a new FieldLabel
33713  * @param {Object} config The config object
33714  */
33715
33716 Roo.bootstrap.FieldLabel = function(config){
33717     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33718     
33719     this.addEvents({
33720             /**
33721              * @event invalid
33722              * Fires after the field has been marked as invalid.
33723              * @param {Roo.form.FieldLabel} this
33724              * @param {String} msg The validation message
33725              */
33726             invalid : true,
33727             /**
33728              * @event valid
33729              * Fires after the field has been validated with no errors.
33730              * @param {Roo.form.FieldLabel} this
33731              */
33732             valid : true
33733         });
33734 };
33735
33736 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33737     
33738     tag: 'label',
33739     cls: '',
33740     html: '',
33741     target: '',
33742     allowBlank : true,
33743     invalidClass : 'has-warning',
33744     validClass : 'has-success',
33745     iconTooltip : 'This field is required',
33746     indicatorpos : 'left',
33747     
33748     getAutoCreate : function(){
33749         
33750         var cls = "";
33751         if (!this.allowBlank) {
33752             cls  = "visible";
33753         }
33754         
33755         var cfg = {
33756             tag : this.tag,
33757             cls : 'roo-bootstrap-field-label ' + this.cls,
33758             for : this.target,
33759             cn : [
33760                 {
33761                     tag : 'i',
33762                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33763                     tooltip : this.iconTooltip
33764                 },
33765                 {
33766                     tag : 'span',
33767                     html : this.html
33768                 }
33769             ] 
33770         };
33771         
33772         if(this.indicatorpos == 'right'){
33773             var cfg = {
33774                 tag : this.tag,
33775                 cls : 'roo-bootstrap-field-label ' + this.cls,
33776                 for : this.target,
33777                 cn : [
33778                     {
33779                         tag : 'span',
33780                         html : this.html
33781                     },
33782                     {
33783                         tag : 'i',
33784                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33785                         tooltip : this.iconTooltip
33786                     }
33787                 ] 
33788             };
33789         }
33790         
33791         return cfg;
33792     },
33793     
33794     initEvents: function() 
33795     {
33796         Roo.bootstrap.Element.superclass.initEvents.call(this);
33797         
33798         this.indicator = this.indicatorEl();
33799         
33800         if(this.indicator){
33801             this.indicator.removeClass('visible');
33802             this.indicator.addClass('invisible');
33803         }
33804         
33805         Roo.bootstrap.FieldLabel.register(this);
33806     },
33807     
33808     indicatorEl : function()
33809     {
33810         var indicator = this.el.select('i.roo-required-indicator',true).first();
33811         
33812         if(!indicator){
33813             return false;
33814         }
33815         
33816         return indicator;
33817         
33818     },
33819     
33820     /**
33821      * Mark this field as valid
33822      */
33823     markValid : function()
33824     {
33825         if(this.indicator){
33826             this.indicator.removeClass('visible');
33827             this.indicator.addClass('invisible');
33828         }
33829         if (Roo.bootstrap.version == 3) {
33830             this.el.removeClass(this.invalidClass);
33831             this.el.addClass(this.validClass);
33832         } else {
33833             this.el.removeClass('is-invalid');
33834             this.el.addClass('is-valid');
33835         }
33836         
33837         
33838         this.fireEvent('valid', this);
33839     },
33840     
33841     /**
33842      * Mark this field as invalid
33843      * @param {String} msg The validation message
33844      */
33845     markInvalid : function(msg)
33846     {
33847         if(this.indicator){
33848             this.indicator.removeClass('invisible');
33849             this.indicator.addClass('visible');
33850         }
33851           if (Roo.bootstrap.version == 3) {
33852             this.el.removeClass(this.validClass);
33853             this.el.addClass(this.invalidClass);
33854         } else {
33855             this.el.removeClass('is-valid');
33856             this.el.addClass('is-invalid');
33857         }
33858         
33859         
33860         this.fireEvent('invalid', this, msg);
33861     }
33862     
33863    
33864 });
33865
33866 Roo.apply(Roo.bootstrap.FieldLabel, {
33867     
33868     groups: {},
33869     
33870      /**
33871     * register a FieldLabel Group
33872     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33873     */
33874     register : function(label)
33875     {
33876         if(this.groups.hasOwnProperty(label.target)){
33877             return;
33878         }
33879      
33880         this.groups[label.target] = label;
33881         
33882     },
33883     /**
33884     * fetch a FieldLabel Group based on the target
33885     * @param {string} target
33886     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33887     */
33888     get: function(target) {
33889         if (typeof(this.groups[target]) == 'undefined') {
33890             return false;
33891         }
33892         
33893         return this.groups[target] ;
33894     }
33895 });
33896
33897  
33898
33899  /*
33900  * - LGPL
33901  *
33902  * page DateSplitField.
33903  * 
33904  */
33905
33906
33907 /**
33908  * @class Roo.bootstrap.DateSplitField
33909  * @extends Roo.bootstrap.Component
33910  * Bootstrap DateSplitField class
33911  * @cfg {string} fieldLabel - the label associated
33912  * @cfg {Number} labelWidth set the width of label (0-12)
33913  * @cfg {String} labelAlign (top|left)
33914  * @cfg {Boolean} dayAllowBlank (true|false) default false
33915  * @cfg {Boolean} monthAllowBlank (true|false) default false
33916  * @cfg {Boolean} yearAllowBlank (true|false) default false
33917  * @cfg {string} dayPlaceholder 
33918  * @cfg {string} monthPlaceholder
33919  * @cfg {string} yearPlaceholder
33920  * @cfg {string} dayFormat default 'd'
33921  * @cfg {string} monthFormat default 'm'
33922  * @cfg {string} yearFormat default 'Y'
33923  * @cfg {Number} labellg set the width of label (1-12)
33924  * @cfg {Number} labelmd set the width of label (1-12)
33925  * @cfg {Number} labelsm set the width of label (1-12)
33926  * @cfg {Number} labelxs set the width of label (1-12)
33927
33928  *     
33929  * @constructor
33930  * Create a new DateSplitField
33931  * @param {Object} config The config object
33932  */
33933
33934 Roo.bootstrap.DateSplitField = function(config){
33935     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33936     
33937     this.addEvents({
33938         // raw events
33939          /**
33940          * @event years
33941          * getting the data of years
33942          * @param {Roo.bootstrap.DateSplitField} this
33943          * @param {Object} years
33944          */
33945         "years" : true,
33946         /**
33947          * @event days
33948          * getting the data of days
33949          * @param {Roo.bootstrap.DateSplitField} this
33950          * @param {Object} days
33951          */
33952         "days" : true,
33953         /**
33954          * @event invalid
33955          * Fires after the field has been marked as invalid.
33956          * @param {Roo.form.Field} this
33957          * @param {String} msg The validation message
33958          */
33959         invalid : true,
33960        /**
33961          * @event valid
33962          * Fires after the field has been validated with no errors.
33963          * @param {Roo.form.Field} this
33964          */
33965         valid : true
33966     });
33967 };
33968
33969 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33970     
33971     fieldLabel : '',
33972     labelAlign : 'top',
33973     labelWidth : 3,
33974     dayAllowBlank : false,
33975     monthAllowBlank : false,
33976     yearAllowBlank : false,
33977     dayPlaceholder : '',
33978     monthPlaceholder : '',
33979     yearPlaceholder : '',
33980     dayFormat : 'd',
33981     monthFormat : 'm',
33982     yearFormat : 'Y',
33983     isFormField : true,
33984     labellg : 0,
33985     labelmd : 0,
33986     labelsm : 0,
33987     labelxs : 0,
33988     
33989     getAutoCreate : function()
33990     {
33991         var cfg = {
33992             tag : 'div',
33993             cls : 'row roo-date-split-field-group',
33994             cn : [
33995                 {
33996                     tag : 'input',
33997                     type : 'hidden',
33998                     cls : 'form-hidden-field roo-date-split-field-group-value',
33999                     name : this.name
34000                 }
34001             ]
34002         };
34003         
34004         var labelCls = 'col-md-12';
34005         var contentCls = 'col-md-4';
34006         
34007         if(this.fieldLabel){
34008             
34009             var label = {
34010                 tag : 'div',
34011                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34012                 cn : [
34013                     {
34014                         tag : 'label',
34015                         html : this.fieldLabel
34016                     }
34017                 ]
34018             };
34019             
34020             if(this.labelAlign == 'left'){
34021             
34022                 if(this.labelWidth > 12){
34023                     label.style = "width: " + this.labelWidth + 'px';
34024                 }
34025
34026                 if(this.labelWidth < 13 && this.labelmd == 0){
34027                     this.labelmd = this.labelWidth;
34028                 }
34029
34030                 if(this.labellg > 0){
34031                     labelCls = ' col-lg-' + this.labellg;
34032                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34033                 }
34034
34035                 if(this.labelmd > 0){
34036                     labelCls = ' col-md-' + this.labelmd;
34037                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34038                 }
34039
34040                 if(this.labelsm > 0){
34041                     labelCls = ' col-sm-' + this.labelsm;
34042                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34043                 }
34044
34045                 if(this.labelxs > 0){
34046                     labelCls = ' col-xs-' + this.labelxs;
34047                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34048                 }
34049             }
34050             
34051             label.cls += ' ' + labelCls;
34052             
34053             cfg.cn.push(label);
34054         }
34055         
34056         Roo.each(['day', 'month', 'year'], function(t){
34057             cfg.cn.push({
34058                 tag : 'div',
34059                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34060             });
34061         }, this);
34062         
34063         return cfg;
34064     },
34065     
34066     inputEl: function ()
34067     {
34068         return this.el.select('.roo-date-split-field-group-value', true).first();
34069     },
34070     
34071     onRender : function(ct, position) 
34072     {
34073         var _this = this;
34074         
34075         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34076         
34077         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34078         
34079         this.dayField = new Roo.bootstrap.ComboBox({
34080             allowBlank : this.dayAllowBlank,
34081             alwaysQuery : true,
34082             displayField : 'value',
34083             editable : false,
34084             fieldLabel : '',
34085             forceSelection : true,
34086             mode : 'local',
34087             placeholder : this.dayPlaceholder,
34088             selectOnFocus : true,
34089             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34090             triggerAction : 'all',
34091             typeAhead : true,
34092             valueField : 'value',
34093             store : new Roo.data.SimpleStore({
34094                 data : (function() {    
34095                     var days = [];
34096                     _this.fireEvent('days', _this, days);
34097                     return days;
34098                 })(),
34099                 fields : [ 'value' ]
34100             }),
34101             listeners : {
34102                 select : function (_self, record, index)
34103                 {
34104                     _this.setValue(_this.getValue());
34105                 }
34106             }
34107         });
34108
34109         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34110         
34111         this.monthField = new Roo.bootstrap.MonthField({
34112             after : '<i class=\"fa fa-calendar\"></i>',
34113             allowBlank : this.monthAllowBlank,
34114             placeholder : this.monthPlaceholder,
34115             readOnly : true,
34116             listeners : {
34117                 render : function (_self)
34118                 {
34119                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34120                         e.preventDefault();
34121                         _self.focus();
34122                     });
34123                 },
34124                 select : function (_self, oldvalue, newvalue)
34125                 {
34126                     _this.setValue(_this.getValue());
34127                 }
34128             }
34129         });
34130         
34131         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34132         
34133         this.yearField = new Roo.bootstrap.ComboBox({
34134             allowBlank : this.yearAllowBlank,
34135             alwaysQuery : true,
34136             displayField : 'value',
34137             editable : false,
34138             fieldLabel : '',
34139             forceSelection : true,
34140             mode : 'local',
34141             placeholder : this.yearPlaceholder,
34142             selectOnFocus : true,
34143             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34144             triggerAction : 'all',
34145             typeAhead : true,
34146             valueField : 'value',
34147             store : new Roo.data.SimpleStore({
34148                 data : (function() {
34149                     var years = [];
34150                     _this.fireEvent('years', _this, years);
34151                     return years;
34152                 })(),
34153                 fields : [ 'value' ]
34154             }),
34155             listeners : {
34156                 select : function (_self, record, index)
34157                 {
34158                     _this.setValue(_this.getValue());
34159                 }
34160             }
34161         });
34162
34163         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34164     },
34165     
34166     setValue : function(v, format)
34167     {
34168         this.inputEl.dom.value = v;
34169         
34170         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34171         
34172         var d = Date.parseDate(v, f);
34173         
34174         if(!d){
34175             this.validate();
34176             return;
34177         }
34178         
34179         this.setDay(d.format(this.dayFormat));
34180         this.setMonth(d.format(this.monthFormat));
34181         this.setYear(d.format(this.yearFormat));
34182         
34183         this.validate();
34184         
34185         return;
34186     },
34187     
34188     setDay : function(v)
34189     {
34190         this.dayField.setValue(v);
34191         this.inputEl.dom.value = this.getValue();
34192         this.validate();
34193         return;
34194     },
34195     
34196     setMonth : function(v)
34197     {
34198         this.monthField.setValue(v, true);
34199         this.inputEl.dom.value = this.getValue();
34200         this.validate();
34201         return;
34202     },
34203     
34204     setYear : function(v)
34205     {
34206         this.yearField.setValue(v);
34207         this.inputEl.dom.value = this.getValue();
34208         this.validate();
34209         return;
34210     },
34211     
34212     getDay : function()
34213     {
34214         return this.dayField.getValue();
34215     },
34216     
34217     getMonth : function()
34218     {
34219         return this.monthField.getValue();
34220     },
34221     
34222     getYear : function()
34223     {
34224         return this.yearField.getValue();
34225     },
34226     
34227     getValue : function()
34228     {
34229         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34230         
34231         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34232         
34233         return date;
34234     },
34235     
34236     reset : function()
34237     {
34238         this.setDay('');
34239         this.setMonth('');
34240         this.setYear('');
34241         this.inputEl.dom.value = '';
34242         this.validate();
34243         return;
34244     },
34245     
34246     validate : function()
34247     {
34248         var d = this.dayField.validate();
34249         var m = this.monthField.validate();
34250         var y = this.yearField.validate();
34251         
34252         var valid = true;
34253         
34254         if(
34255                 (!this.dayAllowBlank && !d) ||
34256                 (!this.monthAllowBlank && !m) ||
34257                 (!this.yearAllowBlank && !y)
34258         ){
34259             valid = false;
34260         }
34261         
34262         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34263             return valid;
34264         }
34265         
34266         if(valid){
34267             this.markValid();
34268             return valid;
34269         }
34270         
34271         this.markInvalid();
34272         
34273         return valid;
34274     },
34275     
34276     markValid : function()
34277     {
34278         
34279         var label = this.el.select('label', true).first();
34280         var icon = this.el.select('i.fa-star', true).first();
34281
34282         if(label && icon){
34283             icon.remove();
34284         }
34285         
34286         this.fireEvent('valid', this);
34287     },
34288     
34289      /**
34290      * Mark this field as invalid
34291      * @param {String} msg The validation message
34292      */
34293     markInvalid : function(msg)
34294     {
34295         
34296         var label = this.el.select('label', true).first();
34297         var icon = this.el.select('i.fa-star', true).first();
34298
34299         if(label && !icon){
34300             this.el.select('.roo-date-split-field-label', true).createChild({
34301                 tag : 'i',
34302                 cls : 'text-danger fa fa-lg fa-star',
34303                 tooltip : 'This field is required',
34304                 style : 'margin-right:5px;'
34305             }, label, true);
34306         }
34307         
34308         this.fireEvent('invalid', this, msg);
34309     },
34310     
34311     clearInvalid : function()
34312     {
34313         var label = this.el.select('label', true).first();
34314         var icon = this.el.select('i.fa-star', true).first();
34315
34316         if(label && icon){
34317             icon.remove();
34318         }
34319         
34320         this.fireEvent('valid', this);
34321     },
34322     
34323     getName: function()
34324     {
34325         return this.name;
34326     }
34327     
34328 });
34329
34330  /**
34331  *
34332  * This is based on 
34333  * http://masonry.desandro.com
34334  *
34335  * The idea is to render all the bricks based on vertical width...
34336  *
34337  * The original code extends 'outlayer' - we might need to use that....
34338  * 
34339  */
34340
34341
34342 /**
34343  * @class Roo.bootstrap.LayoutMasonry
34344  * @extends Roo.bootstrap.Component
34345  * Bootstrap Layout Masonry class
34346  * 
34347  * @constructor
34348  * Create a new Element
34349  * @param {Object} config The config object
34350  */
34351
34352 Roo.bootstrap.LayoutMasonry = function(config){
34353     
34354     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34355     
34356     this.bricks = [];
34357     
34358     Roo.bootstrap.LayoutMasonry.register(this);
34359     
34360     this.addEvents({
34361         // raw events
34362         /**
34363          * @event layout
34364          * Fire after layout the items
34365          * @param {Roo.bootstrap.LayoutMasonry} this
34366          * @param {Roo.EventObject} e
34367          */
34368         "layout" : true
34369     });
34370     
34371 };
34372
34373 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34374     
34375     /**
34376      * @cfg {Boolean} isLayoutInstant = no animation?
34377      */   
34378     isLayoutInstant : false, // needed?
34379    
34380     /**
34381      * @cfg {Number} boxWidth  width of the columns
34382      */   
34383     boxWidth : 450,
34384     
34385       /**
34386      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34387      */   
34388     boxHeight : 0,
34389     
34390     /**
34391      * @cfg {Number} padWidth padding below box..
34392      */   
34393     padWidth : 10, 
34394     
34395     /**
34396      * @cfg {Number} gutter gutter width..
34397      */   
34398     gutter : 10,
34399     
34400      /**
34401      * @cfg {Number} maxCols maximum number of columns
34402      */   
34403     
34404     maxCols: 0,
34405     
34406     /**
34407      * @cfg {Boolean} isAutoInitial defalut true
34408      */   
34409     isAutoInitial : true, 
34410     
34411     containerWidth: 0,
34412     
34413     /**
34414      * @cfg {Boolean} isHorizontal defalut false
34415      */   
34416     isHorizontal : false, 
34417
34418     currentSize : null,
34419     
34420     tag: 'div',
34421     
34422     cls: '',
34423     
34424     bricks: null, //CompositeElement
34425     
34426     cols : 1,
34427     
34428     _isLayoutInited : false,
34429     
34430 //    isAlternative : false, // only use for vertical layout...
34431     
34432     /**
34433      * @cfg {Number} alternativePadWidth padding below box..
34434      */   
34435     alternativePadWidth : 50,
34436     
34437     selectedBrick : [],
34438     
34439     getAutoCreate : function(){
34440         
34441         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34442         
34443         var cfg = {
34444             tag: this.tag,
34445             cls: 'blog-masonary-wrapper ' + this.cls,
34446             cn : {
34447                 cls : 'mas-boxes masonary'
34448             }
34449         };
34450         
34451         return cfg;
34452     },
34453     
34454     getChildContainer: function( )
34455     {
34456         if (this.boxesEl) {
34457             return this.boxesEl;
34458         }
34459         
34460         this.boxesEl = this.el.select('.mas-boxes').first();
34461         
34462         return this.boxesEl;
34463     },
34464     
34465     
34466     initEvents : function()
34467     {
34468         var _this = this;
34469         
34470         if(this.isAutoInitial){
34471             Roo.log('hook children rendered');
34472             this.on('childrenrendered', function() {
34473                 Roo.log('children rendered');
34474                 _this.initial();
34475             } ,this);
34476         }
34477     },
34478     
34479     initial : function()
34480     {
34481         this.selectedBrick = [];
34482         
34483         this.currentSize = this.el.getBox(true);
34484         
34485         Roo.EventManager.onWindowResize(this.resize, this); 
34486
34487         if(!this.isAutoInitial){
34488             this.layout();
34489             return;
34490         }
34491         
34492         this.layout();
34493         
34494         return;
34495         //this.layout.defer(500,this);
34496         
34497     },
34498     
34499     resize : function()
34500     {
34501         var cs = this.el.getBox(true);
34502         
34503         if (
34504                 this.currentSize.width == cs.width && 
34505                 this.currentSize.x == cs.x && 
34506                 this.currentSize.height == cs.height && 
34507                 this.currentSize.y == cs.y 
34508         ) {
34509             Roo.log("no change in with or X or Y");
34510             return;
34511         }
34512         
34513         this.currentSize = cs;
34514         
34515         this.layout();
34516         
34517     },
34518     
34519     layout : function()
34520     {   
34521         this._resetLayout();
34522         
34523         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34524         
34525         this.layoutItems( isInstant );
34526       
34527         this._isLayoutInited = true;
34528         
34529         this.fireEvent('layout', this);
34530         
34531     },
34532     
34533     _resetLayout : function()
34534     {
34535         if(this.isHorizontal){
34536             this.horizontalMeasureColumns();
34537             return;
34538         }
34539         
34540         this.verticalMeasureColumns();
34541         
34542     },
34543     
34544     verticalMeasureColumns : function()
34545     {
34546         this.getContainerWidth();
34547         
34548 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34549 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34550 //            return;
34551 //        }
34552         
34553         var boxWidth = this.boxWidth + this.padWidth;
34554         
34555         if(this.containerWidth < this.boxWidth){
34556             boxWidth = this.containerWidth
34557         }
34558         
34559         var containerWidth = this.containerWidth;
34560         
34561         var cols = Math.floor(containerWidth / boxWidth);
34562         
34563         this.cols = Math.max( cols, 1 );
34564         
34565         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34566         
34567         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34568         
34569         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34570         
34571         this.colWidth = boxWidth + avail - this.padWidth;
34572         
34573         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34574         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34575     },
34576     
34577     horizontalMeasureColumns : function()
34578     {
34579         this.getContainerWidth();
34580         
34581         var boxWidth = this.boxWidth;
34582         
34583         if(this.containerWidth < boxWidth){
34584             boxWidth = this.containerWidth;
34585         }
34586         
34587         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34588         
34589         this.el.setHeight(boxWidth);
34590         
34591     },
34592     
34593     getContainerWidth : function()
34594     {
34595         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34596     },
34597     
34598     layoutItems : function( isInstant )
34599     {
34600         Roo.log(this.bricks);
34601         
34602         var items = Roo.apply([], this.bricks);
34603         
34604         if(this.isHorizontal){
34605             this._horizontalLayoutItems( items , isInstant );
34606             return;
34607         }
34608         
34609 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34610 //            this._verticalAlternativeLayoutItems( items , isInstant );
34611 //            return;
34612 //        }
34613         
34614         this._verticalLayoutItems( items , isInstant );
34615         
34616     },
34617     
34618     _verticalLayoutItems : function ( items , isInstant)
34619     {
34620         if ( !items || !items.length ) {
34621             return;
34622         }
34623         
34624         var standard = [
34625             ['xs', 'xs', 'xs', 'tall'],
34626             ['xs', 'xs', 'tall'],
34627             ['xs', 'xs', 'sm'],
34628             ['xs', 'xs', 'xs'],
34629             ['xs', 'tall'],
34630             ['xs', 'sm'],
34631             ['xs', 'xs'],
34632             ['xs'],
34633             
34634             ['sm', 'xs', 'xs'],
34635             ['sm', 'xs'],
34636             ['sm'],
34637             
34638             ['tall', 'xs', 'xs', 'xs'],
34639             ['tall', 'xs', 'xs'],
34640             ['tall', 'xs'],
34641             ['tall']
34642             
34643         ];
34644         
34645         var queue = [];
34646         
34647         var boxes = [];
34648         
34649         var box = [];
34650         
34651         Roo.each(items, function(item, k){
34652             
34653             switch (item.size) {
34654                 // these layouts take up a full box,
34655                 case 'md' :
34656                 case 'md-left' :
34657                 case 'md-right' :
34658                 case 'wide' :
34659                     
34660                     if(box.length){
34661                         boxes.push(box);
34662                         box = [];
34663                     }
34664                     
34665                     boxes.push([item]);
34666                     
34667                     break;
34668                     
34669                 case 'xs' :
34670                 case 'sm' :
34671                 case 'tall' :
34672                     
34673                     box.push(item);
34674                     
34675                     break;
34676                 default :
34677                     break;
34678                     
34679             }
34680             
34681         }, this);
34682         
34683         if(box.length){
34684             boxes.push(box);
34685             box = [];
34686         }
34687         
34688         var filterPattern = function(box, length)
34689         {
34690             if(!box.length){
34691                 return;
34692             }
34693             
34694             var match = false;
34695             
34696             var pattern = box.slice(0, length);
34697             
34698             var format = [];
34699             
34700             Roo.each(pattern, function(i){
34701                 format.push(i.size);
34702             }, this);
34703             
34704             Roo.each(standard, function(s){
34705                 
34706                 if(String(s) != String(format)){
34707                     return;
34708                 }
34709                 
34710                 match = true;
34711                 return false;
34712                 
34713             }, this);
34714             
34715             if(!match && length == 1){
34716                 return;
34717             }
34718             
34719             if(!match){
34720                 filterPattern(box, length - 1);
34721                 return;
34722             }
34723                 
34724             queue.push(pattern);
34725
34726             box = box.slice(length, box.length);
34727
34728             filterPattern(box, 4);
34729
34730             return;
34731             
34732         }
34733         
34734         Roo.each(boxes, function(box, k){
34735             
34736             if(!box.length){
34737                 return;
34738             }
34739             
34740             if(box.length == 1){
34741                 queue.push(box);
34742                 return;
34743             }
34744             
34745             filterPattern(box, 4);
34746             
34747         }, this);
34748         
34749         this._processVerticalLayoutQueue( queue, isInstant );
34750         
34751     },
34752     
34753 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34754 //    {
34755 //        if ( !items || !items.length ) {
34756 //            return;
34757 //        }
34758 //
34759 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34760 //        
34761 //    },
34762     
34763     _horizontalLayoutItems : function ( items , isInstant)
34764     {
34765         if ( !items || !items.length || items.length < 3) {
34766             return;
34767         }
34768         
34769         items.reverse();
34770         
34771         var eItems = items.slice(0, 3);
34772         
34773         items = items.slice(3, items.length);
34774         
34775         var standard = [
34776             ['xs', 'xs', 'xs', 'wide'],
34777             ['xs', 'xs', 'wide'],
34778             ['xs', 'xs', 'sm'],
34779             ['xs', 'xs', 'xs'],
34780             ['xs', 'wide'],
34781             ['xs', 'sm'],
34782             ['xs', 'xs'],
34783             ['xs'],
34784             
34785             ['sm', 'xs', 'xs'],
34786             ['sm', 'xs'],
34787             ['sm'],
34788             
34789             ['wide', 'xs', 'xs', 'xs'],
34790             ['wide', 'xs', 'xs'],
34791             ['wide', 'xs'],
34792             ['wide'],
34793             
34794             ['wide-thin']
34795         ];
34796         
34797         var queue = [];
34798         
34799         var boxes = [];
34800         
34801         var box = [];
34802         
34803         Roo.each(items, function(item, k){
34804             
34805             switch (item.size) {
34806                 case 'md' :
34807                 case 'md-left' :
34808                 case 'md-right' :
34809                 case 'tall' :
34810                     
34811                     if(box.length){
34812                         boxes.push(box);
34813                         box = [];
34814                     }
34815                     
34816                     boxes.push([item]);
34817                     
34818                     break;
34819                     
34820                 case 'xs' :
34821                 case 'sm' :
34822                 case 'wide' :
34823                 case 'wide-thin' :
34824                     
34825                     box.push(item);
34826                     
34827                     break;
34828                 default :
34829                     break;
34830                     
34831             }
34832             
34833         }, this);
34834         
34835         if(box.length){
34836             boxes.push(box);
34837             box = [];
34838         }
34839         
34840         var filterPattern = function(box, length)
34841         {
34842             if(!box.length){
34843                 return;
34844             }
34845             
34846             var match = false;
34847             
34848             var pattern = box.slice(0, length);
34849             
34850             var format = [];
34851             
34852             Roo.each(pattern, function(i){
34853                 format.push(i.size);
34854             }, this);
34855             
34856             Roo.each(standard, function(s){
34857                 
34858                 if(String(s) != String(format)){
34859                     return;
34860                 }
34861                 
34862                 match = true;
34863                 return false;
34864                 
34865             }, this);
34866             
34867             if(!match && length == 1){
34868                 return;
34869             }
34870             
34871             if(!match){
34872                 filterPattern(box, length - 1);
34873                 return;
34874             }
34875                 
34876             queue.push(pattern);
34877
34878             box = box.slice(length, box.length);
34879
34880             filterPattern(box, 4);
34881
34882             return;
34883             
34884         }
34885         
34886         Roo.each(boxes, function(box, k){
34887             
34888             if(!box.length){
34889                 return;
34890             }
34891             
34892             if(box.length == 1){
34893                 queue.push(box);
34894                 return;
34895             }
34896             
34897             filterPattern(box, 4);
34898             
34899         }, this);
34900         
34901         
34902         var prune = [];
34903         
34904         var pos = this.el.getBox(true);
34905         
34906         var minX = pos.x;
34907         
34908         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34909         
34910         var hit_end = false;
34911         
34912         Roo.each(queue, function(box){
34913             
34914             if(hit_end){
34915                 
34916                 Roo.each(box, function(b){
34917                 
34918                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34919                     b.el.hide();
34920
34921                 }, this);
34922
34923                 return;
34924             }
34925             
34926             var mx = 0;
34927             
34928             Roo.each(box, function(b){
34929                 
34930                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34931                 b.el.show();
34932
34933                 mx = Math.max(mx, b.x);
34934                 
34935             }, this);
34936             
34937             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34938             
34939             if(maxX < minX){
34940                 
34941                 Roo.each(box, function(b){
34942                 
34943                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34944                     b.el.hide();
34945                     
34946                 }, this);
34947                 
34948                 hit_end = true;
34949                 
34950                 return;
34951             }
34952             
34953             prune.push(box);
34954             
34955         }, this);
34956         
34957         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34958     },
34959     
34960     /** Sets position of item in DOM
34961     * @param {Element} item
34962     * @param {Number} x - horizontal position
34963     * @param {Number} y - vertical position
34964     * @param {Boolean} isInstant - disables transitions
34965     */
34966     _processVerticalLayoutQueue : function( queue, isInstant )
34967     {
34968         var pos = this.el.getBox(true);
34969         var x = pos.x;
34970         var y = pos.y;
34971         var maxY = [];
34972         
34973         for (var i = 0; i < this.cols; i++){
34974             maxY[i] = pos.y;
34975         }
34976         
34977         Roo.each(queue, function(box, k){
34978             
34979             var col = k % this.cols;
34980             
34981             Roo.each(box, function(b,kk){
34982                 
34983                 b.el.position('absolute');
34984                 
34985                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34986                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34987                 
34988                 if(b.size == 'md-left' || b.size == 'md-right'){
34989                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34990                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34991                 }
34992                 
34993                 b.el.setWidth(width);
34994                 b.el.setHeight(height);
34995                 // iframe?
34996                 b.el.select('iframe',true).setSize(width,height);
34997                 
34998             }, this);
34999             
35000             for (var i = 0; i < this.cols; i++){
35001                 
35002                 if(maxY[i] < maxY[col]){
35003                     col = i;
35004                     continue;
35005                 }
35006                 
35007                 col = Math.min(col, i);
35008                 
35009             }
35010             
35011             x = pos.x + col * (this.colWidth + this.padWidth);
35012             
35013             y = maxY[col];
35014             
35015             var positions = [];
35016             
35017             switch (box.length){
35018                 case 1 :
35019                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35020                     break;
35021                 case 2 :
35022                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35023                     break;
35024                 case 3 :
35025                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35026                     break;
35027                 case 4 :
35028                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35029                     break;
35030                 default :
35031                     break;
35032             }
35033             
35034             Roo.each(box, function(b,kk){
35035                 
35036                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35037                 
35038                 var sz = b.el.getSize();
35039                 
35040                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35041                 
35042             }, this);
35043             
35044         }, this);
35045         
35046         var mY = 0;
35047         
35048         for (var i = 0; i < this.cols; i++){
35049             mY = Math.max(mY, maxY[i]);
35050         }
35051         
35052         this.el.setHeight(mY - pos.y);
35053         
35054     },
35055     
35056 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35057 //    {
35058 //        var pos = this.el.getBox(true);
35059 //        var x = pos.x;
35060 //        var y = pos.y;
35061 //        var maxX = pos.right;
35062 //        
35063 //        var maxHeight = 0;
35064 //        
35065 //        Roo.each(items, function(item, k){
35066 //            
35067 //            var c = k % 2;
35068 //            
35069 //            item.el.position('absolute');
35070 //                
35071 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35072 //
35073 //            item.el.setWidth(width);
35074 //
35075 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35076 //
35077 //            item.el.setHeight(height);
35078 //            
35079 //            if(c == 0){
35080 //                item.el.setXY([x, y], isInstant ? false : true);
35081 //            } else {
35082 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35083 //            }
35084 //            
35085 //            y = y + height + this.alternativePadWidth;
35086 //            
35087 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35088 //            
35089 //        }, this);
35090 //        
35091 //        this.el.setHeight(maxHeight);
35092 //        
35093 //    },
35094     
35095     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35096     {
35097         var pos = this.el.getBox(true);
35098         
35099         var minX = pos.x;
35100         var minY = pos.y;
35101         
35102         var maxX = pos.right;
35103         
35104         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35105         
35106         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35107         
35108         Roo.each(queue, function(box, k){
35109             
35110             Roo.each(box, function(b, kk){
35111                 
35112                 b.el.position('absolute');
35113                 
35114                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35115                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35116                 
35117                 if(b.size == 'md-left' || b.size == 'md-right'){
35118                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35119                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35120                 }
35121                 
35122                 b.el.setWidth(width);
35123                 b.el.setHeight(height);
35124                 
35125             }, this);
35126             
35127             if(!box.length){
35128                 return;
35129             }
35130             
35131             var positions = [];
35132             
35133             switch (box.length){
35134                 case 1 :
35135                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35136                     break;
35137                 case 2 :
35138                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35139                     break;
35140                 case 3 :
35141                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35142                     break;
35143                 case 4 :
35144                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35145                     break;
35146                 default :
35147                     break;
35148             }
35149             
35150             Roo.each(box, function(b,kk){
35151                 
35152                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35153                 
35154                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35155                 
35156             }, this);
35157             
35158         }, this);
35159         
35160     },
35161     
35162     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35163     {
35164         Roo.each(eItems, function(b,k){
35165             
35166             b.size = (k == 0) ? 'sm' : 'xs';
35167             b.x = (k == 0) ? 2 : 1;
35168             b.y = (k == 0) ? 2 : 1;
35169             
35170             b.el.position('absolute');
35171             
35172             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35173                 
35174             b.el.setWidth(width);
35175             
35176             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35177             
35178             b.el.setHeight(height);
35179             
35180         }, this);
35181
35182         var positions = [];
35183         
35184         positions.push({
35185             x : maxX - this.unitWidth * 2 - this.gutter,
35186             y : minY
35187         });
35188         
35189         positions.push({
35190             x : maxX - this.unitWidth,
35191             y : minY + (this.unitWidth + this.gutter) * 2
35192         });
35193         
35194         positions.push({
35195             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35196             y : minY
35197         });
35198         
35199         Roo.each(eItems, function(b,k){
35200             
35201             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35202
35203         }, this);
35204         
35205     },
35206     
35207     getVerticalOneBoxColPositions : function(x, y, box)
35208     {
35209         var pos = [];
35210         
35211         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35212         
35213         if(box[0].size == 'md-left'){
35214             rand = 0;
35215         }
35216         
35217         if(box[0].size == 'md-right'){
35218             rand = 1;
35219         }
35220         
35221         pos.push({
35222             x : x + (this.unitWidth + this.gutter) * rand,
35223             y : y
35224         });
35225         
35226         return pos;
35227     },
35228     
35229     getVerticalTwoBoxColPositions : function(x, y, box)
35230     {
35231         var pos = [];
35232         
35233         if(box[0].size == 'xs'){
35234             
35235             pos.push({
35236                 x : x,
35237                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35238             });
35239
35240             pos.push({
35241                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35242                 y : y
35243             });
35244             
35245             return pos;
35246             
35247         }
35248         
35249         pos.push({
35250             x : x,
35251             y : y
35252         });
35253
35254         pos.push({
35255             x : x + (this.unitWidth + this.gutter) * 2,
35256             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35257         });
35258         
35259         return pos;
35260         
35261     },
35262     
35263     getVerticalThreeBoxColPositions : function(x, y, box)
35264     {
35265         var pos = [];
35266         
35267         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35268             
35269             pos.push({
35270                 x : x,
35271                 y : y
35272             });
35273
35274             pos.push({
35275                 x : x + (this.unitWidth + this.gutter) * 1,
35276                 y : y
35277             });
35278             
35279             pos.push({
35280                 x : x + (this.unitWidth + this.gutter) * 2,
35281                 y : y
35282             });
35283             
35284             return pos;
35285             
35286         }
35287         
35288         if(box[0].size == 'xs' && box[1].size == 'xs'){
35289             
35290             pos.push({
35291                 x : x,
35292                 y : y
35293             });
35294
35295             pos.push({
35296                 x : x,
35297                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35298             });
35299             
35300             pos.push({
35301                 x : x + (this.unitWidth + this.gutter) * 1,
35302                 y : y
35303             });
35304             
35305             return pos;
35306             
35307         }
35308         
35309         pos.push({
35310             x : x,
35311             y : y
35312         });
35313
35314         pos.push({
35315             x : x + (this.unitWidth + this.gutter) * 2,
35316             y : y
35317         });
35318
35319         pos.push({
35320             x : x + (this.unitWidth + this.gutter) * 2,
35321             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35322         });
35323             
35324         return pos;
35325         
35326     },
35327     
35328     getVerticalFourBoxColPositions : function(x, y, box)
35329     {
35330         var pos = [];
35331         
35332         if(box[0].size == 'xs'){
35333             
35334             pos.push({
35335                 x : x,
35336                 y : y
35337             });
35338
35339             pos.push({
35340                 x : x,
35341                 y : y + (this.unitHeight + this.gutter) * 1
35342             });
35343             
35344             pos.push({
35345                 x : x,
35346                 y : y + (this.unitHeight + this.gutter) * 2
35347             });
35348             
35349             pos.push({
35350                 x : x + (this.unitWidth + this.gutter) * 1,
35351                 y : y
35352             });
35353             
35354             return pos;
35355             
35356         }
35357         
35358         pos.push({
35359             x : x,
35360             y : y
35361         });
35362
35363         pos.push({
35364             x : x + (this.unitWidth + this.gutter) * 2,
35365             y : y
35366         });
35367
35368         pos.push({
35369             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35370             y : y + (this.unitHeight + this.gutter) * 1
35371         });
35372
35373         pos.push({
35374             x : x + (this.unitWidth + this.gutter) * 2,
35375             y : y + (this.unitWidth + this.gutter) * 2
35376         });
35377
35378         return pos;
35379         
35380     },
35381     
35382     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35383     {
35384         var pos = [];
35385         
35386         if(box[0].size == 'md-left'){
35387             pos.push({
35388                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35389                 y : minY
35390             });
35391             
35392             return pos;
35393         }
35394         
35395         if(box[0].size == 'md-right'){
35396             pos.push({
35397                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35398                 y : minY + (this.unitWidth + this.gutter) * 1
35399             });
35400             
35401             return pos;
35402         }
35403         
35404         var rand = Math.floor(Math.random() * (4 - box[0].y));
35405         
35406         pos.push({
35407             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35408             y : minY + (this.unitWidth + this.gutter) * rand
35409         });
35410         
35411         return pos;
35412         
35413     },
35414     
35415     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35416     {
35417         var pos = [];
35418         
35419         if(box[0].size == 'xs'){
35420             
35421             pos.push({
35422                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35423                 y : minY
35424             });
35425
35426             pos.push({
35427                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35428                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35429             });
35430             
35431             return pos;
35432             
35433         }
35434         
35435         pos.push({
35436             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35437             y : minY
35438         });
35439
35440         pos.push({
35441             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35442             y : minY + (this.unitWidth + this.gutter) * 2
35443         });
35444         
35445         return pos;
35446         
35447     },
35448     
35449     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35450     {
35451         var pos = [];
35452         
35453         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35454             
35455             pos.push({
35456                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35457                 y : minY
35458             });
35459
35460             pos.push({
35461                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35462                 y : minY + (this.unitWidth + this.gutter) * 1
35463             });
35464             
35465             pos.push({
35466                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35467                 y : minY + (this.unitWidth + this.gutter) * 2
35468             });
35469             
35470             return pos;
35471             
35472         }
35473         
35474         if(box[0].size == 'xs' && box[1].size == 'xs'){
35475             
35476             pos.push({
35477                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35478                 y : minY
35479             });
35480
35481             pos.push({
35482                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35483                 y : minY
35484             });
35485             
35486             pos.push({
35487                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35488                 y : minY + (this.unitWidth + this.gutter) * 1
35489             });
35490             
35491             return pos;
35492             
35493         }
35494         
35495         pos.push({
35496             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35497             y : minY
35498         });
35499
35500         pos.push({
35501             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35502             y : minY + (this.unitWidth + this.gutter) * 2
35503         });
35504
35505         pos.push({
35506             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35507             y : minY + (this.unitWidth + this.gutter) * 2
35508         });
35509             
35510         return pos;
35511         
35512     },
35513     
35514     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35515     {
35516         var pos = [];
35517         
35518         if(box[0].size == 'xs'){
35519             
35520             pos.push({
35521                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35522                 y : minY
35523             });
35524
35525             pos.push({
35526                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35527                 y : minY
35528             });
35529             
35530             pos.push({
35531                 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),
35532                 y : minY
35533             });
35534             
35535             pos.push({
35536                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35537                 y : minY + (this.unitWidth + this.gutter) * 1
35538             });
35539             
35540             return pos;
35541             
35542         }
35543         
35544         pos.push({
35545             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35546             y : minY
35547         });
35548         
35549         pos.push({
35550             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35551             y : minY + (this.unitWidth + this.gutter) * 2
35552         });
35553         
35554         pos.push({
35555             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35556             y : minY + (this.unitWidth + this.gutter) * 2
35557         });
35558         
35559         pos.push({
35560             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),
35561             y : minY + (this.unitWidth + this.gutter) * 2
35562         });
35563
35564         return pos;
35565         
35566     },
35567     
35568     /**
35569     * remove a Masonry Brick
35570     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35571     */
35572     removeBrick : function(brick_id)
35573     {
35574         if (!brick_id) {
35575             return;
35576         }
35577         
35578         for (var i = 0; i<this.bricks.length; i++) {
35579             if (this.bricks[i].id == brick_id) {
35580                 this.bricks.splice(i,1);
35581                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35582                 this.initial();
35583             }
35584         }
35585     },
35586     
35587     /**
35588     * adds a Masonry Brick
35589     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35590     */
35591     addBrick : function(cfg)
35592     {
35593         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35594         //this.register(cn);
35595         cn.parentId = this.id;
35596         cn.render(this.el);
35597         return cn;
35598     },
35599     
35600     /**
35601     * register a Masonry Brick
35602     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35603     */
35604     
35605     register : function(brick)
35606     {
35607         this.bricks.push(brick);
35608         brick.masonryId = this.id;
35609     },
35610     
35611     /**
35612     * clear all the Masonry Brick
35613     */
35614     clearAll : function()
35615     {
35616         this.bricks = [];
35617         //this.getChildContainer().dom.innerHTML = "";
35618         this.el.dom.innerHTML = '';
35619     },
35620     
35621     getSelected : function()
35622     {
35623         if (!this.selectedBrick) {
35624             return false;
35625         }
35626         
35627         return this.selectedBrick;
35628     }
35629 });
35630
35631 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35632     
35633     groups: {},
35634      /**
35635     * register a Masonry Layout
35636     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35637     */
35638     
35639     register : function(layout)
35640     {
35641         this.groups[layout.id] = layout;
35642     },
35643     /**
35644     * fetch a  Masonry Layout based on the masonry layout ID
35645     * @param {string} the masonry layout to add
35646     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35647     */
35648     
35649     get: function(layout_id) {
35650         if (typeof(this.groups[layout_id]) == 'undefined') {
35651             return false;
35652         }
35653         return this.groups[layout_id] ;
35654     }
35655     
35656     
35657     
35658 });
35659
35660  
35661
35662  /**
35663  *
35664  * This is based on 
35665  * http://masonry.desandro.com
35666  *
35667  * The idea is to render all the bricks based on vertical width...
35668  *
35669  * The original code extends 'outlayer' - we might need to use that....
35670  * 
35671  */
35672
35673
35674 /**
35675  * @class Roo.bootstrap.LayoutMasonryAuto
35676  * @extends Roo.bootstrap.Component
35677  * Bootstrap Layout Masonry class
35678  * 
35679  * @constructor
35680  * Create a new Element
35681  * @param {Object} config The config object
35682  */
35683
35684 Roo.bootstrap.LayoutMasonryAuto = function(config){
35685     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35686 };
35687
35688 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35689     
35690       /**
35691      * @cfg {Boolean} isFitWidth  - resize the width..
35692      */   
35693     isFitWidth : false,  // options..
35694     /**
35695      * @cfg {Boolean} isOriginLeft = left align?
35696      */   
35697     isOriginLeft : true,
35698     /**
35699      * @cfg {Boolean} isOriginTop = top align?
35700      */   
35701     isOriginTop : false,
35702     /**
35703      * @cfg {Boolean} isLayoutInstant = no animation?
35704      */   
35705     isLayoutInstant : false, // needed?
35706     /**
35707      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35708      */   
35709     isResizingContainer : true,
35710     /**
35711      * @cfg {Number} columnWidth  width of the columns 
35712      */   
35713     
35714     columnWidth : 0,
35715     
35716     /**
35717      * @cfg {Number} maxCols maximum number of columns
35718      */   
35719     
35720     maxCols: 0,
35721     /**
35722      * @cfg {Number} padHeight padding below box..
35723      */   
35724     
35725     padHeight : 10, 
35726     
35727     /**
35728      * @cfg {Boolean} isAutoInitial defalut true
35729      */   
35730     
35731     isAutoInitial : true, 
35732     
35733     // private?
35734     gutter : 0,
35735     
35736     containerWidth: 0,
35737     initialColumnWidth : 0,
35738     currentSize : null,
35739     
35740     colYs : null, // array.
35741     maxY : 0,
35742     padWidth: 10,
35743     
35744     
35745     tag: 'div',
35746     cls: '',
35747     bricks: null, //CompositeElement
35748     cols : 0, // array?
35749     // element : null, // wrapped now this.el
35750     _isLayoutInited : null, 
35751     
35752     
35753     getAutoCreate : function(){
35754         
35755         var cfg = {
35756             tag: this.tag,
35757             cls: 'blog-masonary-wrapper ' + this.cls,
35758             cn : {
35759                 cls : 'mas-boxes masonary'
35760             }
35761         };
35762         
35763         return cfg;
35764     },
35765     
35766     getChildContainer: function( )
35767     {
35768         if (this.boxesEl) {
35769             return this.boxesEl;
35770         }
35771         
35772         this.boxesEl = this.el.select('.mas-boxes').first();
35773         
35774         return this.boxesEl;
35775     },
35776     
35777     
35778     initEvents : function()
35779     {
35780         var _this = this;
35781         
35782         if(this.isAutoInitial){
35783             Roo.log('hook children rendered');
35784             this.on('childrenrendered', function() {
35785                 Roo.log('children rendered');
35786                 _this.initial();
35787             } ,this);
35788         }
35789         
35790     },
35791     
35792     initial : function()
35793     {
35794         this.reloadItems();
35795
35796         this.currentSize = this.el.getBox(true);
35797
35798         /// was window resize... - let's see if this works..
35799         Roo.EventManager.onWindowResize(this.resize, this); 
35800
35801         if(!this.isAutoInitial){
35802             this.layout();
35803             return;
35804         }
35805         
35806         this.layout.defer(500,this);
35807     },
35808     
35809     reloadItems: function()
35810     {
35811         this.bricks = this.el.select('.masonry-brick', true);
35812         
35813         this.bricks.each(function(b) {
35814             //Roo.log(b.getSize());
35815             if (!b.attr('originalwidth')) {
35816                 b.attr('originalwidth',  b.getSize().width);
35817             }
35818             
35819         });
35820         
35821         Roo.log(this.bricks.elements.length);
35822     },
35823     
35824     resize : function()
35825     {
35826         Roo.log('resize');
35827         var cs = this.el.getBox(true);
35828         
35829         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35830             Roo.log("no change in with or X");
35831             return;
35832         }
35833         this.currentSize = cs;
35834         this.layout();
35835     },
35836     
35837     layout : function()
35838     {
35839          Roo.log('layout');
35840         this._resetLayout();
35841         //this._manageStamps();
35842       
35843         // don't animate first layout
35844         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35845         this.layoutItems( isInstant );
35846       
35847         // flag for initalized
35848         this._isLayoutInited = true;
35849     },
35850     
35851     layoutItems : function( isInstant )
35852     {
35853         //var items = this._getItemsForLayout( this.items );
35854         // original code supports filtering layout items.. we just ignore it..
35855         
35856         this._layoutItems( this.bricks , isInstant );
35857       
35858         this._postLayout();
35859     },
35860     _layoutItems : function ( items , isInstant)
35861     {
35862        //this.fireEvent( 'layout', this, items );
35863     
35864
35865         if ( !items || !items.elements.length ) {
35866           // no items, emit event with empty array
35867             return;
35868         }
35869
35870         var queue = [];
35871         items.each(function(item) {
35872             Roo.log("layout item");
35873             Roo.log(item);
35874             // get x/y object from method
35875             var position = this._getItemLayoutPosition( item );
35876             // enqueue
35877             position.item = item;
35878             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35879             queue.push( position );
35880         }, this);
35881       
35882         this._processLayoutQueue( queue );
35883     },
35884     /** Sets position of item in DOM
35885     * @param {Element} item
35886     * @param {Number} x - horizontal position
35887     * @param {Number} y - vertical position
35888     * @param {Boolean} isInstant - disables transitions
35889     */
35890     _processLayoutQueue : function( queue )
35891     {
35892         for ( var i=0, len = queue.length; i < len; i++ ) {
35893             var obj = queue[i];
35894             obj.item.position('absolute');
35895             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35896         }
35897     },
35898       
35899     
35900     /**
35901     * Any logic you want to do after each layout,
35902     * i.e. size the container
35903     */
35904     _postLayout : function()
35905     {
35906         this.resizeContainer();
35907     },
35908     
35909     resizeContainer : function()
35910     {
35911         if ( !this.isResizingContainer ) {
35912             return;
35913         }
35914         var size = this._getContainerSize();
35915         if ( size ) {
35916             this.el.setSize(size.width,size.height);
35917             this.boxesEl.setSize(size.width,size.height);
35918         }
35919     },
35920     
35921     
35922     
35923     _resetLayout : function()
35924     {
35925         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35926         this.colWidth = this.el.getWidth();
35927         //this.gutter = this.el.getWidth(); 
35928         
35929         this.measureColumns();
35930
35931         // reset column Y
35932         var i = this.cols;
35933         this.colYs = [];
35934         while (i--) {
35935             this.colYs.push( 0 );
35936         }
35937     
35938         this.maxY = 0;
35939     },
35940
35941     measureColumns : function()
35942     {
35943         this.getContainerWidth();
35944       // if columnWidth is 0, default to outerWidth of first item
35945         if ( !this.columnWidth ) {
35946             var firstItem = this.bricks.first();
35947             Roo.log(firstItem);
35948             this.columnWidth  = this.containerWidth;
35949             if (firstItem && firstItem.attr('originalwidth') ) {
35950                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35951             }
35952             // columnWidth fall back to item of first element
35953             Roo.log("set column width?");
35954                         this.initialColumnWidth = this.columnWidth  ;
35955
35956             // if first elem has no width, default to size of container
35957             
35958         }
35959         
35960         
35961         if (this.initialColumnWidth) {
35962             this.columnWidth = this.initialColumnWidth;
35963         }
35964         
35965         
35966             
35967         // column width is fixed at the top - however if container width get's smaller we should
35968         // reduce it...
35969         
35970         // this bit calcs how man columns..
35971             
35972         var columnWidth = this.columnWidth += this.gutter;
35973       
35974         // calculate columns
35975         var containerWidth = this.containerWidth + this.gutter;
35976         
35977         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35978         // fix rounding errors, typically with gutters
35979         var excess = columnWidth - containerWidth % columnWidth;
35980         
35981         
35982         // if overshoot is less than a pixel, round up, otherwise floor it
35983         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35984         cols = Math[ mathMethod ]( cols );
35985         this.cols = Math.max( cols, 1 );
35986         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35987         
35988          // padding positioning..
35989         var totalColWidth = this.cols * this.columnWidth;
35990         var padavail = this.containerWidth - totalColWidth;
35991         // so for 2 columns - we need 3 'pads'
35992         
35993         var padNeeded = (1+this.cols) * this.padWidth;
35994         
35995         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35996         
35997         this.columnWidth += padExtra
35998         //this.padWidth = Math.floor(padavail /  ( this.cols));
35999         
36000         // adjust colum width so that padding is fixed??
36001         
36002         // we have 3 columns ... total = width * 3
36003         // we have X left over... that should be used by 
36004         
36005         //if (this.expandC) {
36006             
36007         //}
36008         
36009         
36010         
36011     },
36012     
36013     getContainerWidth : function()
36014     {
36015        /* // container is parent if fit width
36016         var container = this.isFitWidth ? this.element.parentNode : this.element;
36017         // check that this.size and size are there
36018         // IE8 triggers resize on body size change, so they might not be
36019         
36020         var size = getSize( container );  //FIXME
36021         this.containerWidth = size && size.innerWidth; //FIXME
36022         */
36023          
36024         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36025         
36026     },
36027     
36028     _getItemLayoutPosition : function( item )  // what is item?
36029     {
36030         // we resize the item to our columnWidth..
36031       
36032         item.setWidth(this.columnWidth);
36033         item.autoBoxAdjust  = false;
36034         
36035         var sz = item.getSize();
36036  
36037         // how many columns does this brick span
36038         var remainder = this.containerWidth % this.columnWidth;
36039         
36040         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36041         // round if off by 1 pixel, otherwise use ceil
36042         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36043         colSpan = Math.min( colSpan, this.cols );
36044         
36045         // normally this should be '1' as we dont' currently allow multi width columns..
36046         
36047         var colGroup = this._getColGroup( colSpan );
36048         // get the minimum Y value from the columns
36049         var minimumY = Math.min.apply( Math, colGroup );
36050         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36051         
36052         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36053          
36054         // position the brick
36055         var position = {
36056             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36057             y: this.currentSize.y + minimumY + this.padHeight
36058         };
36059         
36060         Roo.log(position);
36061         // apply setHeight to necessary columns
36062         var setHeight = minimumY + sz.height + this.padHeight;
36063         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36064         
36065         var setSpan = this.cols + 1 - colGroup.length;
36066         for ( var i = 0; i < setSpan; i++ ) {
36067           this.colYs[ shortColIndex + i ] = setHeight ;
36068         }
36069       
36070         return position;
36071     },
36072     
36073     /**
36074      * @param {Number} colSpan - number of columns the element spans
36075      * @returns {Array} colGroup
36076      */
36077     _getColGroup : function( colSpan )
36078     {
36079         if ( colSpan < 2 ) {
36080           // if brick spans only one column, use all the column Ys
36081           return this.colYs;
36082         }
36083       
36084         var colGroup = [];
36085         // how many different places could this brick fit horizontally
36086         var groupCount = this.cols + 1 - colSpan;
36087         // for each group potential horizontal position
36088         for ( var i = 0; i < groupCount; i++ ) {
36089           // make an array of colY values for that one group
36090           var groupColYs = this.colYs.slice( i, i + colSpan );
36091           // and get the max value of the array
36092           colGroup[i] = Math.max.apply( Math, groupColYs );
36093         }
36094         return colGroup;
36095     },
36096     /*
36097     _manageStamp : function( stamp )
36098     {
36099         var stampSize =  stamp.getSize();
36100         var offset = stamp.getBox();
36101         // get the columns that this stamp affects
36102         var firstX = this.isOriginLeft ? offset.x : offset.right;
36103         var lastX = firstX + stampSize.width;
36104         var firstCol = Math.floor( firstX / this.columnWidth );
36105         firstCol = Math.max( 0, firstCol );
36106         
36107         var lastCol = Math.floor( lastX / this.columnWidth );
36108         // lastCol should not go over if multiple of columnWidth #425
36109         lastCol -= lastX % this.columnWidth ? 0 : 1;
36110         lastCol = Math.min( this.cols - 1, lastCol );
36111         
36112         // set colYs to bottom of the stamp
36113         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36114             stampSize.height;
36115             
36116         for ( var i = firstCol; i <= lastCol; i++ ) {
36117           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36118         }
36119     },
36120     */
36121     
36122     _getContainerSize : function()
36123     {
36124         this.maxY = Math.max.apply( Math, this.colYs );
36125         var size = {
36126             height: this.maxY
36127         };
36128       
36129         if ( this.isFitWidth ) {
36130             size.width = this._getContainerFitWidth();
36131         }
36132       
36133         return size;
36134     },
36135     
36136     _getContainerFitWidth : function()
36137     {
36138         var unusedCols = 0;
36139         // count unused columns
36140         var i = this.cols;
36141         while ( --i ) {
36142           if ( this.colYs[i] !== 0 ) {
36143             break;
36144           }
36145           unusedCols++;
36146         }
36147         // fit container to columns that have been used
36148         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36149     },
36150     
36151     needsResizeLayout : function()
36152     {
36153         var previousWidth = this.containerWidth;
36154         this.getContainerWidth();
36155         return previousWidth !== this.containerWidth;
36156     }
36157  
36158 });
36159
36160  
36161
36162  /*
36163  * - LGPL
36164  *
36165  * element
36166  * 
36167  */
36168
36169 /**
36170  * @class Roo.bootstrap.MasonryBrick
36171  * @extends Roo.bootstrap.Component
36172  * Bootstrap MasonryBrick class
36173  * 
36174  * @constructor
36175  * Create a new MasonryBrick
36176  * @param {Object} config The config object
36177  */
36178
36179 Roo.bootstrap.MasonryBrick = function(config){
36180     
36181     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36182     
36183     Roo.bootstrap.MasonryBrick.register(this);
36184     
36185     this.addEvents({
36186         // raw events
36187         /**
36188          * @event click
36189          * When a MasonryBrick is clcik
36190          * @param {Roo.bootstrap.MasonryBrick} this
36191          * @param {Roo.EventObject} e
36192          */
36193         "click" : true
36194     });
36195 };
36196
36197 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36198     
36199     /**
36200      * @cfg {String} title
36201      */   
36202     title : '',
36203     /**
36204      * @cfg {String} html
36205      */   
36206     html : '',
36207     /**
36208      * @cfg {String} bgimage
36209      */   
36210     bgimage : '',
36211     /**
36212      * @cfg {String} videourl
36213      */   
36214     videourl : '',
36215     /**
36216      * @cfg {String} cls
36217      */   
36218     cls : '',
36219     /**
36220      * @cfg {String} href
36221      */   
36222     href : '',
36223     /**
36224      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36225      */   
36226     size : 'xs',
36227     
36228     /**
36229      * @cfg {String} placetitle (center|bottom)
36230      */   
36231     placetitle : '',
36232     
36233     /**
36234      * @cfg {Boolean} isFitContainer defalut true
36235      */   
36236     isFitContainer : true, 
36237     
36238     /**
36239      * @cfg {Boolean} preventDefault defalut false
36240      */   
36241     preventDefault : false, 
36242     
36243     /**
36244      * @cfg {Boolean} inverse defalut false
36245      */   
36246     maskInverse : false, 
36247     
36248     getAutoCreate : function()
36249     {
36250         if(!this.isFitContainer){
36251             return this.getSplitAutoCreate();
36252         }
36253         
36254         var cls = 'masonry-brick masonry-brick-full';
36255         
36256         if(this.href.length){
36257             cls += ' masonry-brick-link';
36258         }
36259         
36260         if(this.bgimage.length){
36261             cls += ' masonry-brick-image';
36262         }
36263         
36264         if(this.maskInverse){
36265             cls += ' mask-inverse';
36266         }
36267         
36268         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36269             cls += ' enable-mask';
36270         }
36271         
36272         if(this.size){
36273             cls += ' masonry-' + this.size + '-brick';
36274         }
36275         
36276         if(this.placetitle.length){
36277             
36278             switch (this.placetitle) {
36279                 case 'center' :
36280                     cls += ' masonry-center-title';
36281                     break;
36282                 case 'bottom' :
36283                     cls += ' masonry-bottom-title';
36284                     break;
36285                 default:
36286                     break;
36287             }
36288             
36289         } else {
36290             if(!this.html.length && !this.bgimage.length){
36291                 cls += ' masonry-center-title';
36292             }
36293
36294             if(!this.html.length && this.bgimage.length){
36295                 cls += ' masonry-bottom-title';
36296             }
36297         }
36298         
36299         if(this.cls){
36300             cls += ' ' + this.cls;
36301         }
36302         
36303         var cfg = {
36304             tag: (this.href.length) ? 'a' : 'div',
36305             cls: cls,
36306             cn: [
36307                 {
36308                     tag: 'div',
36309                     cls: 'masonry-brick-mask'
36310                 },
36311                 {
36312                     tag: 'div',
36313                     cls: 'masonry-brick-paragraph',
36314                     cn: []
36315                 }
36316             ]
36317         };
36318         
36319         if(this.href.length){
36320             cfg.href = this.href;
36321         }
36322         
36323         var cn = cfg.cn[1].cn;
36324         
36325         if(this.title.length){
36326             cn.push({
36327                 tag: 'h4',
36328                 cls: 'masonry-brick-title',
36329                 html: this.title
36330             });
36331         }
36332         
36333         if(this.html.length){
36334             cn.push({
36335                 tag: 'p',
36336                 cls: 'masonry-brick-text',
36337                 html: this.html
36338             });
36339         }
36340         
36341         if (!this.title.length && !this.html.length) {
36342             cfg.cn[1].cls += ' hide';
36343         }
36344         
36345         if(this.bgimage.length){
36346             cfg.cn.push({
36347                 tag: 'img',
36348                 cls: 'masonry-brick-image-view',
36349                 src: this.bgimage
36350             });
36351         }
36352         
36353         if(this.videourl.length){
36354             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36355             // youtube support only?
36356             cfg.cn.push({
36357                 tag: 'iframe',
36358                 cls: 'masonry-brick-image-view',
36359                 src: vurl,
36360                 frameborder : 0,
36361                 allowfullscreen : true
36362             });
36363         }
36364         
36365         return cfg;
36366         
36367     },
36368     
36369     getSplitAutoCreate : function()
36370     {
36371         var cls = 'masonry-brick masonry-brick-split';
36372         
36373         if(this.href.length){
36374             cls += ' masonry-brick-link';
36375         }
36376         
36377         if(this.bgimage.length){
36378             cls += ' masonry-brick-image';
36379         }
36380         
36381         if(this.size){
36382             cls += ' masonry-' + this.size + '-brick';
36383         }
36384         
36385         switch (this.placetitle) {
36386             case 'center' :
36387                 cls += ' masonry-center-title';
36388                 break;
36389             case 'bottom' :
36390                 cls += ' masonry-bottom-title';
36391                 break;
36392             default:
36393                 if(!this.bgimage.length){
36394                     cls += ' masonry-center-title';
36395                 }
36396
36397                 if(this.bgimage.length){
36398                     cls += ' masonry-bottom-title';
36399                 }
36400                 break;
36401         }
36402         
36403         if(this.cls){
36404             cls += ' ' + this.cls;
36405         }
36406         
36407         var cfg = {
36408             tag: (this.href.length) ? 'a' : 'div',
36409             cls: cls,
36410             cn: [
36411                 {
36412                     tag: 'div',
36413                     cls: 'masonry-brick-split-head',
36414                     cn: [
36415                         {
36416                             tag: 'div',
36417                             cls: 'masonry-brick-paragraph',
36418                             cn: []
36419                         }
36420                     ]
36421                 },
36422                 {
36423                     tag: 'div',
36424                     cls: 'masonry-brick-split-body',
36425                     cn: []
36426                 }
36427             ]
36428         };
36429         
36430         if(this.href.length){
36431             cfg.href = this.href;
36432         }
36433         
36434         if(this.title.length){
36435             cfg.cn[0].cn[0].cn.push({
36436                 tag: 'h4',
36437                 cls: 'masonry-brick-title',
36438                 html: this.title
36439             });
36440         }
36441         
36442         if(this.html.length){
36443             cfg.cn[1].cn.push({
36444                 tag: 'p',
36445                 cls: 'masonry-brick-text',
36446                 html: this.html
36447             });
36448         }
36449
36450         if(this.bgimage.length){
36451             cfg.cn[0].cn.push({
36452                 tag: 'img',
36453                 cls: 'masonry-brick-image-view',
36454                 src: this.bgimage
36455             });
36456         }
36457         
36458         if(this.videourl.length){
36459             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36460             // youtube support only?
36461             cfg.cn[0].cn.cn.push({
36462                 tag: 'iframe',
36463                 cls: 'masonry-brick-image-view',
36464                 src: vurl,
36465                 frameborder : 0,
36466                 allowfullscreen : true
36467             });
36468         }
36469         
36470         return cfg;
36471     },
36472     
36473     initEvents: function() 
36474     {
36475         switch (this.size) {
36476             case 'xs' :
36477                 this.x = 1;
36478                 this.y = 1;
36479                 break;
36480             case 'sm' :
36481                 this.x = 2;
36482                 this.y = 2;
36483                 break;
36484             case 'md' :
36485             case 'md-left' :
36486             case 'md-right' :
36487                 this.x = 3;
36488                 this.y = 3;
36489                 break;
36490             case 'tall' :
36491                 this.x = 2;
36492                 this.y = 3;
36493                 break;
36494             case 'wide' :
36495                 this.x = 3;
36496                 this.y = 2;
36497                 break;
36498             case 'wide-thin' :
36499                 this.x = 3;
36500                 this.y = 1;
36501                 break;
36502                         
36503             default :
36504                 break;
36505         }
36506         
36507         if(Roo.isTouch){
36508             this.el.on('touchstart', this.onTouchStart, this);
36509             this.el.on('touchmove', this.onTouchMove, this);
36510             this.el.on('touchend', this.onTouchEnd, this);
36511             this.el.on('contextmenu', this.onContextMenu, this);
36512         } else {
36513             this.el.on('mouseenter'  ,this.enter, this);
36514             this.el.on('mouseleave', this.leave, this);
36515             this.el.on('click', this.onClick, this);
36516         }
36517         
36518         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36519             this.parent().bricks.push(this);   
36520         }
36521         
36522     },
36523     
36524     onClick: function(e, el)
36525     {
36526         var time = this.endTimer - this.startTimer;
36527         // Roo.log(e.preventDefault());
36528         if(Roo.isTouch){
36529             if(time > 1000){
36530                 e.preventDefault();
36531                 return;
36532             }
36533         }
36534         
36535         if(!this.preventDefault){
36536             return;
36537         }
36538         
36539         e.preventDefault();
36540         
36541         if (this.activeClass != '') {
36542             this.selectBrick();
36543         }
36544         
36545         this.fireEvent('click', this, e);
36546     },
36547     
36548     enter: function(e, el)
36549     {
36550         e.preventDefault();
36551         
36552         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36553             return;
36554         }
36555         
36556         if(this.bgimage.length && this.html.length){
36557             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36558         }
36559     },
36560     
36561     leave: function(e, el)
36562     {
36563         e.preventDefault();
36564         
36565         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36566             return;
36567         }
36568         
36569         if(this.bgimage.length && this.html.length){
36570             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36571         }
36572     },
36573     
36574     onTouchStart: function(e, el)
36575     {
36576 //        e.preventDefault();
36577         
36578         this.touchmoved = false;
36579         
36580         if(!this.isFitContainer){
36581             return;
36582         }
36583         
36584         if(!this.bgimage.length || !this.html.length){
36585             return;
36586         }
36587         
36588         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36589         
36590         this.timer = new Date().getTime();
36591         
36592     },
36593     
36594     onTouchMove: function(e, el)
36595     {
36596         this.touchmoved = true;
36597     },
36598     
36599     onContextMenu : function(e,el)
36600     {
36601         e.preventDefault();
36602         e.stopPropagation();
36603         return false;
36604     },
36605     
36606     onTouchEnd: function(e, el)
36607     {
36608 //        e.preventDefault();
36609         
36610         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36611         
36612             this.leave(e,el);
36613             
36614             return;
36615         }
36616         
36617         if(!this.bgimage.length || !this.html.length){
36618             
36619             if(this.href.length){
36620                 window.location.href = this.href;
36621             }
36622             
36623             return;
36624         }
36625         
36626         if(!this.isFitContainer){
36627             return;
36628         }
36629         
36630         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36631         
36632         window.location.href = this.href;
36633     },
36634     
36635     //selection on single brick only
36636     selectBrick : function() {
36637         
36638         if (!this.parentId) {
36639             return;
36640         }
36641         
36642         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36643         var index = m.selectedBrick.indexOf(this.id);
36644         
36645         if ( index > -1) {
36646             m.selectedBrick.splice(index,1);
36647             this.el.removeClass(this.activeClass);
36648             return;
36649         }
36650         
36651         for(var i = 0; i < m.selectedBrick.length; i++) {
36652             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36653             b.el.removeClass(b.activeClass);
36654         }
36655         
36656         m.selectedBrick = [];
36657         
36658         m.selectedBrick.push(this.id);
36659         this.el.addClass(this.activeClass);
36660         return;
36661     },
36662     
36663     isSelected : function(){
36664         return this.el.hasClass(this.activeClass);
36665         
36666     }
36667 });
36668
36669 Roo.apply(Roo.bootstrap.MasonryBrick, {
36670     
36671     //groups: {},
36672     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36673      /**
36674     * register a Masonry Brick
36675     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36676     */
36677     
36678     register : function(brick)
36679     {
36680         //this.groups[brick.id] = brick;
36681         this.groups.add(brick.id, brick);
36682     },
36683     /**
36684     * fetch a  masonry brick based on the masonry brick ID
36685     * @param {string} the masonry brick to add
36686     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36687     */
36688     
36689     get: function(brick_id) 
36690     {
36691         // if (typeof(this.groups[brick_id]) == 'undefined') {
36692         //     return false;
36693         // }
36694         // return this.groups[brick_id] ;
36695         
36696         if(this.groups.key(brick_id)) {
36697             return this.groups.key(brick_id);
36698         }
36699         
36700         return false;
36701     }
36702     
36703     
36704     
36705 });
36706
36707  /*
36708  * - LGPL
36709  *
36710  * element
36711  * 
36712  */
36713
36714 /**
36715  * @class Roo.bootstrap.Brick
36716  * @extends Roo.bootstrap.Component
36717  * Bootstrap Brick class
36718  * 
36719  * @constructor
36720  * Create a new Brick
36721  * @param {Object} config The config object
36722  */
36723
36724 Roo.bootstrap.Brick = function(config){
36725     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36726     
36727     this.addEvents({
36728         // raw events
36729         /**
36730          * @event click
36731          * When a Brick is click
36732          * @param {Roo.bootstrap.Brick} this
36733          * @param {Roo.EventObject} e
36734          */
36735         "click" : true
36736     });
36737 };
36738
36739 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36740     
36741     /**
36742      * @cfg {String} title
36743      */   
36744     title : '',
36745     /**
36746      * @cfg {String} html
36747      */   
36748     html : '',
36749     /**
36750      * @cfg {String} bgimage
36751      */   
36752     bgimage : '',
36753     /**
36754      * @cfg {String} cls
36755      */   
36756     cls : '',
36757     /**
36758      * @cfg {String} href
36759      */   
36760     href : '',
36761     /**
36762      * @cfg {String} video
36763      */   
36764     video : '',
36765     /**
36766      * @cfg {Boolean} square
36767      */   
36768     square : true,
36769     
36770     getAutoCreate : function()
36771     {
36772         var cls = 'roo-brick';
36773         
36774         if(this.href.length){
36775             cls += ' roo-brick-link';
36776         }
36777         
36778         if(this.bgimage.length){
36779             cls += ' roo-brick-image';
36780         }
36781         
36782         if(!this.html.length && !this.bgimage.length){
36783             cls += ' roo-brick-center-title';
36784         }
36785         
36786         if(!this.html.length && this.bgimage.length){
36787             cls += ' roo-brick-bottom-title';
36788         }
36789         
36790         if(this.cls){
36791             cls += ' ' + this.cls;
36792         }
36793         
36794         var cfg = {
36795             tag: (this.href.length) ? 'a' : 'div',
36796             cls: cls,
36797             cn: [
36798                 {
36799                     tag: 'div',
36800                     cls: 'roo-brick-paragraph',
36801                     cn: []
36802                 }
36803             ]
36804         };
36805         
36806         if(this.href.length){
36807             cfg.href = this.href;
36808         }
36809         
36810         var cn = cfg.cn[0].cn;
36811         
36812         if(this.title.length){
36813             cn.push({
36814                 tag: 'h4',
36815                 cls: 'roo-brick-title',
36816                 html: this.title
36817             });
36818         }
36819         
36820         if(this.html.length){
36821             cn.push({
36822                 tag: 'p',
36823                 cls: 'roo-brick-text',
36824                 html: this.html
36825             });
36826         } else {
36827             cn.cls += ' hide';
36828         }
36829         
36830         if(this.bgimage.length){
36831             cfg.cn.push({
36832                 tag: 'img',
36833                 cls: 'roo-brick-image-view',
36834                 src: this.bgimage
36835             });
36836         }
36837         
36838         return cfg;
36839     },
36840     
36841     initEvents: function() 
36842     {
36843         if(this.title.length || this.html.length){
36844             this.el.on('mouseenter'  ,this.enter, this);
36845             this.el.on('mouseleave', this.leave, this);
36846         }
36847         
36848         Roo.EventManager.onWindowResize(this.resize, this); 
36849         
36850         if(this.bgimage.length){
36851             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36852             this.imageEl.on('load', this.onImageLoad, this);
36853             return;
36854         }
36855         
36856         this.resize();
36857     },
36858     
36859     onImageLoad : function()
36860     {
36861         this.resize();
36862     },
36863     
36864     resize : function()
36865     {
36866         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36867         
36868         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36869         
36870         if(this.bgimage.length){
36871             var image = this.el.select('.roo-brick-image-view', true).first();
36872             
36873             image.setWidth(paragraph.getWidth());
36874             
36875             if(this.square){
36876                 image.setHeight(paragraph.getWidth());
36877             }
36878             
36879             this.el.setHeight(image.getHeight());
36880             paragraph.setHeight(image.getHeight());
36881             
36882         }
36883         
36884     },
36885     
36886     enter: function(e, el)
36887     {
36888         e.preventDefault();
36889         
36890         if(this.bgimage.length){
36891             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36892             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36893         }
36894     },
36895     
36896     leave: function(e, el)
36897     {
36898         e.preventDefault();
36899         
36900         if(this.bgimage.length){
36901             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36902             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36903         }
36904     }
36905     
36906 });
36907
36908  
36909
36910  /*
36911  * - LGPL
36912  *
36913  * Number field 
36914  */
36915
36916 /**
36917  * @class Roo.bootstrap.NumberField
36918  * @extends Roo.bootstrap.Input
36919  * Bootstrap NumberField class
36920  * 
36921  * 
36922  * 
36923  * 
36924  * @constructor
36925  * Create a new NumberField
36926  * @param {Object} config The config object
36927  */
36928
36929 Roo.bootstrap.NumberField = function(config){
36930     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36931 };
36932
36933 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36934     
36935     /**
36936      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36937      */
36938     allowDecimals : true,
36939     /**
36940      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36941      */
36942     decimalSeparator : ".",
36943     /**
36944      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36945      */
36946     decimalPrecision : 2,
36947     /**
36948      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36949      */
36950     allowNegative : true,
36951     
36952     /**
36953      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36954      */
36955     allowZero: true,
36956     /**
36957      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36958      */
36959     minValue : Number.NEGATIVE_INFINITY,
36960     /**
36961      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36962      */
36963     maxValue : Number.MAX_VALUE,
36964     /**
36965      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36966      */
36967     minText : "The minimum value for this field is {0}",
36968     /**
36969      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36970      */
36971     maxText : "The maximum value for this field is {0}",
36972     /**
36973      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36974      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36975      */
36976     nanText : "{0} is not a valid number",
36977     /**
36978      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36979      */
36980     thousandsDelimiter : false,
36981     /**
36982      * @cfg {String} valueAlign alignment of value
36983      */
36984     valueAlign : "left",
36985
36986     getAutoCreate : function()
36987     {
36988         var hiddenInput = {
36989             tag: 'input',
36990             type: 'hidden',
36991             id: Roo.id(),
36992             cls: 'hidden-number-input'
36993         };
36994         
36995         if (this.name) {
36996             hiddenInput.name = this.name;
36997         }
36998         
36999         this.name = '';
37000         
37001         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37002         
37003         this.name = hiddenInput.name;
37004         
37005         if(cfg.cn.length > 0) {
37006             cfg.cn.push(hiddenInput);
37007         }
37008         
37009         return cfg;
37010     },
37011
37012     // private
37013     initEvents : function()
37014     {   
37015         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37016         
37017         var allowed = "0123456789";
37018         
37019         if(this.allowDecimals){
37020             allowed += this.decimalSeparator;
37021         }
37022         
37023         if(this.allowNegative){
37024             allowed += "-";
37025         }
37026         
37027         if(this.thousandsDelimiter) {
37028             allowed += ",";
37029         }
37030         
37031         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37032         
37033         var keyPress = function(e){
37034             
37035             var k = e.getKey();
37036             
37037             var c = e.getCharCode();
37038             
37039             if(
37040                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37041                     allowed.indexOf(String.fromCharCode(c)) === -1
37042             ){
37043                 e.stopEvent();
37044                 return;
37045             }
37046             
37047             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37048                 return;
37049             }
37050             
37051             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37052                 e.stopEvent();
37053             }
37054         };
37055         
37056         this.el.on("keypress", keyPress, this);
37057     },
37058     
37059     validateValue : function(value)
37060     {
37061         
37062         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37063             return false;
37064         }
37065         
37066         var num = this.parseValue(value);
37067         
37068         if(isNaN(num)){
37069             this.markInvalid(String.format(this.nanText, value));
37070             return false;
37071         }
37072         
37073         if(num < this.minValue){
37074             this.markInvalid(String.format(this.minText, this.minValue));
37075             return false;
37076         }
37077         
37078         if(num > this.maxValue){
37079             this.markInvalid(String.format(this.maxText, this.maxValue));
37080             return false;
37081         }
37082         
37083         return true;
37084     },
37085
37086     getValue : function()
37087     {
37088         var v = this.hiddenEl().getValue();
37089         
37090         return this.fixPrecision(this.parseValue(v));
37091     },
37092
37093     parseValue : function(value)
37094     {
37095         if(this.thousandsDelimiter) {
37096             value += "";
37097             r = new RegExp(",", "g");
37098             value = value.replace(r, "");
37099         }
37100         
37101         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37102         return isNaN(value) ? '' : value;
37103     },
37104
37105     fixPrecision : function(value)
37106     {
37107         if(this.thousandsDelimiter) {
37108             value += "";
37109             r = new RegExp(",", "g");
37110             value = value.replace(r, "");
37111         }
37112         
37113         var nan = isNaN(value);
37114         
37115         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37116             return nan ? '' : value;
37117         }
37118         return parseFloat(value).toFixed(this.decimalPrecision);
37119     },
37120
37121     setValue : function(v)
37122     {
37123         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37124         
37125         this.value = v;
37126         
37127         if(this.rendered){
37128             
37129             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37130             
37131             this.inputEl().dom.value = (v == '') ? '' :
37132                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37133             
37134             if(!this.allowZero && v === '0') {
37135                 this.hiddenEl().dom.value = '';
37136                 this.inputEl().dom.value = '';
37137             }
37138             
37139             this.validate();
37140         }
37141     },
37142
37143     decimalPrecisionFcn : function(v)
37144     {
37145         return Math.floor(v);
37146     },
37147
37148     beforeBlur : function()
37149     {
37150         var v = this.parseValue(this.getRawValue());
37151         
37152         if(v || v === 0 || v === ''){
37153             this.setValue(v);
37154         }
37155     },
37156     
37157     hiddenEl : function()
37158     {
37159         return this.el.select('input.hidden-number-input',true).first();
37160     }
37161     
37162 });
37163
37164  
37165
37166 /*
37167 * Licence: LGPL
37168 */
37169
37170 /**
37171  * @class Roo.bootstrap.DocumentSlider
37172  * @extends Roo.bootstrap.Component
37173  * Bootstrap DocumentSlider class
37174  * 
37175  * @constructor
37176  * Create a new DocumentViewer
37177  * @param {Object} config The config object
37178  */
37179
37180 Roo.bootstrap.DocumentSlider = function(config){
37181     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37182     
37183     this.files = [];
37184     
37185     this.addEvents({
37186         /**
37187          * @event initial
37188          * Fire after initEvent
37189          * @param {Roo.bootstrap.DocumentSlider} this
37190          */
37191         "initial" : true,
37192         /**
37193          * @event update
37194          * Fire after update
37195          * @param {Roo.bootstrap.DocumentSlider} this
37196          */
37197         "update" : true,
37198         /**
37199          * @event click
37200          * Fire after click
37201          * @param {Roo.bootstrap.DocumentSlider} this
37202          */
37203         "click" : true
37204     });
37205 };
37206
37207 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37208     
37209     files : false,
37210     
37211     indicator : 0,
37212     
37213     getAutoCreate : function()
37214     {
37215         var cfg = {
37216             tag : 'div',
37217             cls : 'roo-document-slider',
37218             cn : [
37219                 {
37220                     tag : 'div',
37221                     cls : 'roo-document-slider-header',
37222                     cn : [
37223                         {
37224                             tag : 'div',
37225                             cls : 'roo-document-slider-header-title'
37226                         }
37227                     ]
37228                 },
37229                 {
37230                     tag : 'div',
37231                     cls : 'roo-document-slider-body',
37232                     cn : [
37233                         {
37234                             tag : 'div',
37235                             cls : 'roo-document-slider-prev',
37236                             cn : [
37237                                 {
37238                                     tag : 'i',
37239                                     cls : 'fa fa-chevron-left'
37240                                 }
37241                             ]
37242                         },
37243                         {
37244                             tag : 'div',
37245                             cls : 'roo-document-slider-thumb',
37246                             cn : [
37247                                 {
37248                                     tag : 'img',
37249                                     cls : 'roo-document-slider-image'
37250                                 }
37251                             ]
37252                         },
37253                         {
37254                             tag : 'div',
37255                             cls : 'roo-document-slider-next',
37256                             cn : [
37257                                 {
37258                                     tag : 'i',
37259                                     cls : 'fa fa-chevron-right'
37260                                 }
37261                             ]
37262                         }
37263                     ]
37264                 }
37265             ]
37266         };
37267         
37268         return cfg;
37269     },
37270     
37271     initEvents : function()
37272     {
37273         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37274         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37275         
37276         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37277         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37278         
37279         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37280         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37281         
37282         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37283         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37284         
37285         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37286         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37287         
37288         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37289         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37290         
37291         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37292         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37293         
37294         this.thumbEl.on('click', this.onClick, this);
37295         
37296         this.prevIndicator.on('click', this.prev, this);
37297         
37298         this.nextIndicator.on('click', this.next, this);
37299         
37300     },
37301     
37302     initial : function()
37303     {
37304         if(this.files.length){
37305             this.indicator = 1;
37306             this.update()
37307         }
37308         
37309         this.fireEvent('initial', this);
37310     },
37311     
37312     update : function()
37313     {
37314         this.imageEl.attr('src', this.files[this.indicator - 1]);
37315         
37316         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37317         
37318         this.prevIndicator.show();
37319         
37320         if(this.indicator == 1){
37321             this.prevIndicator.hide();
37322         }
37323         
37324         this.nextIndicator.show();
37325         
37326         if(this.indicator == this.files.length){
37327             this.nextIndicator.hide();
37328         }
37329         
37330         this.thumbEl.scrollTo('top');
37331         
37332         this.fireEvent('update', this);
37333     },
37334     
37335     onClick : function(e)
37336     {
37337         e.preventDefault();
37338         
37339         this.fireEvent('click', this);
37340     },
37341     
37342     prev : function(e)
37343     {
37344         e.preventDefault();
37345         
37346         this.indicator = Math.max(1, this.indicator - 1);
37347         
37348         this.update();
37349     },
37350     
37351     next : function(e)
37352     {
37353         e.preventDefault();
37354         
37355         this.indicator = Math.min(this.files.length, this.indicator + 1);
37356         
37357         this.update();
37358     }
37359 });
37360 /*
37361  * - LGPL
37362  *
37363  * RadioSet
37364  *
37365  *
37366  */
37367
37368 /**
37369  * @class Roo.bootstrap.RadioSet
37370  * @extends Roo.bootstrap.Input
37371  * Bootstrap RadioSet class
37372  * @cfg {String} indicatorpos (left|right) default left
37373  * @cfg {Boolean} inline (true|false) inline the element (default true)
37374  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37375  * @constructor
37376  * Create a new RadioSet
37377  * @param {Object} config The config object
37378  */
37379
37380 Roo.bootstrap.RadioSet = function(config){
37381     
37382     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37383     
37384     this.radioes = [];
37385     
37386     Roo.bootstrap.RadioSet.register(this);
37387     
37388     this.addEvents({
37389         /**
37390         * @event check
37391         * Fires when the element is checked or unchecked.
37392         * @param {Roo.bootstrap.RadioSet} this This radio
37393         * @param {Roo.bootstrap.Radio} item The checked item
37394         */
37395        check : true,
37396        /**
37397         * @event click
37398         * Fires when the element is click.
37399         * @param {Roo.bootstrap.RadioSet} this This radio set
37400         * @param {Roo.bootstrap.Radio} item The checked item
37401         * @param {Roo.EventObject} e The event object
37402         */
37403        click : true
37404     });
37405     
37406 };
37407
37408 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37409
37410     radioes : false,
37411     
37412     inline : true,
37413     
37414     weight : '',
37415     
37416     indicatorpos : 'left',
37417     
37418     getAutoCreate : function()
37419     {
37420         var label = {
37421             tag : 'label',
37422             cls : 'roo-radio-set-label',
37423             cn : [
37424                 {
37425                     tag : 'span',
37426                     html : this.fieldLabel
37427                 }
37428             ]
37429         };
37430         if (Roo.bootstrap.version == 3) {
37431             
37432             
37433             if(this.indicatorpos == 'left'){
37434                 label.cn.unshift({
37435                     tag : 'i',
37436                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37437                     tooltip : 'This field is required'
37438                 });
37439             } else {
37440                 label.cn.push({
37441                     tag : 'i',
37442                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37443                     tooltip : 'This field is required'
37444                 });
37445             }
37446         }
37447         var items = {
37448             tag : 'div',
37449             cls : 'roo-radio-set-items'
37450         };
37451         
37452         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37453         
37454         if (align === 'left' && this.fieldLabel.length) {
37455             
37456             items = {
37457                 cls : "roo-radio-set-right", 
37458                 cn: [
37459                     items
37460                 ]
37461             };
37462             
37463             if(this.labelWidth > 12){
37464                 label.style = "width: " + this.labelWidth + 'px';
37465             }
37466             
37467             if(this.labelWidth < 13 && this.labelmd == 0){
37468                 this.labelmd = this.labelWidth;
37469             }
37470             
37471             if(this.labellg > 0){
37472                 label.cls += ' col-lg-' + this.labellg;
37473                 items.cls += ' col-lg-' + (12 - this.labellg);
37474             }
37475             
37476             if(this.labelmd > 0){
37477                 label.cls += ' col-md-' + this.labelmd;
37478                 items.cls += ' col-md-' + (12 - this.labelmd);
37479             }
37480             
37481             if(this.labelsm > 0){
37482                 label.cls += ' col-sm-' + this.labelsm;
37483                 items.cls += ' col-sm-' + (12 - this.labelsm);
37484             }
37485             
37486             if(this.labelxs > 0){
37487                 label.cls += ' col-xs-' + this.labelxs;
37488                 items.cls += ' col-xs-' + (12 - this.labelxs);
37489             }
37490         }
37491         
37492         var cfg = {
37493             tag : 'div',
37494             cls : 'roo-radio-set',
37495             cn : [
37496                 {
37497                     tag : 'input',
37498                     cls : 'roo-radio-set-input',
37499                     type : 'hidden',
37500                     name : this.name,
37501                     value : this.value ? this.value :  ''
37502                 },
37503                 label,
37504                 items
37505             ]
37506         };
37507         
37508         if(this.weight.length){
37509             cfg.cls += ' roo-radio-' + this.weight;
37510         }
37511         
37512         if(this.inline) {
37513             cfg.cls += ' roo-radio-set-inline';
37514         }
37515         
37516         var settings=this;
37517         ['xs','sm','md','lg'].map(function(size){
37518             if (settings[size]) {
37519                 cfg.cls += ' col-' + size + '-' + settings[size];
37520             }
37521         });
37522         
37523         return cfg;
37524         
37525     },
37526
37527     initEvents : function()
37528     {
37529         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37530         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37531         
37532         if(!this.fieldLabel.length){
37533             this.labelEl.hide();
37534         }
37535         
37536         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37537         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37538         
37539         this.indicator = this.indicatorEl();
37540         
37541         if(this.indicator){
37542             this.indicator.addClass('invisible');
37543         }
37544         
37545         this.originalValue = this.getValue();
37546         
37547     },
37548     
37549     inputEl: function ()
37550     {
37551         return this.el.select('.roo-radio-set-input', true).first();
37552     },
37553     
37554     getChildContainer : function()
37555     {
37556         return this.itemsEl;
37557     },
37558     
37559     register : function(item)
37560     {
37561         this.radioes.push(item);
37562         
37563     },
37564     
37565     validate : function()
37566     {   
37567         if(this.getVisibilityEl().hasClass('hidden')){
37568             return true;
37569         }
37570         
37571         var valid = false;
37572         
37573         Roo.each(this.radioes, function(i){
37574             if(!i.checked){
37575                 return;
37576             }
37577             
37578             valid = true;
37579             return false;
37580         });
37581         
37582         if(this.allowBlank) {
37583             return true;
37584         }
37585         
37586         if(this.disabled || valid){
37587             this.markValid();
37588             return true;
37589         }
37590         
37591         this.markInvalid();
37592         return false;
37593         
37594     },
37595     
37596     markValid : function()
37597     {
37598         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37599             this.indicatorEl().removeClass('visible');
37600             this.indicatorEl().addClass('invisible');
37601         }
37602         
37603         
37604         if (Roo.bootstrap.version == 3) {
37605             this.el.removeClass([this.invalidClass, this.validClass]);
37606             this.el.addClass(this.validClass);
37607         } else {
37608             this.el.removeClass(['is-invalid','is-valid']);
37609             this.el.addClass(['is-valid']);
37610         }
37611         this.fireEvent('valid', this);
37612     },
37613     
37614     markInvalid : function(msg)
37615     {
37616         if(this.allowBlank || this.disabled){
37617             return;
37618         }
37619         
37620         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37621             this.indicatorEl().removeClass('invisible');
37622             this.indicatorEl().addClass('visible');
37623         }
37624         if (Roo.bootstrap.version == 3) {
37625             this.el.removeClass([this.invalidClass, this.validClass]);
37626             this.el.addClass(this.invalidClass);
37627         } else {
37628             this.el.removeClass(['is-invalid','is-valid']);
37629             this.el.addClass(['is-invalid']);
37630         }
37631         
37632         this.fireEvent('invalid', this, msg);
37633         
37634     },
37635     
37636     setValue : function(v, suppressEvent)
37637     {   
37638         if(this.value === v){
37639             return;
37640         }
37641         
37642         this.value = v;
37643         
37644         if(this.rendered){
37645             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37646         }
37647         
37648         Roo.each(this.radioes, function(i){
37649             i.checked = false;
37650             i.el.removeClass('checked');
37651         });
37652         
37653         Roo.each(this.radioes, function(i){
37654             
37655             if(i.value === v || i.value.toString() === v.toString()){
37656                 i.checked = true;
37657                 i.el.addClass('checked');
37658                 
37659                 if(suppressEvent !== true){
37660                     this.fireEvent('check', this, i);
37661                 }
37662                 
37663                 return false;
37664             }
37665             
37666         }, this);
37667         
37668         this.validate();
37669     },
37670     
37671     clearInvalid : function(){
37672         
37673         if(!this.el || this.preventMark){
37674             return;
37675         }
37676         
37677         this.el.removeClass([this.invalidClass]);
37678         
37679         this.fireEvent('valid', this);
37680     }
37681     
37682 });
37683
37684 Roo.apply(Roo.bootstrap.RadioSet, {
37685     
37686     groups: {},
37687     
37688     register : function(set)
37689     {
37690         this.groups[set.name] = set;
37691     },
37692     
37693     get: function(name) 
37694     {
37695         if (typeof(this.groups[name]) == 'undefined') {
37696             return false;
37697         }
37698         
37699         return this.groups[name] ;
37700     }
37701     
37702 });
37703 /*
37704  * Based on:
37705  * Ext JS Library 1.1.1
37706  * Copyright(c) 2006-2007, Ext JS, LLC.
37707  *
37708  * Originally Released Under LGPL - original licence link has changed is not relivant.
37709  *
37710  * Fork - LGPL
37711  * <script type="text/javascript">
37712  */
37713
37714
37715 /**
37716  * @class Roo.bootstrap.SplitBar
37717  * @extends Roo.util.Observable
37718  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37719  * <br><br>
37720  * Usage:
37721  * <pre><code>
37722 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37723                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37724 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37725 split.minSize = 100;
37726 split.maxSize = 600;
37727 split.animate = true;
37728 split.on('moved', splitterMoved);
37729 </code></pre>
37730  * @constructor
37731  * Create a new SplitBar
37732  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37733  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37734  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37735  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37736                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37737                         position of the SplitBar).
37738  */
37739 Roo.bootstrap.SplitBar = function(cfg){
37740     
37741     /** @private */
37742     
37743     //{
37744     //  dragElement : elm
37745     //  resizingElement: el,
37746         // optional..
37747     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37748     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37749         // existingProxy ???
37750     //}
37751     
37752     this.el = Roo.get(cfg.dragElement, true);
37753     this.el.dom.unselectable = "on";
37754     /** @private */
37755     this.resizingEl = Roo.get(cfg.resizingElement, true);
37756
37757     /**
37758      * @private
37759      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37760      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37761      * @type Number
37762      */
37763     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37764     
37765     /**
37766      * The minimum size of the resizing element. (Defaults to 0)
37767      * @type Number
37768      */
37769     this.minSize = 0;
37770     
37771     /**
37772      * The maximum size of the resizing element. (Defaults to 2000)
37773      * @type Number
37774      */
37775     this.maxSize = 2000;
37776     
37777     /**
37778      * Whether to animate the transition to the new size
37779      * @type Boolean
37780      */
37781     this.animate = false;
37782     
37783     /**
37784      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37785      * @type Boolean
37786      */
37787     this.useShim = false;
37788     
37789     /** @private */
37790     this.shim = null;
37791     
37792     if(!cfg.existingProxy){
37793         /** @private */
37794         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37795     }else{
37796         this.proxy = Roo.get(cfg.existingProxy).dom;
37797     }
37798     /** @private */
37799     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37800     
37801     /** @private */
37802     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37803     
37804     /** @private */
37805     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37806     
37807     /** @private */
37808     this.dragSpecs = {};
37809     
37810     /**
37811      * @private The adapter to use to positon and resize elements
37812      */
37813     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37814     this.adapter.init(this);
37815     
37816     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37817         /** @private */
37818         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37819         this.el.addClass("roo-splitbar-h");
37820     }else{
37821         /** @private */
37822         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37823         this.el.addClass("roo-splitbar-v");
37824     }
37825     
37826     this.addEvents({
37827         /**
37828          * @event resize
37829          * Fires when the splitter is moved (alias for {@link #event-moved})
37830          * @param {Roo.bootstrap.SplitBar} this
37831          * @param {Number} newSize the new width or height
37832          */
37833         "resize" : true,
37834         /**
37835          * @event moved
37836          * Fires when the splitter is moved
37837          * @param {Roo.bootstrap.SplitBar} this
37838          * @param {Number} newSize the new width or height
37839          */
37840         "moved" : true,
37841         /**
37842          * @event beforeresize
37843          * Fires before the splitter is dragged
37844          * @param {Roo.bootstrap.SplitBar} this
37845          */
37846         "beforeresize" : true,
37847
37848         "beforeapply" : true
37849     });
37850
37851     Roo.util.Observable.call(this);
37852 };
37853
37854 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37855     onStartProxyDrag : function(x, y){
37856         this.fireEvent("beforeresize", this);
37857         if(!this.overlay){
37858             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37859             o.unselectable();
37860             o.enableDisplayMode("block");
37861             // all splitbars share the same overlay
37862             Roo.bootstrap.SplitBar.prototype.overlay = o;
37863         }
37864         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37865         this.overlay.show();
37866         Roo.get(this.proxy).setDisplayed("block");
37867         var size = this.adapter.getElementSize(this);
37868         this.activeMinSize = this.getMinimumSize();;
37869         this.activeMaxSize = this.getMaximumSize();;
37870         var c1 = size - this.activeMinSize;
37871         var c2 = Math.max(this.activeMaxSize - size, 0);
37872         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37873             this.dd.resetConstraints();
37874             this.dd.setXConstraint(
37875                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37876                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37877             );
37878             this.dd.setYConstraint(0, 0);
37879         }else{
37880             this.dd.resetConstraints();
37881             this.dd.setXConstraint(0, 0);
37882             this.dd.setYConstraint(
37883                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37884                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37885             );
37886          }
37887         this.dragSpecs.startSize = size;
37888         this.dragSpecs.startPoint = [x, y];
37889         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37890     },
37891     
37892     /** 
37893      * @private Called after the drag operation by the DDProxy
37894      */
37895     onEndProxyDrag : function(e){
37896         Roo.get(this.proxy).setDisplayed(false);
37897         var endPoint = Roo.lib.Event.getXY(e);
37898         if(this.overlay){
37899             this.overlay.hide();
37900         }
37901         var newSize;
37902         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37903             newSize = this.dragSpecs.startSize + 
37904                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37905                     endPoint[0] - this.dragSpecs.startPoint[0] :
37906                     this.dragSpecs.startPoint[0] - endPoint[0]
37907                 );
37908         }else{
37909             newSize = this.dragSpecs.startSize + 
37910                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37911                     endPoint[1] - this.dragSpecs.startPoint[1] :
37912                     this.dragSpecs.startPoint[1] - endPoint[1]
37913                 );
37914         }
37915         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37916         if(newSize != this.dragSpecs.startSize){
37917             if(this.fireEvent('beforeapply', this, newSize) !== false){
37918                 this.adapter.setElementSize(this, newSize);
37919                 this.fireEvent("moved", this, newSize);
37920                 this.fireEvent("resize", this, newSize);
37921             }
37922         }
37923     },
37924     
37925     /**
37926      * Get the adapter this SplitBar uses
37927      * @return The adapter object
37928      */
37929     getAdapter : function(){
37930         return this.adapter;
37931     },
37932     
37933     /**
37934      * Set the adapter this SplitBar uses
37935      * @param {Object} adapter A SplitBar adapter object
37936      */
37937     setAdapter : function(adapter){
37938         this.adapter = adapter;
37939         this.adapter.init(this);
37940     },
37941     
37942     /**
37943      * Gets the minimum size for the resizing element
37944      * @return {Number} The minimum size
37945      */
37946     getMinimumSize : function(){
37947         return this.minSize;
37948     },
37949     
37950     /**
37951      * Sets the minimum size for the resizing element
37952      * @param {Number} minSize The minimum size
37953      */
37954     setMinimumSize : function(minSize){
37955         this.minSize = minSize;
37956     },
37957     
37958     /**
37959      * Gets the maximum size for the resizing element
37960      * @return {Number} The maximum size
37961      */
37962     getMaximumSize : function(){
37963         return this.maxSize;
37964     },
37965     
37966     /**
37967      * Sets the maximum size for the resizing element
37968      * @param {Number} maxSize The maximum size
37969      */
37970     setMaximumSize : function(maxSize){
37971         this.maxSize = maxSize;
37972     },
37973     
37974     /**
37975      * Sets the initialize size for the resizing element
37976      * @param {Number} size The initial size
37977      */
37978     setCurrentSize : function(size){
37979         var oldAnimate = this.animate;
37980         this.animate = false;
37981         this.adapter.setElementSize(this, size);
37982         this.animate = oldAnimate;
37983     },
37984     
37985     /**
37986      * Destroy this splitbar. 
37987      * @param {Boolean} removeEl True to remove the element
37988      */
37989     destroy : function(removeEl){
37990         if(this.shim){
37991             this.shim.remove();
37992         }
37993         this.dd.unreg();
37994         this.proxy.parentNode.removeChild(this.proxy);
37995         if(removeEl){
37996             this.el.remove();
37997         }
37998     }
37999 });
38000
38001 /**
38002  * @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.
38003  */
38004 Roo.bootstrap.SplitBar.createProxy = function(dir){
38005     var proxy = new Roo.Element(document.createElement("div"));
38006     proxy.unselectable();
38007     var cls = 'roo-splitbar-proxy';
38008     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38009     document.body.appendChild(proxy.dom);
38010     return proxy.dom;
38011 };
38012
38013 /** 
38014  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38015  * Default Adapter. It assumes the splitter and resizing element are not positioned
38016  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38017  */
38018 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38019 };
38020
38021 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38022     // do nothing for now
38023     init : function(s){
38024     
38025     },
38026     /**
38027      * Called before drag operations to get the current size of the resizing element. 
38028      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38029      */
38030      getElementSize : function(s){
38031         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38032             return s.resizingEl.getWidth();
38033         }else{
38034             return s.resizingEl.getHeight();
38035         }
38036     },
38037     
38038     /**
38039      * Called after drag operations to set the size of the resizing element.
38040      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38041      * @param {Number} newSize The new size to set
38042      * @param {Function} onComplete A function to be invoked when resizing is complete
38043      */
38044     setElementSize : function(s, newSize, onComplete){
38045         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38046             if(!s.animate){
38047                 s.resizingEl.setWidth(newSize);
38048                 if(onComplete){
38049                     onComplete(s, newSize);
38050                 }
38051             }else{
38052                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38053             }
38054         }else{
38055             
38056             if(!s.animate){
38057                 s.resizingEl.setHeight(newSize);
38058                 if(onComplete){
38059                     onComplete(s, newSize);
38060                 }
38061             }else{
38062                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38063             }
38064         }
38065     }
38066 };
38067
38068 /** 
38069  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38070  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38071  * Adapter that  moves the splitter element to align with the resized sizing element. 
38072  * Used with an absolute positioned SplitBar.
38073  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38074  * document.body, make sure you assign an id to the body element.
38075  */
38076 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38077     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38078     this.container = Roo.get(container);
38079 };
38080
38081 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38082     init : function(s){
38083         this.basic.init(s);
38084     },
38085     
38086     getElementSize : function(s){
38087         return this.basic.getElementSize(s);
38088     },
38089     
38090     setElementSize : function(s, newSize, onComplete){
38091         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38092     },
38093     
38094     moveSplitter : function(s){
38095         var yes = Roo.bootstrap.SplitBar;
38096         switch(s.placement){
38097             case yes.LEFT:
38098                 s.el.setX(s.resizingEl.getRight());
38099                 break;
38100             case yes.RIGHT:
38101                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38102                 break;
38103             case yes.TOP:
38104                 s.el.setY(s.resizingEl.getBottom());
38105                 break;
38106             case yes.BOTTOM:
38107                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38108                 break;
38109         }
38110     }
38111 };
38112
38113 /**
38114  * Orientation constant - Create a vertical SplitBar
38115  * @static
38116  * @type Number
38117  */
38118 Roo.bootstrap.SplitBar.VERTICAL = 1;
38119
38120 /**
38121  * Orientation constant - Create a horizontal SplitBar
38122  * @static
38123  * @type Number
38124  */
38125 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38126
38127 /**
38128  * Placement constant - The resizing element is to the left of the splitter element
38129  * @static
38130  * @type Number
38131  */
38132 Roo.bootstrap.SplitBar.LEFT = 1;
38133
38134 /**
38135  * Placement constant - The resizing element is to the right of the splitter element
38136  * @static
38137  * @type Number
38138  */
38139 Roo.bootstrap.SplitBar.RIGHT = 2;
38140
38141 /**
38142  * Placement constant - The resizing element is positioned above the splitter element
38143  * @static
38144  * @type Number
38145  */
38146 Roo.bootstrap.SplitBar.TOP = 3;
38147
38148 /**
38149  * Placement constant - The resizing element is positioned under splitter element
38150  * @static
38151  * @type Number
38152  */
38153 Roo.bootstrap.SplitBar.BOTTOM = 4;
38154 Roo.namespace("Roo.bootstrap.layout");/*
38155  * Based on:
38156  * Ext JS Library 1.1.1
38157  * Copyright(c) 2006-2007, Ext JS, LLC.
38158  *
38159  * Originally Released Under LGPL - original licence link has changed is not relivant.
38160  *
38161  * Fork - LGPL
38162  * <script type="text/javascript">
38163  */
38164
38165 /**
38166  * @class Roo.bootstrap.layout.Manager
38167  * @extends Roo.bootstrap.Component
38168  * Base class for layout managers.
38169  */
38170 Roo.bootstrap.layout.Manager = function(config)
38171 {
38172     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38173
38174
38175
38176
38177
38178     /** false to disable window resize monitoring @type Boolean */
38179     this.monitorWindowResize = true;
38180     this.regions = {};
38181     this.addEvents({
38182         /**
38183          * @event layout
38184          * Fires when a layout is performed.
38185          * @param {Roo.LayoutManager} this
38186          */
38187         "layout" : true,
38188         /**
38189          * @event regionresized
38190          * Fires when the user resizes a region.
38191          * @param {Roo.LayoutRegion} region The resized region
38192          * @param {Number} newSize The new size (width for east/west, height for north/south)
38193          */
38194         "regionresized" : true,
38195         /**
38196          * @event regioncollapsed
38197          * Fires when a region is collapsed.
38198          * @param {Roo.LayoutRegion} region The collapsed region
38199          */
38200         "regioncollapsed" : true,
38201         /**
38202          * @event regionexpanded
38203          * Fires when a region is expanded.
38204          * @param {Roo.LayoutRegion} region The expanded region
38205          */
38206         "regionexpanded" : true
38207     });
38208     this.updating = false;
38209
38210     if (config.el) {
38211         this.el = Roo.get(config.el);
38212         this.initEvents();
38213     }
38214
38215 };
38216
38217 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38218
38219
38220     regions : null,
38221
38222     monitorWindowResize : true,
38223
38224
38225     updating : false,
38226
38227
38228     onRender : function(ct, position)
38229     {
38230         if(!this.el){
38231             this.el = Roo.get(ct);
38232             this.initEvents();
38233         }
38234         //this.fireEvent('render',this);
38235     },
38236
38237
38238     initEvents: function()
38239     {
38240
38241
38242         // ie scrollbar fix
38243         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38244             document.body.scroll = "no";
38245         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38246             this.el.position('relative');
38247         }
38248         this.id = this.el.id;
38249         this.el.addClass("roo-layout-container");
38250         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38251         if(this.el.dom != document.body ) {
38252             this.el.on('resize', this.layout,this);
38253             this.el.on('show', this.layout,this);
38254         }
38255
38256     },
38257
38258     /**
38259      * Returns true if this layout is currently being updated
38260      * @return {Boolean}
38261      */
38262     isUpdating : function(){
38263         return this.updating;
38264     },
38265
38266     /**
38267      * Suspend the LayoutManager from doing auto-layouts while
38268      * making multiple add or remove calls
38269      */
38270     beginUpdate : function(){
38271         this.updating = true;
38272     },
38273
38274     /**
38275      * Restore auto-layouts and optionally disable the manager from performing a layout
38276      * @param {Boolean} noLayout true to disable a layout update
38277      */
38278     endUpdate : function(noLayout){
38279         this.updating = false;
38280         if(!noLayout){
38281             this.layout();
38282         }
38283     },
38284
38285     layout: function(){
38286         // abstract...
38287     },
38288
38289     onRegionResized : function(region, newSize){
38290         this.fireEvent("regionresized", region, newSize);
38291         this.layout();
38292     },
38293
38294     onRegionCollapsed : function(region){
38295         this.fireEvent("regioncollapsed", region);
38296     },
38297
38298     onRegionExpanded : function(region){
38299         this.fireEvent("regionexpanded", region);
38300     },
38301
38302     /**
38303      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38304      * performs box-model adjustments.
38305      * @return {Object} The size as an object {width: (the width), height: (the height)}
38306      */
38307     getViewSize : function()
38308     {
38309         var size;
38310         if(this.el.dom != document.body){
38311             size = this.el.getSize();
38312         }else{
38313             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38314         }
38315         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38316         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38317         return size;
38318     },
38319
38320     /**
38321      * Returns the Element this layout is bound to.
38322      * @return {Roo.Element}
38323      */
38324     getEl : function(){
38325         return this.el;
38326     },
38327
38328     /**
38329      * Returns the specified region.
38330      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38331      * @return {Roo.LayoutRegion}
38332      */
38333     getRegion : function(target){
38334         return this.regions[target.toLowerCase()];
38335     },
38336
38337     onWindowResize : function(){
38338         if(this.monitorWindowResize){
38339             this.layout();
38340         }
38341     }
38342 });
38343 /*
38344  * Based on:
38345  * Ext JS Library 1.1.1
38346  * Copyright(c) 2006-2007, Ext JS, LLC.
38347  *
38348  * Originally Released Under LGPL - original licence link has changed is not relivant.
38349  *
38350  * Fork - LGPL
38351  * <script type="text/javascript">
38352  */
38353 /**
38354  * @class Roo.bootstrap.layout.Border
38355  * @extends Roo.bootstrap.layout.Manager
38356  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38357  * please see: examples/bootstrap/nested.html<br><br>
38358  
38359 <b>The container the layout is rendered into can be either the body element or any other element.
38360 If it is not the body element, the container needs to either be an absolute positioned element,
38361 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38362 the container size if it is not the body element.</b>
38363
38364 * @constructor
38365 * Create a new Border
38366 * @param {Object} config Configuration options
38367  */
38368 Roo.bootstrap.layout.Border = function(config){
38369     config = config || {};
38370     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38371     
38372     
38373     
38374     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38375         if(config[region]){
38376             config[region].region = region;
38377             this.addRegion(config[region]);
38378         }
38379     },this);
38380     
38381 };
38382
38383 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38384
38385 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38386     
38387     parent : false, // this might point to a 'nest' or a ???
38388     
38389     /**
38390      * Creates and adds a new region if it doesn't already exist.
38391      * @param {String} target The target region key (north, south, east, west or center).
38392      * @param {Object} config The regions config object
38393      * @return {BorderLayoutRegion} The new region
38394      */
38395     addRegion : function(config)
38396     {
38397         if(!this.regions[config.region]){
38398             var r = this.factory(config);
38399             this.bindRegion(r);
38400         }
38401         return this.regions[config.region];
38402     },
38403
38404     // private (kinda)
38405     bindRegion : function(r){
38406         this.regions[r.config.region] = r;
38407         
38408         r.on("visibilitychange",    this.layout, this);
38409         r.on("paneladded",          this.layout, this);
38410         r.on("panelremoved",        this.layout, this);
38411         r.on("invalidated",         this.layout, this);
38412         r.on("resized",             this.onRegionResized, this);
38413         r.on("collapsed",           this.onRegionCollapsed, this);
38414         r.on("expanded",            this.onRegionExpanded, this);
38415     },
38416
38417     /**
38418      * Performs a layout update.
38419      */
38420     layout : function()
38421     {
38422         if(this.updating) {
38423             return;
38424         }
38425         
38426         // render all the rebions if they have not been done alreayd?
38427         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38428             if(this.regions[region] && !this.regions[region].bodyEl){
38429                 this.regions[region].onRender(this.el)
38430             }
38431         },this);
38432         
38433         var size = this.getViewSize();
38434         var w = size.width;
38435         var h = size.height;
38436         var centerW = w;
38437         var centerH = h;
38438         var centerY = 0;
38439         var centerX = 0;
38440         //var x = 0, y = 0;
38441
38442         var rs = this.regions;
38443         var north = rs["north"];
38444         var south = rs["south"]; 
38445         var west = rs["west"];
38446         var east = rs["east"];
38447         var center = rs["center"];
38448         //if(this.hideOnLayout){ // not supported anymore
38449             //c.el.setStyle("display", "none");
38450         //}
38451         if(north && north.isVisible()){
38452             var b = north.getBox();
38453             var m = north.getMargins();
38454             b.width = w - (m.left+m.right);
38455             b.x = m.left;
38456             b.y = m.top;
38457             centerY = b.height + b.y + m.bottom;
38458             centerH -= centerY;
38459             north.updateBox(this.safeBox(b));
38460         }
38461         if(south && south.isVisible()){
38462             var b = south.getBox();
38463             var m = south.getMargins();
38464             b.width = w - (m.left+m.right);
38465             b.x = m.left;
38466             var totalHeight = (b.height + m.top + m.bottom);
38467             b.y = h - totalHeight + m.top;
38468             centerH -= totalHeight;
38469             south.updateBox(this.safeBox(b));
38470         }
38471         if(west && west.isVisible()){
38472             var b = west.getBox();
38473             var m = west.getMargins();
38474             b.height = centerH - (m.top+m.bottom);
38475             b.x = m.left;
38476             b.y = centerY + m.top;
38477             var totalWidth = (b.width + m.left + m.right);
38478             centerX += totalWidth;
38479             centerW -= totalWidth;
38480             west.updateBox(this.safeBox(b));
38481         }
38482         if(east && east.isVisible()){
38483             var b = east.getBox();
38484             var m = east.getMargins();
38485             b.height = centerH - (m.top+m.bottom);
38486             var totalWidth = (b.width + m.left + m.right);
38487             b.x = w - totalWidth + m.left;
38488             b.y = centerY + m.top;
38489             centerW -= totalWidth;
38490             east.updateBox(this.safeBox(b));
38491         }
38492         if(center){
38493             var m = center.getMargins();
38494             var centerBox = {
38495                 x: centerX + m.left,
38496                 y: centerY + m.top,
38497                 width: centerW - (m.left+m.right),
38498                 height: centerH - (m.top+m.bottom)
38499             };
38500             //if(this.hideOnLayout){
38501                 //center.el.setStyle("display", "block");
38502             //}
38503             center.updateBox(this.safeBox(centerBox));
38504         }
38505         this.el.repaint();
38506         this.fireEvent("layout", this);
38507     },
38508
38509     // private
38510     safeBox : function(box){
38511         box.width = Math.max(0, box.width);
38512         box.height = Math.max(0, box.height);
38513         return box;
38514     },
38515
38516     /**
38517      * Adds a ContentPanel (or subclass) to this layout.
38518      * @param {String} target The target region key (north, south, east, west or center).
38519      * @param {Roo.ContentPanel} panel The panel to add
38520      * @return {Roo.ContentPanel} The added panel
38521      */
38522     add : function(target, panel){
38523          
38524         target = target.toLowerCase();
38525         return this.regions[target].add(panel);
38526     },
38527
38528     /**
38529      * Remove a ContentPanel (or subclass) to this layout.
38530      * @param {String} target The target region key (north, south, east, west or center).
38531      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38532      * @return {Roo.ContentPanel} The removed panel
38533      */
38534     remove : function(target, panel){
38535         target = target.toLowerCase();
38536         return this.regions[target].remove(panel);
38537     },
38538
38539     /**
38540      * Searches all regions for a panel with the specified id
38541      * @param {String} panelId
38542      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38543      */
38544     findPanel : function(panelId){
38545         var rs = this.regions;
38546         for(var target in rs){
38547             if(typeof rs[target] != "function"){
38548                 var p = rs[target].getPanel(panelId);
38549                 if(p){
38550                     return p;
38551                 }
38552             }
38553         }
38554         return null;
38555     },
38556
38557     /**
38558      * Searches all regions for a panel with the specified id and activates (shows) it.
38559      * @param {String/ContentPanel} panelId The panels id or the panel itself
38560      * @return {Roo.ContentPanel} The shown panel or null
38561      */
38562     showPanel : function(panelId) {
38563       var rs = this.regions;
38564       for(var target in rs){
38565          var r = rs[target];
38566          if(typeof r != "function"){
38567             if(r.hasPanel(panelId)){
38568                return r.showPanel(panelId);
38569             }
38570          }
38571       }
38572       return null;
38573    },
38574
38575    /**
38576      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38577      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38578      */
38579    /*
38580     restoreState : function(provider){
38581         if(!provider){
38582             provider = Roo.state.Manager;
38583         }
38584         var sm = new Roo.LayoutStateManager();
38585         sm.init(this, provider);
38586     },
38587 */
38588  
38589  
38590     /**
38591      * Adds a xtype elements to the layout.
38592      * <pre><code>
38593
38594 layout.addxtype({
38595        xtype : 'ContentPanel',
38596        region: 'west',
38597        items: [ .... ]
38598    }
38599 );
38600
38601 layout.addxtype({
38602         xtype : 'NestedLayoutPanel',
38603         region: 'west',
38604         layout: {
38605            center: { },
38606            west: { }   
38607         },
38608         items : [ ... list of content panels or nested layout panels.. ]
38609    }
38610 );
38611 </code></pre>
38612      * @param {Object} cfg Xtype definition of item to add.
38613      */
38614     addxtype : function(cfg)
38615     {
38616         // basically accepts a pannel...
38617         // can accept a layout region..!?!?
38618         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38619         
38620         
38621         // theory?  children can only be panels??
38622         
38623         //if (!cfg.xtype.match(/Panel$/)) {
38624         //    return false;
38625         //}
38626         var ret = false;
38627         
38628         if (typeof(cfg.region) == 'undefined') {
38629             Roo.log("Failed to add Panel, region was not set");
38630             Roo.log(cfg);
38631             return false;
38632         }
38633         var region = cfg.region;
38634         delete cfg.region;
38635         
38636           
38637         var xitems = [];
38638         if (cfg.items) {
38639             xitems = cfg.items;
38640             delete cfg.items;
38641         }
38642         var nb = false;
38643         
38644         if ( region == 'center') {
38645             Roo.log("Center: " + cfg.title);
38646         }
38647         
38648         
38649         switch(cfg.xtype) 
38650         {
38651             case 'Content':  // ContentPanel (el, cfg)
38652             case 'Scroll':  // ContentPanel (el, cfg)
38653             case 'View': 
38654                 cfg.autoCreate = cfg.autoCreate || true;
38655                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38656                 //} else {
38657                 //    var el = this.el.createChild();
38658                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38659                 //}
38660                 
38661                 this.add(region, ret);
38662                 break;
38663             
38664             /*
38665             case 'TreePanel': // our new panel!
38666                 cfg.el = this.el.createChild();
38667                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38668                 this.add(region, ret);
38669                 break;
38670             */
38671             
38672             case 'Nest': 
38673                 // create a new Layout (which is  a Border Layout...
38674                 
38675                 var clayout = cfg.layout;
38676                 clayout.el  = this.el.createChild();
38677                 clayout.items   = clayout.items  || [];
38678                 
38679                 delete cfg.layout;
38680                 
38681                 // replace this exitems with the clayout ones..
38682                 xitems = clayout.items;
38683                  
38684                 // force background off if it's in center...
38685                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38686                     cfg.background = false;
38687                 }
38688                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38689                 
38690                 
38691                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38692                 //console.log('adding nested layout panel '  + cfg.toSource());
38693                 this.add(region, ret);
38694                 nb = {}; /// find first...
38695                 break;
38696             
38697             case 'Grid':
38698                 
38699                 // needs grid and region
38700                 
38701                 //var el = this.getRegion(region).el.createChild();
38702                 /*
38703                  *var el = this.el.createChild();
38704                 // create the grid first...
38705                 cfg.grid.container = el;
38706                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38707                 */
38708                 
38709                 if (region == 'center' && this.active ) {
38710                     cfg.background = false;
38711                 }
38712                 
38713                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38714                 
38715                 this.add(region, ret);
38716                 /*
38717                 if (cfg.background) {
38718                     // render grid on panel activation (if panel background)
38719                     ret.on('activate', function(gp) {
38720                         if (!gp.grid.rendered) {
38721                     //        gp.grid.render(el);
38722                         }
38723                     });
38724                 } else {
38725                   //  cfg.grid.render(el);
38726                 }
38727                 */
38728                 break;
38729            
38730            
38731             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38732                 // it was the old xcomponent building that caused this before.
38733                 // espeically if border is the top element in the tree.
38734                 ret = this;
38735                 break; 
38736                 
38737                     
38738                 
38739                 
38740                 
38741             default:
38742                 /*
38743                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38744                     
38745                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38746                     this.add(region, ret);
38747                 } else {
38748                 */
38749                     Roo.log(cfg);
38750                     throw "Can not add '" + cfg.xtype + "' to Border";
38751                     return null;
38752              
38753                                 
38754              
38755         }
38756         this.beginUpdate();
38757         // add children..
38758         var region = '';
38759         var abn = {};
38760         Roo.each(xitems, function(i)  {
38761             region = nb && i.region ? i.region : false;
38762             
38763             var add = ret.addxtype(i);
38764            
38765             if (region) {
38766                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38767                 if (!i.background) {
38768                     abn[region] = nb[region] ;
38769                 }
38770             }
38771             
38772         });
38773         this.endUpdate();
38774
38775         // make the last non-background panel active..
38776         //if (nb) { Roo.log(abn); }
38777         if (nb) {
38778             
38779             for(var r in abn) {
38780                 region = this.getRegion(r);
38781                 if (region) {
38782                     // tried using nb[r], but it does not work..
38783                      
38784                     region.showPanel(abn[r]);
38785                    
38786                 }
38787             }
38788         }
38789         return ret;
38790         
38791     },
38792     
38793     
38794 // private
38795     factory : function(cfg)
38796     {
38797         
38798         var validRegions = Roo.bootstrap.layout.Border.regions;
38799
38800         var target = cfg.region;
38801         cfg.mgr = this;
38802         
38803         var r = Roo.bootstrap.layout;
38804         Roo.log(target);
38805         switch(target){
38806             case "north":
38807                 return new r.North(cfg);
38808             case "south":
38809                 return new r.South(cfg);
38810             case "east":
38811                 return new r.East(cfg);
38812             case "west":
38813                 return new r.West(cfg);
38814             case "center":
38815                 return new r.Center(cfg);
38816         }
38817         throw 'Layout region "'+target+'" not supported.';
38818     }
38819     
38820     
38821 });
38822  /*
38823  * Based on:
38824  * Ext JS Library 1.1.1
38825  * Copyright(c) 2006-2007, Ext JS, LLC.
38826  *
38827  * Originally Released Under LGPL - original licence link has changed is not relivant.
38828  *
38829  * Fork - LGPL
38830  * <script type="text/javascript">
38831  */
38832  
38833 /**
38834  * @class Roo.bootstrap.layout.Basic
38835  * @extends Roo.util.Observable
38836  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38837  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38838  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38839  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38840  * @cfg {string}   region  the region that it inhabits..
38841  * @cfg {bool}   skipConfig skip config?
38842  * 
38843
38844  */
38845 Roo.bootstrap.layout.Basic = function(config){
38846     
38847     this.mgr = config.mgr;
38848     
38849     this.position = config.region;
38850     
38851     var skipConfig = config.skipConfig;
38852     
38853     this.events = {
38854         /**
38855          * @scope Roo.BasicLayoutRegion
38856          */
38857         
38858         /**
38859          * @event beforeremove
38860          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38861          * @param {Roo.LayoutRegion} this
38862          * @param {Roo.ContentPanel} panel The panel
38863          * @param {Object} e The cancel event object
38864          */
38865         "beforeremove" : true,
38866         /**
38867          * @event invalidated
38868          * Fires when the layout for this region is changed.
38869          * @param {Roo.LayoutRegion} this
38870          */
38871         "invalidated" : true,
38872         /**
38873          * @event visibilitychange
38874          * Fires when this region is shown or hidden 
38875          * @param {Roo.LayoutRegion} this
38876          * @param {Boolean} visibility true or false
38877          */
38878         "visibilitychange" : true,
38879         /**
38880          * @event paneladded
38881          * Fires when a panel is added. 
38882          * @param {Roo.LayoutRegion} this
38883          * @param {Roo.ContentPanel} panel The panel
38884          */
38885         "paneladded" : true,
38886         /**
38887          * @event panelremoved
38888          * Fires when a panel is removed. 
38889          * @param {Roo.LayoutRegion} this
38890          * @param {Roo.ContentPanel} panel The panel
38891          */
38892         "panelremoved" : true,
38893         /**
38894          * @event beforecollapse
38895          * Fires when this region before collapse.
38896          * @param {Roo.LayoutRegion} this
38897          */
38898         "beforecollapse" : true,
38899         /**
38900          * @event collapsed
38901          * Fires when this region is collapsed.
38902          * @param {Roo.LayoutRegion} this
38903          */
38904         "collapsed" : true,
38905         /**
38906          * @event expanded
38907          * Fires when this region is expanded.
38908          * @param {Roo.LayoutRegion} this
38909          */
38910         "expanded" : true,
38911         /**
38912          * @event slideshow
38913          * Fires when this region is slid into view.
38914          * @param {Roo.LayoutRegion} this
38915          */
38916         "slideshow" : true,
38917         /**
38918          * @event slidehide
38919          * Fires when this region slides out of view. 
38920          * @param {Roo.LayoutRegion} this
38921          */
38922         "slidehide" : true,
38923         /**
38924          * @event panelactivated
38925          * Fires when a panel is activated. 
38926          * @param {Roo.LayoutRegion} this
38927          * @param {Roo.ContentPanel} panel The activated panel
38928          */
38929         "panelactivated" : true,
38930         /**
38931          * @event resized
38932          * Fires when the user resizes this region. 
38933          * @param {Roo.LayoutRegion} this
38934          * @param {Number} newSize The new size (width for east/west, height for north/south)
38935          */
38936         "resized" : true
38937     };
38938     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38939     this.panels = new Roo.util.MixedCollection();
38940     this.panels.getKey = this.getPanelId.createDelegate(this);
38941     this.box = null;
38942     this.activePanel = null;
38943     // ensure listeners are added...
38944     
38945     if (config.listeners || config.events) {
38946         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38947             listeners : config.listeners || {},
38948             events : config.events || {}
38949         });
38950     }
38951     
38952     if(skipConfig !== true){
38953         this.applyConfig(config);
38954     }
38955 };
38956
38957 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38958 {
38959     getPanelId : function(p){
38960         return p.getId();
38961     },
38962     
38963     applyConfig : function(config){
38964         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38965         this.config = config;
38966         
38967     },
38968     
38969     /**
38970      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38971      * the width, for horizontal (north, south) the height.
38972      * @param {Number} newSize The new width or height
38973      */
38974     resizeTo : function(newSize){
38975         var el = this.el ? this.el :
38976                  (this.activePanel ? this.activePanel.getEl() : null);
38977         if(el){
38978             switch(this.position){
38979                 case "east":
38980                 case "west":
38981                     el.setWidth(newSize);
38982                     this.fireEvent("resized", this, newSize);
38983                 break;
38984                 case "north":
38985                 case "south":
38986                     el.setHeight(newSize);
38987                     this.fireEvent("resized", this, newSize);
38988                 break;                
38989             }
38990         }
38991     },
38992     
38993     getBox : function(){
38994         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38995     },
38996     
38997     getMargins : function(){
38998         return this.margins;
38999     },
39000     
39001     updateBox : function(box){
39002         this.box = box;
39003         var el = this.activePanel.getEl();
39004         el.dom.style.left = box.x + "px";
39005         el.dom.style.top = box.y + "px";
39006         this.activePanel.setSize(box.width, box.height);
39007     },
39008     
39009     /**
39010      * Returns the container element for this region.
39011      * @return {Roo.Element}
39012      */
39013     getEl : function(){
39014         return this.activePanel;
39015     },
39016     
39017     /**
39018      * Returns true if this region is currently visible.
39019      * @return {Boolean}
39020      */
39021     isVisible : function(){
39022         return this.activePanel ? true : false;
39023     },
39024     
39025     setActivePanel : function(panel){
39026         panel = this.getPanel(panel);
39027         if(this.activePanel && this.activePanel != panel){
39028             this.activePanel.setActiveState(false);
39029             this.activePanel.getEl().setLeftTop(-10000,-10000);
39030         }
39031         this.activePanel = panel;
39032         panel.setActiveState(true);
39033         if(this.box){
39034             panel.setSize(this.box.width, this.box.height);
39035         }
39036         this.fireEvent("panelactivated", this, panel);
39037         this.fireEvent("invalidated");
39038     },
39039     
39040     /**
39041      * Show the specified panel.
39042      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39043      * @return {Roo.ContentPanel} The shown panel or null
39044      */
39045     showPanel : function(panel){
39046         panel = this.getPanel(panel);
39047         if(panel){
39048             this.setActivePanel(panel);
39049         }
39050         return panel;
39051     },
39052     
39053     /**
39054      * Get the active panel for this region.
39055      * @return {Roo.ContentPanel} The active panel or null
39056      */
39057     getActivePanel : function(){
39058         return this.activePanel;
39059     },
39060     
39061     /**
39062      * Add the passed ContentPanel(s)
39063      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39064      * @return {Roo.ContentPanel} The panel added (if only one was added)
39065      */
39066     add : function(panel){
39067         if(arguments.length > 1){
39068             for(var i = 0, len = arguments.length; i < len; i++) {
39069                 this.add(arguments[i]);
39070             }
39071             return null;
39072         }
39073         if(this.hasPanel(panel)){
39074             this.showPanel(panel);
39075             return panel;
39076         }
39077         var el = panel.getEl();
39078         if(el.dom.parentNode != this.mgr.el.dom){
39079             this.mgr.el.dom.appendChild(el.dom);
39080         }
39081         if(panel.setRegion){
39082             panel.setRegion(this);
39083         }
39084         this.panels.add(panel);
39085         el.setStyle("position", "absolute");
39086         if(!panel.background){
39087             this.setActivePanel(panel);
39088             if(this.config.initialSize && this.panels.getCount()==1){
39089                 this.resizeTo(this.config.initialSize);
39090             }
39091         }
39092         this.fireEvent("paneladded", this, panel);
39093         return panel;
39094     },
39095     
39096     /**
39097      * Returns true if the panel is in this region.
39098      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39099      * @return {Boolean}
39100      */
39101     hasPanel : function(panel){
39102         if(typeof panel == "object"){ // must be panel obj
39103             panel = panel.getId();
39104         }
39105         return this.getPanel(panel) ? true : false;
39106     },
39107     
39108     /**
39109      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39110      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39111      * @param {Boolean} preservePanel Overrides the config preservePanel option
39112      * @return {Roo.ContentPanel} The panel that was removed
39113      */
39114     remove : function(panel, preservePanel){
39115         panel = this.getPanel(panel);
39116         if(!panel){
39117             return null;
39118         }
39119         var e = {};
39120         this.fireEvent("beforeremove", this, panel, e);
39121         if(e.cancel === true){
39122             return null;
39123         }
39124         var panelId = panel.getId();
39125         this.panels.removeKey(panelId);
39126         return panel;
39127     },
39128     
39129     /**
39130      * Returns the panel specified or null if it's not in this region.
39131      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39132      * @return {Roo.ContentPanel}
39133      */
39134     getPanel : function(id){
39135         if(typeof id == "object"){ // must be panel obj
39136             return id;
39137         }
39138         return this.panels.get(id);
39139     },
39140     
39141     /**
39142      * Returns this regions position (north/south/east/west/center).
39143      * @return {String} 
39144      */
39145     getPosition: function(){
39146         return this.position;    
39147     }
39148 });/*
39149  * Based on:
39150  * Ext JS Library 1.1.1
39151  * Copyright(c) 2006-2007, Ext JS, LLC.
39152  *
39153  * Originally Released Under LGPL - original licence link has changed is not relivant.
39154  *
39155  * Fork - LGPL
39156  * <script type="text/javascript">
39157  */
39158  
39159 /**
39160  * @class Roo.bootstrap.layout.Region
39161  * @extends Roo.bootstrap.layout.Basic
39162  * This class represents a region in a layout manager.
39163  
39164  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39165  * @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})
39166  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39167  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39168  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39169  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39170  * @cfg {String}    title           The title for the region (overrides panel titles)
39171  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39172  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39173  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39174  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39175  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39176  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39177  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39178  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39179  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39180  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39181
39182  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39183  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39184  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39185  * @cfg {Number}    width           For East/West panels
39186  * @cfg {Number}    height          For North/South panels
39187  * @cfg {Boolean}   split           To show the splitter
39188  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39189  * 
39190  * @cfg {string}   cls             Extra CSS classes to add to region
39191  * 
39192  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39193  * @cfg {string}   region  the region that it inhabits..
39194  *
39195
39196  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39197  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39198
39199  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39200  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39201  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39202  */
39203 Roo.bootstrap.layout.Region = function(config)
39204 {
39205     this.applyConfig(config);
39206
39207     var mgr = config.mgr;
39208     var pos = config.region;
39209     config.skipConfig = true;
39210     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39211     
39212     if (mgr.el) {
39213         this.onRender(mgr.el);   
39214     }
39215      
39216     this.visible = true;
39217     this.collapsed = false;
39218     this.unrendered_panels = [];
39219 };
39220
39221 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39222
39223     position: '', // set by wrapper (eg. north/south etc..)
39224     unrendered_panels : null,  // unrendered panels.
39225     
39226     tabPosition : false,
39227     
39228     mgr: false, // points to 'Border'
39229     
39230     
39231     createBody : function(){
39232         /** This region's body element 
39233         * @type Roo.Element */
39234         this.bodyEl = this.el.createChild({
39235                 tag: "div",
39236                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39237         });
39238     },
39239
39240     onRender: function(ctr, pos)
39241     {
39242         var dh = Roo.DomHelper;
39243         /** This region's container element 
39244         * @type Roo.Element */
39245         this.el = dh.append(ctr.dom, {
39246                 tag: "div",
39247                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39248             }, true);
39249         /** This region's title element 
39250         * @type Roo.Element */
39251     
39252         this.titleEl = dh.append(this.el.dom,  {
39253                 tag: "div",
39254                 unselectable: "on",
39255                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39256                 children:[
39257                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39258                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39259                 ]
39260             }, true);
39261         
39262         this.titleEl.enableDisplayMode();
39263         /** This region's title text element 
39264         * @type HTMLElement */
39265         this.titleTextEl = this.titleEl.dom.firstChild;
39266         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39267         /*
39268         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39269         this.closeBtn.enableDisplayMode();
39270         this.closeBtn.on("click", this.closeClicked, this);
39271         this.closeBtn.hide();
39272     */
39273         this.createBody(this.config);
39274         if(this.config.hideWhenEmpty){
39275             this.hide();
39276             this.on("paneladded", this.validateVisibility, this);
39277             this.on("panelremoved", this.validateVisibility, this);
39278         }
39279         if(this.autoScroll){
39280             this.bodyEl.setStyle("overflow", "auto");
39281         }else{
39282             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39283         }
39284         //if(c.titlebar !== false){
39285             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39286                 this.titleEl.hide();
39287             }else{
39288                 this.titleEl.show();
39289                 if(this.config.title){
39290                     this.titleTextEl.innerHTML = this.config.title;
39291                 }
39292             }
39293         //}
39294         if(this.config.collapsed){
39295             this.collapse(true);
39296         }
39297         if(this.config.hidden){
39298             this.hide();
39299         }
39300         
39301         if (this.unrendered_panels && this.unrendered_panels.length) {
39302             for (var i =0;i< this.unrendered_panels.length; i++) {
39303                 this.add(this.unrendered_panels[i]);
39304             }
39305             this.unrendered_panels = null;
39306             
39307         }
39308         
39309     },
39310     
39311     applyConfig : function(c)
39312     {
39313         /*
39314          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39315             var dh = Roo.DomHelper;
39316             if(c.titlebar !== false){
39317                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39318                 this.collapseBtn.on("click", this.collapse, this);
39319                 this.collapseBtn.enableDisplayMode();
39320                 /*
39321                 if(c.showPin === true || this.showPin){
39322                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39323                     this.stickBtn.enableDisplayMode();
39324                     this.stickBtn.on("click", this.expand, this);
39325                     this.stickBtn.hide();
39326                 }
39327                 
39328             }
39329             */
39330             /** This region's collapsed element
39331             * @type Roo.Element */
39332             /*
39333              *
39334             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39335                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39336             ]}, true);
39337             
39338             if(c.floatable !== false){
39339                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39340                this.collapsedEl.on("click", this.collapseClick, this);
39341             }
39342
39343             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39344                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39345                    id: "message", unselectable: "on", style:{"float":"left"}});
39346                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39347              }
39348             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39349             this.expandBtn.on("click", this.expand, this);
39350             
39351         }
39352         
39353         if(this.collapseBtn){
39354             this.collapseBtn.setVisible(c.collapsible == true);
39355         }
39356         
39357         this.cmargins = c.cmargins || this.cmargins ||
39358                          (this.position == "west" || this.position == "east" ?
39359                              {top: 0, left: 2, right:2, bottom: 0} :
39360                              {top: 2, left: 0, right:0, bottom: 2});
39361         */
39362         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39363         
39364         
39365         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39366         
39367         this.autoScroll = c.autoScroll || false;
39368         
39369         
39370        
39371         
39372         this.duration = c.duration || .30;
39373         this.slideDuration = c.slideDuration || .45;
39374         this.config = c;
39375        
39376     },
39377     /**
39378      * Returns true if this region is currently visible.
39379      * @return {Boolean}
39380      */
39381     isVisible : function(){
39382         return this.visible;
39383     },
39384
39385     /**
39386      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39387      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39388      */
39389     //setCollapsedTitle : function(title){
39390     //    title = title || "&#160;";
39391      //   if(this.collapsedTitleTextEl){
39392       //      this.collapsedTitleTextEl.innerHTML = title;
39393        // }
39394     //},
39395
39396     getBox : function(){
39397         var b;
39398       //  if(!this.collapsed){
39399             b = this.el.getBox(false, true);
39400        // }else{
39401           //  b = this.collapsedEl.getBox(false, true);
39402         //}
39403         return b;
39404     },
39405
39406     getMargins : function(){
39407         return this.margins;
39408         //return this.collapsed ? this.cmargins : this.margins;
39409     },
39410 /*
39411     highlight : function(){
39412         this.el.addClass("x-layout-panel-dragover");
39413     },
39414
39415     unhighlight : function(){
39416         this.el.removeClass("x-layout-panel-dragover");
39417     },
39418 */
39419     updateBox : function(box)
39420     {
39421         if (!this.bodyEl) {
39422             return; // not rendered yet..
39423         }
39424         
39425         this.box = box;
39426         if(!this.collapsed){
39427             this.el.dom.style.left = box.x + "px";
39428             this.el.dom.style.top = box.y + "px";
39429             this.updateBody(box.width, box.height);
39430         }else{
39431             this.collapsedEl.dom.style.left = box.x + "px";
39432             this.collapsedEl.dom.style.top = box.y + "px";
39433             this.collapsedEl.setSize(box.width, box.height);
39434         }
39435         if(this.tabs){
39436             this.tabs.autoSizeTabs();
39437         }
39438     },
39439
39440     updateBody : function(w, h)
39441     {
39442         if(w !== null){
39443             this.el.setWidth(w);
39444             w -= this.el.getBorderWidth("rl");
39445             if(this.config.adjustments){
39446                 w += this.config.adjustments[0];
39447             }
39448         }
39449         if(h !== null && h > 0){
39450             this.el.setHeight(h);
39451             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39452             h -= this.el.getBorderWidth("tb");
39453             if(this.config.adjustments){
39454                 h += this.config.adjustments[1];
39455             }
39456             this.bodyEl.setHeight(h);
39457             if(this.tabs){
39458                 h = this.tabs.syncHeight(h);
39459             }
39460         }
39461         if(this.panelSize){
39462             w = w !== null ? w : this.panelSize.width;
39463             h = h !== null ? h : this.panelSize.height;
39464         }
39465         if(this.activePanel){
39466             var el = this.activePanel.getEl();
39467             w = w !== null ? w : el.getWidth();
39468             h = h !== null ? h : el.getHeight();
39469             this.panelSize = {width: w, height: h};
39470             this.activePanel.setSize(w, h);
39471         }
39472         if(Roo.isIE && this.tabs){
39473             this.tabs.el.repaint();
39474         }
39475     },
39476
39477     /**
39478      * Returns the container element for this region.
39479      * @return {Roo.Element}
39480      */
39481     getEl : function(){
39482         return this.el;
39483     },
39484
39485     /**
39486      * Hides this region.
39487      */
39488     hide : function(){
39489         //if(!this.collapsed){
39490             this.el.dom.style.left = "-2000px";
39491             this.el.hide();
39492         //}else{
39493          //   this.collapsedEl.dom.style.left = "-2000px";
39494          //   this.collapsedEl.hide();
39495        // }
39496         this.visible = false;
39497         this.fireEvent("visibilitychange", this, false);
39498     },
39499
39500     /**
39501      * Shows this region if it was previously hidden.
39502      */
39503     show : function(){
39504         //if(!this.collapsed){
39505             this.el.show();
39506         //}else{
39507         //    this.collapsedEl.show();
39508        // }
39509         this.visible = true;
39510         this.fireEvent("visibilitychange", this, true);
39511     },
39512 /*
39513     closeClicked : function(){
39514         if(this.activePanel){
39515             this.remove(this.activePanel);
39516         }
39517     },
39518
39519     collapseClick : function(e){
39520         if(this.isSlid){
39521            e.stopPropagation();
39522            this.slideIn();
39523         }else{
39524            e.stopPropagation();
39525            this.slideOut();
39526         }
39527     },
39528 */
39529     /**
39530      * Collapses this region.
39531      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39532      */
39533     /*
39534     collapse : function(skipAnim, skipCheck = false){
39535         if(this.collapsed) {
39536             return;
39537         }
39538         
39539         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39540             
39541             this.collapsed = true;
39542             if(this.split){
39543                 this.split.el.hide();
39544             }
39545             if(this.config.animate && skipAnim !== true){
39546                 this.fireEvent("invalidated", this);
39547                 this.animateCollapse();
39548             }else{
39549                 this.el.setLocation(-20000,-20000);
39550                 this.el.hide();
39551                 this.collapsedEl.show();
39552                 this.fireEvent("collapsed", this);
39553                 this.fireEvent("invalidated", this);
39554             }
39555         }
39556         
39557     },
39558 */
39559     animateCollapse : function(){
39560         // overridden
39561     },
39562
39563     /**
39564      * Expands this region if it was previously collapsed.
39565      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39566      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39567      */
39568     /*
39569     expand : function(e, skipAnim){
39570         if(e) {
39571             e.stopPropagation();
39572         }
39573         if(!this.collapsed || this.el.hasActiveFx()) {
39574             return;
39575         }
39576         if(this.isSlid){
39577             this.afterSlideIn();
39578             skipAnim = true;
39579         }
39580         this.collapsed = false;
39581         if(this.config.animate && skipAnim !== true){
39582             this.animateExpand();
39583         }else{
39584             this.el.show();
39585             if(this.split){
39586                 this.split.el.show();
39587             }
39588             this.collapsedEl.setLocation(-2000,-2000);
39589             this.collapsedEl.hide();
39590             this.fireEvent("invalidated", this);
39591             this.fireEvent("expanded", this);
39592         }
39593     },
39594 */
39595     animateExpand : function(){
39596         // overridden
39597     },
39598
39599     initTabs : function()
39600     {
39601         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39602         
39603         var ts = new Roo.bootstrap.panel.Tabs({
39604             el: this.bodyEl.dom,
39605             region : this,
39606             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39607             disableTooltips: this.config.disableTabTips,
39608             toolbar : this.config.toolbar
39609         });
39610         
39611         if(this.config.hideTabs){
39612             ts.stripWrap.setDisplayed(false);
39613         }
39614         this.tabs = ts;
39615         ts.resizeTabs = this.config.resizeTabs === true;
39616         ts.minTabWidth = this.config.minTabWidth || 40;
39617         ts.maxTabWidth = this.config.maxTabWidth || 250;
39618         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39619         ts.monitorResize = false;
39620         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39621         ts.bodyEl.addClass('roo-layout-tabs-body');
39622         this.panels.each(this.initPanelAsTab, this);
39623     },
39624
39625     initPanelAsTab : function(panel){
39626         var ti = this.tabs.addTab(
39627             panel.getEl().id,
39628             panel.getTitle(),
39629             null,
39630             this.config.closeOnTab && panel.isClosable(),
39631             panel.tpl
39632         );
39633         if(panel.tabTip !== undefined){
39634             ti.setTooltip(panel.tabTip);
39635         }
39636         ti.on("activate", function(){
39637               this.setActivePanel(panel);
39638         }, this);
39639         
39640         if(this.config.closeOnTab){
39641             ti.on("beforeclose", function(t, e){
39642                 e.cancel = true;
39643                 this.remove(panel);
39644             }, this);
39645         }
39646         
39647         panel.tabItem = ti;
39648         
39649         return ti;
39650     },
39651
39652     updatePanelTitle : function(panel, title)
39653     {
39654         if(this.activePanel == panel){
39655             this.updateTitle(title);
39656         }
39657         if(this.tabs){
39658             var ti = this.tabs.getTab(panel.getEl().id);
39659             ti.setText(title);
39660             if(panel.tabTip !== undefined){
39661                 ti.setTooltip(panel.tabTip);
39662             }
39663         }
39664     },
39665
39666     updateTitle : function(title){
39667         if(this.titleTextEl && !this.config.title){
39668             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39669         }
39670     },
39671
39672     setActivePanel : function(panel)
39673     {
39674         panel = this.getPanel(panel);
39675         if(this.activePanel && this.activePanel != panel){
39676             if(this.activePanel.setActiveState(false) === false){
39677                 return;
39678             }
39679         }
39680         this.activePanel = panel;
39681         panel.setActiveState(true);
39682         if(this.panelSize){
39683             panel.setSize(this.panelSize.width, this.panelSize.height);
39684         }
39685         if(this.closeBtn){
39686             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39687         }
39688         this.updateTitle(panel.getTitle());
39689         if(this.tabs){
39690             this.fireEvent("invalidated", this);
39691         }
39692         this.fireEvent("panelactivated", this, panel);
39693     },
39694
39695     /**
39696      * Shows the specified panel.
39697      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39698      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39699      */
39700     showPanel : function(panel)
39701     {
39702         panel = this.getPanel(panel);
39703         if(panel){
39704             if(this.tabs){
39705                 var tab = this.tabs.getTab(panel.getEl().id);
39706                 if(tab.isHidden()){
39707                     this.tabs.unhideTab(tab.id);
39708                 }
39709                 tab.activate();
39710             }else{
39711                 this.setActivePanel(panel);
39712             }
39713         }
39714         return panel;
39715     },
39716
39717     /**
39718      * Get the active panel for this region.
39719      * @return {Roo.ContentPanel} The active panel or null
39720      */
39721     getActivePanel : function(){
39722         return this.activePanel;
39723     },
39724
39725     validateVisibility : function(){
39726         if(this.panels.getCount() < 1){
39727             this.updateTitle("&#160;");
39728             this.closeBtn.hide();
39729             this.hide();
39730         }else{
39731             if(!this.isVisible()){
39732                 this.show();
39733             }
39734         }
39735     },
39736
39737     /**
39738      * Adds the passed ContentPanel(s) to this region.
39739      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39740      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39741      */
39742     add : function(panel)
39743     {
39744         if(arguments.length > 1){
39745             for(var i = 0, len = arguments.length; i < len; i++) {
39746                 this.add(arguments[i]);
39747             }
39748             return null;
39749         }
39750         
39751         // if we have not been rendered yet, then we can not really do much of this..
39752         if (!this.bodyEl) {
39753             this.unrendered_panels.push(panel);
39754             return panel;
39755         }
39756         
39757         
39758         
39759         
39760         if(this.hasPanel(panel)){
39761             this.showPanel(panel);
39762             return panel;
39763         }
39764         panel.setRegion(this);
39765         this.panels.add(panel);
39766        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39767             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39768             // and hide them... ???
39769             this.bodyEl.dom.appendChild(panel.getEl().dom);
39770             if(panel.background !== true){
39771                 this.setActivePanel(panel);
39772             }
39773             this.fireEvent("paneladded", this, panel);
39774             return panel;
39775         }
39776         */
39777         if(!this.tabs){
39778             this.initTabs();
39779         }else{
39780             this.initPanelAsTab(panel);
39781         }
39782         
39783         
39784         if(panel.background !== true){
39785             this.tabs.activate(panel.getEl().id);
39786         }
39787         this.fireEvent("paneladded", this, panel);
39788         return panel;
39789     },
39790
39791     /**
39792      * Hides the tab for the specified panel.
39793      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39794      */
39795     hidePanel : function(panel){
39796         if(this.tabs && (panel = this.getPanel(panel))){
39797             this.tabs.hideTab(panel.getEl().id);
39798         }
39799     },
39800
39801     /**
39802      * Unhides the tab for a previously hidden panel.
39803      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39804      */
39805     unhidePanel : function(panel){
39806         if(this.tabs && (panel = this.getPanel(panel))){
39807             this.tabs.unhideTab(panel.getEl().id);
39808         }
39809     },
39810
39811     clearPanels : function(){
39812         while(this.panels.getCount() > 0){
39813              this.remove(this.panels.first());
39814         }
39815     },
39816
39817     /**
39818      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39819      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39820      * @param {Boolean} preservePanel Overrides the config preservePanel option
39821      * @return {Roo.ContentPanel} The panel that was removed
39822      */
39823     remove : function(panel, preservePanel)
39824     {
39825         panel = this.getPanel(panel);
39826         if(!panel){
39827             return null;
39828         }
39829         var e = {};
39830         this.fireEvent("beforeremove", this, panel, e);
39831         if(e.cancel === true){
39832             return null;
39833         }
39834         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39835         var panelId = panel.getId();
39836         this.panels.removeKey(panelId);
39837         if(preservePanel){
39838             document.body.appendChild(panel.getEl().dom);
39839         }
39840         if(this.tabs){
39841             this.tabs.removeTab(panel.getEl().id);
39842         }else if (!preservePanel){
39843             this.bodyEl.dom.removeChild(panel.getEl().dom);
39844         }
39845         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39846             var p = this.panels.first();
39847             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39848             tempEl.appendChild(p.getEl().dom);
39849             this.bodyEl.update("");
39850             this.bodyEl.dom.appendChild(p.getEl().dom);
39851             tempEl = null;
39852             this.updateTitle(p.getTitle());
39853             this.tabs = null;
39854             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39855             this.setActivePanel(p);
39856         }
39857         panel.setRegion(null);
39858         if(this.activePanel == panel){
39859             this.activePanel = null;
39860         }
39861         if(this.config.autoDestroy !== false && preservePanel !== true){
39862             try{panel.destroy();}catch(e){}
39863         }
39864         this.fireEvent("panelremoved", this, panel);
39865         return panel;
39866     },
39867
39868     /**
39869      * Returns the TabPanel component used by this region
39870      * @return {Roo.TabPanel}
39871      */
39872     getTabs : function(){
39873         return this.tabs;
39874     },
39875
39876     createTool : function(parentEl, className){
39877         var btn = Roo.DomHelper.append(parentEl, {
39878             tag: "div",
39879             cls: "x-layout-tools-button",
39880             children: [ {
39881                 tag: "div",
39882                 cls: "roo-layout-tools-button-inner " + className,
39883                 html: "&#160;"
39884             }]
39885         }, true);
39886         btn.addClassOnOver("roo-layout-tools-button-over");
39887         return btn;
39888     }
39889 });/*
39890  * Based on:
39891  * Ext JS Library 1.1.1
39892  * Copyright(c) 2006-2007, Ext JS, LLC.
39893  *
39894  * Originally Released Under LGPL - original licence link has changed is not relivant.
39895  *
39896  * Fork - LGPL
39897  * <script type="text/javascript">
39898  */
39899  
39900
39901
39902 /**
39903  * @class Roo.SplitLayoutRegion
39904  * @extends Roo.LayoutRegion
39905  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39906  */
39907 Roo.bootstrap.layout.Split = function(config){
39908     this.cursor = config.cursor;
39909     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39910 };
39911
39912 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39913 {
39914     splitTip : "Drag to resize.",
39915     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39916     useSplitTips : false,
39917
39918     applyConfig : function(config){
39919         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39920     },
39921     
39922     onRender : function(ctr,pos) {
39923         
39924         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39925         if(!this.config.split){
39926             return;
39927         }
39928         if(!this.split){
39929             
39930             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39931                             tag: "div",
39932                             id: this.el.id + "-split",
39933                             cls: "roo-layout-split roo-layout-split-"+this.position,
39934                             html: "&#160;"
39935             });
39936             /** The SplitBar for this region 
39937             * @type Roo.SplitBar */
39938             // does not exist yet...
39939             Roo.log([this.position, this.orientation]);
39940             
39941             this.split = new Roo.bootstrap.SplitBar({
39942                 dragElement : splitEl,
39943                 resizingElement: this.el,
39944                 orientation : this.orientation
39945             });
39946             
39947             this.split.on("moved", this.onSplitMove, this);
39948             this.split.useShim = this.config.useShim === true;
39949             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39950             if(this.useSplitTips){
39951                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39952             }
39953             //if(config.collapsible){
39954             //    this.split.el.on("dblclick", this.collapse,  this);
39955             //}
39956         }
39957         if(typeof this.config.minSize != "undefined"){
39958             this.split.minSize = this.config.minSize;
39959         }
39960         if(typeof this.config.maxSize != "undefined"){
39961             this.split.maxSize = this.config.maxSize;
39962         }
39963         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39964             this.hideSplitter();
39965         }
39966         
39967     },
39968
39969     getHMaxSize : function(){
39970          var cmax = this.config.maxSize || 10000;
39971          var center = this.mgr.getRegion("center");
39972          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39973     },
39974
39975     getVMaxSize : function(){
39976          var cmax = this.config.maxSize || 10000;
39977          var center = this.mgr.getRegion("center");
39978          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39979     },
39980
39981     onSplitMove : function(split, newSize){
39982         this.fireEvent("resized", this, newSize);
39983     },
39984     
39985     /** 
39986      * Returns the {@link Roo.SplitBar} for this region.
39987      * @return {Roo.SplitBar}
39988      */
39989     getSplitBar : function(){
39990         return this.split;
39991     },
39992     
39993     hide : function(){
39994         this.hideSplitter();
39995         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39996     },
39997
39998     hideSplitter : function(){
39999         if(this.split){
40000             this.split.el.setLocation(-2000,-2000);
40001             this.split.el.hide();
40002         }
40003     },
40004
40005     show : function(){
40006         if(this.split){
40007             this.split.el.show();
40008         }
40009         Roo.bootstrap.layout.Split.superclass.show.call(this);
40010     },
40011     
40012     beforeSlide: function(){
40013         if(Roo.isGecko){// firefox overflow auto bug workaround
40014             this.bodyEl.clip();
40015             if(this.tabs) {
40016                 this.tabs.bodyEl.clip();
40017             }
40018             if(this.activePanel){
40019                 this.activePanel.getEl().clip();
40020                 
40021                 if(this.activePanel.beforeSlide){
40022                     this.activePanel.beforeSlide();
40023                 }
40024             }
40025         }
40026     },
40027     
40028     afterSlide : function(){
40029         if(Roo.isGecko){// firefox overflow auto bug workaround
40030             this.bodyEl.unclip();
40031             if(this.tabs) {
40032                 this.tabs.bodyEl.unclip();
40033             }
40034             if(this.activePanel){
40035                 this.activePanel.getEl().unclip();
40036                 if(this.activePanel.afterSlide){
40037                     this.activePanel.afterSlide();
40038                 }
40039             }
40040         }
40041     },
40042
40043     initAutoHide : function(){
40044         if(this.autoHide !== false){
40045             if(!this.autoHideHd){
40046                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40047                 this.autoHideHd = {
40048                     "mouseout": function(e){
40049                         if(!e.within(this.el, true)){
40050                             st.delay(500);
40051                         }
40052                     },
40053                     "mouseover" : function(e){
40054                         st.cancel();
40055                     },
40056                     scope : this
40057                 };
40058             }
40059             this.el.on(this.autoHideHd);
40060         }
40061     },
40062
40063     clearAutoHide : function(){
40064         if(this.autoHide !== false){
40065             this.el.un("mouseout", this.autoHideHd.mouseout);
40066             this.el.un("mouseover", this.autoHideHd.mouseover);
40067         }
40068     },
40069
40070     clearMonitor : function(){
40071         Roo.get(document).un("click", this.slideInIf, this);
40072     },
40073
40074     // these names are backwards but not changed for compat
40075     slideOut : function(){
40076         if(this.isSlid || this.el.hasActiveFx()){
40077             return;
40078         }
40079         this.isSlid = true;
40080         if(this.collapseBtn){
40081             this.collapseBtn.hide();
40082         }
40083         this.closeBtnState = this.closeBtn.getStyle('display');
40084         this.closeBtn.hide();
40085         if(this.stickBtn){
40086             this.stickBtn.show();
40087         }
40088         this.el.show();
40089         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40090         this.beforeSlide();
40091         this.el.setStyle("z-index", 10001);
40092         this.el.slideIn(this.getSlideAnchor(), {
40093             callback: function(){
40094                 this.afterSlide();
40095                 this.initAutoHide();
40096                 Roo.get(document).on("click", this.slideInIf, this);
40097                 this.fireEvent("slideshow", this);
40098             },
40099             scope: this,
40100             block: true
40101         });
40102     },
40103
40104     afterSlideIn : function(){
40105         this.clearAutoHide();
40106         this.isSlid = false;
40107         this.clearMonitor();
40108         this.el.setStyle("z-index", "");
40109         if(this.collapseBtn){
40110             this.collapseBtn.show();
40111         }
40112         this.closeBtn.setStyle('display', this.closeBtnState);
40113         if(this.stickBtn){
40114             this.stickBtn.hide();
40115         }
40116         this.fireEvent("slidehide", this);
40117     },
40118
40119     slideIn : function(cb){
40120         if(!this.isSlid || this.el.hasActiveFx()){
40121             Roo.callback(cb);
40122             return;
40123         }
40124         this.isSlid = false;
40125         this.beforeSlide();
40126         this.el.slideOut(this.getSlideAnchor(), {
40127             callback: function(){
40128                 this.el.setLeftTop(-10000, -10000);
40129                 this.afterSlide();
40130                 this.afterSlideIn();
40131                 Roo.callback(cb);
40132             },
40133             scope: this,
40134             block: true
40135         });
40136     },
40137     
40138     slideInIf : function(e){
40139         if(!e.within(this.el)){
40140             this.slideIn();
40141         }
40142     },
40143
40144     animateCollapse : function(){
40145         this.beforeSlide();
40146         this.el.setStyle("z-index", 20000);
40147         var anchor = this.getSlideAnchor();
40148         this.el.slideOut(anchor, {
40149             callback : function(){
40150                 this.el.setStyle("z-index", "");
40151                 this.collapsedEl.slideIn(anchor, {duration:.3});
40152                 this.afterSlide();
40153                 this.el.setLocation(-10000,-10000);
40154                 this.el.hide();
40155                 this.fireEvent("collapsed", this);
40156             },
40157             scope: this,
40158             block: true
40159         });
40160     },
40161
40162     animateExpand : function(){
40163         this.beforeSlide();
40164         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40165         this.el.setStyle("z-index", 20000);
40166         this.collapsedEl.hide({
40167             duration:.1
40168         });
40169         this.el.slideIn(this.getSlideAnchor(), {
40170             callback : function(){
40171                 this.el.setStyle("z-index", "");
40172                 this.afterSlide();
40173                 if(this.split){
40174                     this.split.el.show();
40175                 }
40176                 this.fireEvent("invalidated", this);
40177                 this.fireEvent("expanded", this);
40178             },
40179             scope: this,
40180             block: true
40181         });
40182     },
40183
40184     anchors : {
40185         "west" : "left",
40186         "east" : "right",
40187         "north" : "top",
40188         "south" : "bottom"
40189     },
40190
40191     sanchors : {
40192         "west" : "l",
40193         "east" : "r",
40194         "north" : "t",
40195         "south" : "b"
40196     },
40197
40198     canchors : {
40199         "west" : "tl-tr",
40200         "east" : "tr-tl",
40201         "north" : "tl-bl",
40202         "south" : "bl-tl"
40203     },
40204
40205     getAnchor : function(){
40206         return this.anchors[this.position];
40207     },
40208
40209     getCollapseAnchor : function(){
40210         return this.canchors[this.position];
40211     },
40212
40213     getSlideAnchor : function(){
40214         return this.sanchors[this.position];
40215     },
40216
40217     getAlignAdj : function(){
40218         var cm = this.cmargins;
40219         switch(this.position){
40220             case "west":
40221                 return [0, 0];
40222             break;
40223             case "east":
40224                 return [0, 0];
40225             break;
40226             case "north":
40227                 return [0, 0];
40228             break;
40229             case "south":
40230                 return [0, 0];
40231             break;
40232         }
40233     },
40234
40235     getExpandAdj : function(){
40236         var c = this.collapsedEl, cm = this.cmargins;
40237         switch(this.position){
40238             case "west":
40239                 return [-(cm.right+c.getWidth()+cm.left), 0];
40240             break;
40241             case "east":
40242                 return [cm.right+c.getWidth()+cm.left, 0];
40243             break;
40244             case "north":
40245                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40246             break;
40247             case "south":
40248                 return [0, cm.top+cm.bottom+c.getHeight()];
40249             break;
40250         }
40251     }
40252 });/*
40253  * Based on:
40254  * Ext JS Library 1.1.1
40255  * Copyright(c) 2006-2007, Ext JS, LLC.
40256  *
40257  * Originally Released Under LGPL - original licence link has changed is not relivant.
40258  *
40259  * Fork - LGPL
40260  * <script type="text/javascript">
40261  */
40262 /*
40263  * These classes are private internal classes
40264  */
40265 Roo.bootstrap.layout.Center = function(config){
40266     config.region = "center";
40267     Roo.bootstrap.layout.Region.call(this, config);
40268     this.visible = true;
40269     this.minWidth = config.minWidth || 20;
40270     this.minHeight = config.minHeight || 20;
40271 };
40272
40273 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40274     hide : function(){
40275         // center panel can't be hidden
40276     },
40277     
40278     show : function(){
40279         // center panel can't be hidden
40280     },
40281     
40282     getMinWidth: function(){
40283         return this.minWidth;
40284     },
40285     
40286     getMinHeight: function(){
40287         return this.minHeight;
40288     }
40289 });
40290
40291
40292
40293
40294  
40295
40296
40297
40298
40299
40300
40301 Roo.bootstrap.layout.North = function(config)
40302 {
40303     config.region = 'north';
40304     config.cursor = 'n-resize';
40305     
40306     Roo.bootstrap.layout.Split.call(this, config);
40307     
40308     
40309     if(this.split){
40310         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40311         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40312         this.split.el.addClass("roo-layout-split-v");
40313     }
40314     //var size = config.initialSize || config.height;
40315     //if(this.el && typeof size != "undefined"){
40316     //    this.el.setHeight(size);
40317     //}
40318 };
40319 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40320 {
40321     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40322      
40323      
40324     onRender : function(ctr, pos)
40325     {
40326         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40327         var size = this.config.initialSize || this.config.height;
40328         if(this.el && typeof size != "undefined"){
40329             this.el.setHeight(size);
40330         }
40331     
40332     },
40333     
40334     getBox : function(){
40335         if(this.collapsed){
40336             return this.collapsedEl.getBox();
40337         }
40338         var box = this.el.getBox();
40339         if(this.split){
40340             box.height += this.split.el.getHeight();
40341         }
40342         return box;
40343     },
40344     
40345     updateBox : function(box){
40346         if(this.split && !this.collapsed){
40347             box.height -= this.split.el.getHeight();
40348             this.split.el.setLeft(box.x);
40349             this.split.el.setTop(box.y+box.height);
40350             this.split.el.setWidth(box.width);
40351         }
40352         if(this.collapsed){
40353             this.updateBody(box.width, null);
40354         }
40355         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40356     }
40357 });
40358
40359
40360
40361
40362
40363 Roo.bootstrap.layout.South = function(config){
40364     config.region = 'south';
40365     config.cursor = 's-resize';
40366     Roo.bootstrap.layout.Split.call(this, config);
40367     if(this.split){
40368         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40369         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40370         this.split.el.addClass("roo-layout-split-v");
40371     }
40372     
40373 };
40374
40375 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40376     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40377     
40378     onRender : function(ctr, pos)
40379     {
40380         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40381         var size = this.config.initialSize || this.config.height;
40382         if(this.el && typeof size != "undefined"){
40383             this.el.setHeight(size);
40384         }
40385     
40386     },
40387     
40388     getBox : function(){
40389         if(this.collapsed){
40390             return this.collapsedEl.getBox();
40391         }
40392         var box = this.el.getBox();
40393         if(this.split){
40394             var sh = this.split.el.getHeight();
40395             box.height += sh;
40396             box.y -= sh;
40397         }
40398         return box;
40399     },
40400     
40401     updateBox : function(box){
40402         if(this.split && !this.collapsed){
40403             var sh = this.split.el.getHeight();
40404             box.height -= sh;
40405             box.y += sh;
40406             this.split.el.setLeft(box.x);
40407             this.split.el.setTop(box.y-sh);
40408             this.split.el.setWidth(box.width);
40409         }
40410         if(this.collapsed){
40411             this.updateBody(box.width, null);
40412         }
40413         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40414     }
40415 });
40416
40417 Roo.bootstrap.layout.East = function(config){
40418     config.region = "east";
40419     config.cursor = "e-resize";
40420     Roo.bootstrap.layout.Split.call(this, config);
40421     if(this.split){
40422         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40423         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40424         this.split.el.addClass("roo-layout-split-h");
40425     }
40426     
40427 };
40428 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40429     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40430     
40431     onRender : function(ctr, pos)
40432     {
40433         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40434         var size = this.config.initialSize || this.config.width;
40435         if(this.el && typeof size != "undefined"){
40436             this.el.setWidth(size);
40437         }
40438     
40439     },
40440     
40441     getBox : function(){
40442         if(this.collapsed){
40443             return this.collapsedEl.getBox();
40444         }
40445         var box = this.el.getBox();
40446         if(this.split){
40447             var sw = this.split.el.getWidth();
40448             box.width += sw;
40449             box.x -= sw;
40450         }
40451         return box;
40452     },
40453
40454     updateBox : function(box){
40455         if(this.split && !this.collapsed){
40456             var sw = this.split.el.getWidth();
40457             box.width -= sw;
40458             this.split.el.setLeft(box.x);
40459             this.split.el.setTop(box.y);
40460             this.split.el.setHeight(box.height);
40461             box.x += sw;
40462         }
40463         if(this.collapsed){
40464             this.updateBody(null, box.height);
40465         }
40466         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40467     }
40468 });
40469
40470 Roo.bootstrap.layout.West = function(config){
40471     config.region = "west";
40472     config.cursor = "w-resize";
40473     
40474     Roo.bootstrap.layout.Split.call(this, config);
40475     if(this.split){
40476         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40477         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40478         this.split.el.addClass("roo-layout-split-h");
40479     }
40480     
40481 };
40482 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40483     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40484     
40485     onRender: function(ctr, pos)
40486     {
40487         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40488         var size = this.config.initialSize || this.config.width;
40489         if(typeof size != "undefined"){
40490             this.el.setWidth(size);
40491         }
40492     },
40493     
40494     getBox : function(){
40495         if(this.collapsed){
40496             return this.collapsedEl.getBox();
40497         }
40498         var box = this.el.getBox();
40499         if (box.width == 0) {
40500             box.width = this.config.width; // kludge?
40501         }
40502         if(this.split){
40503             box.width += this.split.el.getWidth();
40504         }
40505         return box;
40506     },
40507     
40508     updateBox : function(box){
40509         if(this.split && !this.collapsed){
40510             var sw = this.split.el.getWidth();
40511             box.width -= sw;
40512             this.split.el.setLeft(box.x+box.width);
40513             this.split.el.setTop(box.y);
40514             this.split.el.setHeight(box.height);
40515         }
40516         if(this.collapsed){
40517             this.updateBody(null, box.height);
40518         }
40519         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40520     }
40521 });Roo.namespace("Roo.bootstrap.panel");/*
40522  * Based on:
40523  * Ext JS Library 1.1.1
40524  * Copyright(c) 2006-2007, Ext JS, LLC.
40525  *
40526  * Originally Released Under LGPL - original licence link has changed is not relivant.
40527  *
40528  * Fork - LGPL
40529  * <script type="text/javascript">
40530  */
40531 /**
40532  * @class Roo.ContentPanel
40533  * @extends Roo.util.Observable
40534  * A basic ContentPanel element.
40535  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40536  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40537  * @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
40538  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40539  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40540  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40541  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40542  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40543  * @cfg {String} title          The title for this panel
40544  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40545  * @cfg {String} url            Calls {@link #setUrl} with this value
40546  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40547  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40548  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40549  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40550  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40551  * @cfg {Boolean} badges render the badges
40552  * @cfg {String} cls  extra classes to use  
40553  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40554
40555  * @constructor
40556  * Create a new ContentPanel.
40557  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40558  * @param {String/Object} config A string to set only the title or a config object
40559  * @param {String} content (optional) Set the HTML content for this panel
40560  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40561  */
40562 Roo.bootstrap.panel.Content = function( config){
40563     
40564     this.tpl = config.tpl || false;
40565     
40566     var el = config.el;
40567     var content = config.content;
40568
40569     if(config.autoCreate){ // xtype is available if this is called from factory
40570         el = Roo.id();
40571     }
40572     this.el = Roo.get(el);
40573     if(!this.el && config && config.autoCreate){
40574         if(typeof config.autoCreate == "object"){
40575             if(!config.autoCreate.id){
40576                 config.autoCreate.id = config.id||el;
40577             }
40578             this.el = Roo.DomHelper.append(document.body,
40579                         config.autoCreate, true);
40580         }else{
40581             var elcfg =  {
40582                 tag: "div",
40583                 cls: (config.cls || '') +
40584                     (config.background ? ' bg-' + config.background : '') +
40585                     " roo-layout-inactive-content",
40586                 id: config.id||el
40587             };
40588             if (config.iframe) {
40589                 elcfg.cn = [
40590                     {
40591                         tag : 'iframe',
40592                         style : 'border: 0px',
40593                         src : 'about:blank'
40594                     }
40595                 ];
40596             }
40597               
40598             if (config.html) {
40599                 elcfg.html = config.html;
40600                 
40601             }
40602                         
40603             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40604             if (config.iframe) {
40605                 this.iframeEl = this.el.select('iframe',true).first();
40606             }
40607             
40608         }
40609     } 
40610     this.closable = false;
40611     this.loaded = false;
40612     this.active = false;
40613    
40614       
40615     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40616         
40617         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40618         
40619         this.wrapEl = this.el; //this.el.wrap();
40620         var ti = [];
40621         if (config.toolbar.items) {
40622             ti = config.toolbar.items ;
40623             delete config.toolbar.items ;
40624         }
40625         
40626         var nitems = [];
40627         this.toolbar.render(this.wrapEl, 'before');
40628         for(var i =0;i < ti.length;i++) {
40629           //  Roo.log(['add child', items[i]]);
40630             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40631         }
40632         this.toolbar.items = nitems;
40633         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40634         delete config.toolbar;
40635         
40636     }
40637     /*
40638     // xtype created footer. - not sure if will work as we normally have to render first..
40639     if (this.footer && !this.footer.el && this.footer.xtype) {
40640         if (!this.wrapEl) {
40641             this.wrapEl = this.el.wrap();
40642         }
40643     
40644         this.footer.container = this.wrapEl.createChild();
40645          
40646         this.footer = Roo.factory(this.footer, Roo);
40647         
40648     }
40649     */
40650     
40651      if(typeof config == "string"){
40652         this.title = config;
40653     }else{
40654         Roo.apply(this, config);
40655     }
40656     
40657     if(this.resizeEl){
40658         this.resizeEl = Roo.get(this.resizeEl, true);
40659     }else{
40660         this.resizeEl = this.el;
40661     }
40662     // handle view.xtype
40663     
40664  
40665     
40666     
40667     this.addEvents({
40668         /**
40669          * @event activate
40670          * Fires when this panel is activated. 
40671          * @param {Roo.ContentPanel} this
40672          */
40673         "activate" : true,
40674         /**
40675          * @event deactivate
40676          * Fires when this panel is activated. 
40677          * @param {Roo.ContentPanel} this
40678          */
40679         "deactivate" : true,
40680
40681         /**
40682          * @event resize
40683          * Fires when this panel is resized if fitToFrame is true.
40684          * @param {Roo.ContentPanel} this
40685          * @param {Number} width The width after any component adjustments
40686          * @param {Number} height The height after any component adjustments
40687          */
40688         "resize" : true,
40689         
40690          /**
40691          * @event render
40692          * Fires when this tab is created
40693          * @param {Roo.ContentPanel} this
40694          */
40695         "render" : true,
40696         
40697           /**
40698          * @event scroll
40699          * Fires when this content is scrolled
40700          * @param {Roo.ContentPanel} this
40701          * @param {Event} scrollEvent
40702          */
40703         "scroll" : true
40704         
40705         
40706         
40707     });
40708     
40709
40710     
40711     
40712     if(this.autoScroll && !this.iframe){
40713         this.resizeEl.setStyle("overflow", "auto");
40714         this.resizeEl.on('scroll', this.onScroll, this);
40715     } else {
40716         // fix randome scrolling
40717         //this.el.on('scroll', function() {
40718         //    Roo.log('fix random scolling');
40719         //    this.scrollTo('top',0); 
40720         //});
40721     }
40722     content = content || this.content;
40723     if(content){
40724         this.setContent(content);
40725     }
40726     if(config && config.url){
40727         this.setUrl(this.url, this.params, this.loadOnce);
40728     }
40729     
40730     
40731     
40732     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40733     
40734     if (this.view && typeof(this.view.xtype) != 'undefined') {
40735         this.view.el = this.el.appendChild(document.createElement("div"));
40736         this.view = Roo.factory(this.view); 
40737         this.view.render  &&  this.view.render(false, '');  
40738     }
40739     
40740     
40741     this.fireEvent('render', this);
40742 };
40743
40744 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40745     
40746     cls : '',
40747     background : '',
40748     
40749     tabTip : '',
40750     
40751     iframe : false,
40752     iframeEl : false,
40753     
40754     /* Resize Element - use this to work out scroll etc. */
40755     resizeEl : false,
40756     
40757     setRegion : function(region){
40758         this.region = region;
40759         this.setActiveClass(region && !this.background);
40760     },
40761     
40762     
40763     setActiveClass: function(state)
40764     {
40765         if(state){
40766            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40767            this.el.setStyle('position','relative');
40768         }else{
40769            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40770            this.el.setStyle('position', 'absolute');
40771         } 
40772     },
40773     
40774     /**
40775      * Returns the toolbar for this Panel if one was configured. 
40776      * @return {Roo.Toolbar} 
40777      */
40778     getToolbar : function(){
40779         return this.toolbar;
40780     },
40781     
40782     setActiveState : function(active)
40783     {
40784         this.active = active;
40785         this.setActiveClass(active);
40786         if(!active){
40787             if(this.fireEvent("deactivate", this) === false){
40788                 return false;
40789             }
40790             return true;
40791         }
40792         this.fireEvent("activate", this);
40793         return true;
40794     },
40795     /**
40796      * Updates this panel's element (not for iframe)
40797      * @param {String} content The new content
40798      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40799     */
40800     setContent : function(content, loadScripts){
40801         if (this.iframe) {
40802             return;
40803         }
40804         
40805         this.el.update(content, loadScripts);
40806     },
40807
40808     ignoreResize : function(w, h){
40809         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40810             return true;
40811         }else{
40812             this.lastSize = {width: w, height: h};
40813             return false;
40814         }
40815     },
40816     /**
40817      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40818      * @return {Roo.UpdateManager} The UpdateManager
40819      */
40820     getUpdateManager : function(){
40821         if (this.iframe) {
40822             return false;
40823         }
40824         return this.el.getUpdateManager();
40825     },
40826      /**
40827      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40828      * Does not work with IFRAME contents
40829      * @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:
40830 <pre><code>
40831 panel.load({
40832     url: "your-url.php",
40833     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40834     callback: yourFunction,
40835     scope: yourObject, //(optional scope)
40836     discardUrl: false,
40837     nocache: false,
40838     text: "Loading...",
40839     timeout: 30,
40840     scripts: false
40841 });
40842 </code></pre>
40843      
40844      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40845      * 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.
40846      * @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}
40847      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40848      * @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.
40849      * @return {Roo.ContentPanel} this
40850      */
40851     load : function(){
40852         
40853         if (this.iframe) {
40854             return this;
40855         }
40856         
40857         var um = this.el.getUpdateManager();
40858         um.update.apply(um, arguments);
40859         return this;
40860     },
40861
40862
40863     /**
40864      * 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.
40865      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40866      * @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)
40867      * @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)
40868      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40869      */
40870     setUrl : function(url, params, loadOnce){
40871         if (this.iframe) {
40872             this.iframeEl.dom.src = url;
40873             return false;
40874         }
40875         
40876         if(this.refreshDelegate){
40877             this.removeListener("activate", this.refreshDelegate);
40878         }
40879         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40880         this.on("activate", this.refreshDelegate);
40881         return this.el.getUpdateManager();
40882     },
40883     
40884     _handleRefresh : function(url, params, loadOnce){
40885         if(!loadOnce || !this.loaded){
40886             var updater = this.el.getUpdateManager();
40887             updater.update(url, params, this._setLoaded.createDelegate(this));
40888         }
40889     },
40890     
40891     _setLoaded : function(){
40892         this.loaded = true;
40893     }, 
40894     
40895     /**
40896      * Returns this panel's id
40897      * @return {String} 
40898      */
40899     getId : function(){
40900         return this.el.id;
40901     },
40902     
40903     /** 
40904      * Returns this panel's element - used by regiosn to add.
40905      * @return {Roo.Element} 
40906      */
40907     getEl : function(){
40908         return this.wrapEl || this.el;
40909     },
40910     
40911    
40912     
40913     adjustForComponents : function(width, height)
40914     {
40915         //Roo.log('adjustForComponents ');
40916         if(this.resizeEl != this.el){
40917             width -= this.el.getFrameWidth('lr');
40918             height -= this.el.getFrameWidth('tb');
40919         }
40920         if(this.toolbar){
40921             var te = this.toolbar.getEl();
40922             te.setWidth(width);
40923             height -= te.getHeight();
40924         }
40925         if(this.footer){
40926             var te = this.footer.getEl();
40927             te.setWidth(width);
40928             height -= te.getHeight();
40929         }
40930         
40931         
40932         if(this.adjustments){
40933             width += this.adjustments[0];
40934             height += this.adjustments[1];
40935         }
40936         return {"width": width, "height": height};
40937     },
40938     
40939     setSize : function(width, height){
40940         if(this.fitToFrame && !this.ignoreResize(width, height)){
40941             if(this.fitContainer && this.resizeEl != this.el){
40942                 this.el.setSize(width, height);
40943             }
40944             var size = this.adjustForComponents(width, height);
40945             if (this.iframe) {
40946                 this.iframeEl.setSize(width,height);
40947             }
40948             
40949             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40950             this.fireEvent('resize', this, size.width, size.height);
40951             
40952             
40953         }
40954     },
40955     
40956     /**
40957      * Returns this panel's title
40958      * @return {String} 
40959      */
40960     getTitle : function(){
40961         
40962         if (typeof(this.title) != 'object') {
40963             return this.title;
40964         }
40965         
40966         var t = '';
40967         for (var k in this.title) {
40968             if (!this.title.hasOwnProperty(k)) {
40969                 continue;
40970             }
40971             
40972             if (k.indexOf('-') >= 0) {
40973                 var s = k.split('-');
40974                 for (var i = 0; i<s.length; i++) {
40975                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40976                 }
40977             } else {
40978                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40979             }
40980         }
40981         return t;
40982     },
40983     
40984     /**
40985      * Set this panel's title
40986      * @param {String} title
40987      */
40988     setTitle : function(title){
40989         this.title = title;
40990         if(this.region){
40991             this.region.updatePanelTitle(this, title);
40992         }
40993     },
40994     
40995     /**
40996      * Returns true is this panel was configured to be closable
40997      * @return {Boolean} 
40998      */
40999     isClosable : function(){
41000         return this.closable;
41001     },
41002     
41003     beforeSlide : function(){
41004         this.el.clip();
41005         this.resizeEl.clip();
41006     },
41007     
41008     afterSlide : function(){
41009         this.el.unclip();
41010         this.resizeEl.unclip();
41011     },
41012     
41013     /**
41014      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41015      *   Will fail silently if the {@link #setUrl} method has not been called.
41016      *   This does not activate the panel, just updates its content.
41017      */
41018     refresh : function(){
41019         if(this.refreshDelegate){
41020            this.loaded = false;
41021            this.refreshDelegate();
41022         }
41023     },
41024     
41025     /**
41026      * Destroys this panel
41027      */
41028     destroy : function(){
41029         this.el.removeAllListeners();
41030         var tempEl = document.createElement("span");
41031         tempEl.appendChild(this.el.dom);
41032         tempEl.innerHTML = "";
41033         this.el.remove();
41034         this.el = null;
41035     },
41036     
41037     /**
41038      * form - if the content panel contains a form - this is a reference to it.
41039      * @type {Roo.form.Form}
41040      */
41041     form : false,
41042     /**
41043      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41044      *    This contains a reference to it.
41045      * @type {Roo.View}
41046      */
41047     view : false,
41048     
41049       /**
41050      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41051      * <pre><code>
41052
41053 layout.addxtype({
41054        xtype : 'Form',
41055        items: [ .... ]
41056    }
41057 );
41058
41059 </code></pre>
41060      * @param {Object} cfg Xtype definition of item to add.
41061      */
41062     
41063     
41064     getChildContainer: function () {
41065         return this.getEl();
41066     },
41067     
41068     
41069     onScroll : function(e)
41070     {
41071         this.fireEvent('scroll', this, e);
41072     }
41073     
41074     
41075     /*
41076         var  ret = new Roo.factory(cfg);
41077         return ret;
41078         
41079         
41080         // add form..
41081         if (cfg.xtype.match(/^Form$/)) {
41082             
41083             var el;
41084             //if (this.footer) {
41085             //    el = this.footer.container.insertSibling(false, 'before');
41086             //} else {
41087                 el = this.el.createChild();
41088             //}
41089
41090             this.form = new  Roo.form.Form(cfg);
41091             
41092             
41093             if ( this.form.allItems.length) {
41094                 this.form.render(el.dom);
41095             }
41096             return this.form;
41097         }
41098         // should only have one of theses..
41099         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41100             // views.. should not be just added - used named prop 'view''
41101             
41102             cfg.el = this.el.appendChild(document.createElement("div"));
41103             // factory?
41104             
41105             var ret = new Roo.factory(cfg);
41106              
41107              ret.render && ret.render(false, ''); // render blank..
41108             this.view = ret;
41109             return ret;
41110         }
41111         return false;
41112     }
41113     \*/
41114 });
41115  
41116 /**
41117  * @class Roo.bootstrap.panel.Grid
41118  * @extends Roo.bootstrap.panel.Content
41119  * @constructor
41120  * Create a new GridPanel.
41121  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41122  * @param {Object} config A the config object
41123   
41124  */
41125
41126
41127
41128 Roo.bootstrap.panel.Grid = function(config)
41129 {
41130     
41131       
41132     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41133         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41134
41135     config.el = this.wrapper;
41136     //this.el = this.wrapper;
41137     
41138       if (config.container) {
41139         // ctor'ed from a Border/panel.grid
41140         
41141         
41142         this.wrapper.setStyle("overflow", "hidden");
41143         this.wrapper.addClass('roo-grid-container');
41144
41145     }
41146     
41147     
41148     if(config.toolbar){
41149         var tool_el = this.wrapper.createChild();    
41150         this.toolbar = Roo.factory(config.toolbar);
41151         var ti = [];
41152         if (config.toolbar.items) {
41153             ti = config.toolbar.items ;
41154             delete config.toolbar.items ;
41155         }
41156         
41157         var nitems = [];
41158         this.toolbar.render(tool_el);
41159         for(var i =0;i < ti.length;i++) {
41160           //  Roo.log(['add child', items[i]]);
41161             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41162         }
41163         this.toolbar.items = nitems;
41164         
41165         delete config.toolbar;
41166     }
41167     
41168     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41169     config.grid.scrollBody = true;;
41170     config.grid.monitorWindowResize = false; // turn off autosizing
41171     config.grid.autoHeight = false;
41172     config.grid.autoWidth = false;
41173     
41174     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41175     
41176     if (config.background) {
41177         // render grid on panel activation (if panel background)
41178         this.on('activate', function(gp) {
41179             if (!gp.grid.rendered) {
41180                 gp.grid.render(this.wrapper);
41181                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41182             }
41183         });
41184             
41185     } else {
41186         this.grid.render(this.wrapper);
41187         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41188
41189     }
41190     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41191     // ??? needed ??? config.el = this.wrapper;
41192     
41193     
41194     
41195   
41196     // xtype created footer. - not sure if will work as we normally have to render first..
41197     if (this.footer && !this.footer.el && this.footer.xtype) {
41198         
41199         var ctr = this.grid.getView().getFooterPanel(true);
41200         this.footer.dataSource = this.grid.dataSource;
41201         this.footer = Roo.factory(this.footer, Roo);
41202         this.footer.render(ctr);
41203         
41204     }
41205     
41206     
41207     
41208     
41209      
41210 };
41211
41212 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41213     getId : function(){
41214         return this.grid.id;
41215     },
41216     
41217     /**
41218      * Returns the grid for this panel
41219      * @return {Roo.bootstrap.Table} 
41220      */
41221     getGrid : function(){
41222         return this.grid;    
41223     },
41224     
41225     setSize : function(width, height){
41226         if(!this.ignoreResize(width, height)){
41227             var grid = this.grid;
41228             var size = this.adjustForComponents(width, height);
41229             // tfoot is not a footer?
41230           
41231             
41232             var gridel = grid.getGridEl();
41233             gridel.setSize(size.width, size.height);
41234             
41235             var tbd = grid.getGridEl().select('tbody', true).first();
41236             var thd = grid.getGridEl().select('thead',true).first();
41237             var tbf= grid.getGridEl().select('tfoot', true).first();
41238
41239             if (tbf) {
41240                 size.height -= tbf.getHeight();
41241             }
41242             if (thd) {
41243                 size.height -= thd.getHeight();
41244             }
41245             
41246             tbd.setSize(size.width, size.height );
41247             // this is for the account management tab -seems to work there.
41248             var thd = grid.getGridEl().select('thead',true).first();
41249             //if (tbd) {
41250             //    tbd.setSize(size.width, size.height - thd.getHeight());
41251             //}
41252              
41253             grid.autoSize();
41254         }
41255     },
41256      
41257     
41258     
41259     beforeSlide : function(){
41260         this.grid.getView().scroller.clip();
41261     },
41262     
41263     afterSlide : function(){
41264         this.grid.getView().scroller.unclip();
41265     },
41266     
41267     destroy : function(){
41268         this.grid.destroy();
41269         delete this.grid;
41270         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41271     }
41272 });
41273
41274 /**
41275  * @class Roo.bootstrap.panel.Nest
41276  * @extends Roo.bootstrap.panel.Content
41277  * @constructor
41278  * Create a new Panel, that can contain a layout.Border.
41279  * 
41280  * 
41281  * @param {Roo.BorderLayout} layout The layout for this panel
41282  * @param {String/Object} config A string to set only the title or a config object
41283  */
41284 Roo.bootstrap.panel.Nest = function(config)
41285 {
41286     // construct with only one argument..
41287     /* FIXME - implement nicer consturctors
41288     if (layout.layout) {
41289         config = layout;
41290         layout = config.layout;
41291         delete config.layout;
41292     }
41293     if (layout.xtype && !layout.getEl) {
41294         // then layout needs constructing..
41295         layout = Roo.factory(layout, Roo);
41296     }
41297     */
41298     
41299     config.el =  config.layout.getEl();
41300     
41301     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41302     
41303     config.layout.monitorWindowResize = false; // turn off autosizing
41304     this.layout = config.layout;
41305     this.layout.getEl().addClass("roo-layout-nested-layout");
41306     this.layout.parent = this;
41307     
41308     
41309     
41310     
41311 };
41312
41313 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41314
41315     setSize : function(width, height){
41316         if(!this.ignoreResize(width, height)){
41317             var size = this.adjustForComponents(width, height);
41318             var el = this.layout.getEl();
41319             if (size.height < 1) {
41320                 el.setWidth(size.width);   
41321             } else {
41322                 el.setSize(size.width, size.height);
41323             }
41324             var touch = el.dom.offsetWidth;
41325             this.layout.layout();
41326             // ie requires a double layout on the first pass
41327             if(Roo.isIE && !this.initialized){
41328                 this.initialized = true;
41329                 this.layout.layout();
41330             }
41331         }
41332     },
41333     
41334     // activate all subpanels if not currently active..
41335     
41336     setActiveState : function(active){
41337         this.active = active;
41338         this.setActiveClass(active);
41339         
41340         if(!active){
41341             this.fireEvent("deactivate", this);
41342             return;
41343         }
41344         
41345         this.fireEvent("activate", this);
41346         // not sure if this should happen before or after..
41347         if (!this.layout) {
41348             return; // should not happen..
41349         }
41350         var reg = false;
41351         for (var r in this.layout.regions) {
41352             reg = this.layout.getRegion(r);
41353             if (reg.getActivePanel()) {
41354                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41355                 reg.setActivePanel(reg.getActivePanel());
41356                 continue;
41357             }
41358             if (!reg.panels.length) {
41359                 continue;
41360             }
41361             reg.showPanel(reg.getPanel(0));
41362         }
41363         
41364         
41365         
41366         
41367     },
41368     
41369     /**
41370      * Returns the nested BorderLayout for this panel
41371      * @return {Roo.BorderLayout} 
41372      */
41373     getLayout : function(){
41374         return this.layout;
41375     },
41376     
41377      /**
41378      * Adds a xtype elements to the layout of the nested panel
41379      * <pre><code>
41380
41381 panel.addxtype({
41382        xtype : 'ContentPanel',
41383        region: 'west',
41384        items: [ .... ]
41385    }
41386 );
41387
41388 panel.addxtype({
41389         xtype : 'NestedLayoutPanel',
41390         region: 'west',
41391         layout: {
41392            center: { },
41393            west: { }   
41394         },
41395         items : [ ... list of content panels or nested layout panels.. ]
41396    }
41397 );
41398 </code></pre>
41399      * @param {Object} cfg Xtype definition of item to add.
41400      */
41401     addxtype : function(cfg) {
41402         return this.layout.addxtype(cfg);
41403     
41404     }
41405 });/*
41406  * Based on:
41407  * Ext JS Library 1.1.1
41408  * Copyright(c) 2006-2007, Ext JS, LLC.
41409  *
41410  * Originally Released Under LGPL - original licence link has changed is not relivant.
41411  *
41412  * Fork - LGPL
41413  * <script type="text/javascript">
41414  */
41415 /**
41416  * @class Roo.TabPanel
41417  * @extends Roo.util.Observable
41418  * A lightweight tab container.
41419  * <br><br>
41420  * Usage:
41421  * <pre><code>
41422 // basic tabs 1, built from existing content
41423 var tabs = new Roo.TabPanel("tabs1");
41424 tabs.addTab("script", "View Script");
41425 tabs.addTab("markup", "View Markup");
41426 tabs.activate("script");
41427
41428 // more advanced tabs, built from javascript
41429 var jtabs = new Roo.TabPanel("jtabs");
41430 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41431
41432 // set up the UpdateManager
41433 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41434 var updater = tab2.getUpdateManager();
41435 updater.setDefaultUrl("ajax1.htm");
41436 tab2.on('activate', updater.refresh, updater, true);
41437
41438 // Use setUrl for Ajax loading
41439 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41440 tab3.setUrl("ajax2.htm", null, true);
41441
41442 // Disabled tab
41443 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41444 tab4.disable();
41445
41446 jtabs.activate("jtabs-1");
41447  * </code></pre>
41448  * @constructor
41449  * Create a new TabPanel.
41450  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41451  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41452  */
41453 Roo.bootstrap.panel.Tabs = function(config){
41454     /**
41455     * The container element for this TabPanel.
41456     * @type Roo.Element
41457     */
41458     this.el = Roo.get(config.el);
41459     delete config.el;
41460     if(config){
41461         if(typeof config == "boolean"){
41462             this.tabPosition = config ? "bottom" : "top";
41463         }else{
41464             Roo.apply(this, config);
41465         }
41466     }
41467     
41468     if(this.tabPosition == "bottom"){
41469         // if tabs are at the bottom = create the body first.
41470         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41471         this.el.addClass("roo-tabs-bottom");
41472     }
41473     // next create the tabs holders
41474     
41475     if (this.tabPosition == "west"){
41476         
41477         var reg = this.region; // fake it..
41478         while (reg) {
41479             if (!reg.mgr.parent) {
41480                 break;
41481             }
41482             reg = reg.mgr.parent.region;
41483         }
41484         Roo.log("got nest?");
41485         Roo.log(reg);
41486         if (reg.mgr.getRegion('west')) {
41487             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41488             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41489             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41490             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41491             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41492         
41493             
41494         }
41495         
41496         
41497     } else {
41498      
41499         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41500         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41501         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41502         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41503     }
41504     
41505     
41506     if(Roo.isIE){
41507         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41508     }
41509     
41510     // finally - if tabs are at the top, then create the body last..
41511     if(this.tabPosition != "bottom"){
41512         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41513          * @type Roo.Element
41514          */
41515         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41516         this.el.addClass("roo-tabs-top");
41517     }
41518     this.items = [];
41519
41520     this.bodyEl.setStyle("position", "relative");
41521
41522     this.active = null;
41523     this.activateDelegate = this.activate.createDelegate(this);
41524
41525     this.addEvents({
41526         /**
41527          * @event tabchange
41528          * Fires when the active tab changes
41529          * @param {Roo.TabPanel} this
41530          * @param {Roo.TabPanelItem} activePanel The new active tab
41531          */
41532         "tabchange": true,
41533         /**
41534          * @event beforetabchange
41535          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41536          * @param {Roo.TabPanel} this
41537          * @param {Object} e Set cancel to true on this object to cancel the tab change
41538          * @param {Roo.TabPanelItem} tab The tab being changed to
41539          */
41540         "beforetabchange" : true
41541     });
41542
41543     Roo.EventManager.onWindowResize(this.onResize, this);
41544     this.cpad = this.el.getPadding("lr");
41545     this.hiddenCount = 0;
41546
41547
41548     // toolbar on the tabbar support...
41549     if (this.toolbar) {
41550         alert("no toolbar support yet");
41551         this.toolbar  = false;
41552         /*
41553         var tcfg = this.toolbar;
41554         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41555         this.toolbar = new Roo.Toolbar(tcfg);
41556         if (Roo.isSafari) {
41557             var tbl = tcfg.container.child('table', true);
41558             tbl.setAttribute('width', '100%');
41559         }
41560         */
41561         
41562     }
41563    
41564
41565
41566     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41567 };
41568
41569 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41570     /*
41571      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41572      */
41573     tabPosition : "top",
41574     /*
41575      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41576      */
41577     currentTabWidth : 0,
41578     /*
41579      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41580      */
41581     minTabWidth : 40,
41582     /*
41583      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41584      */
41585     maxTabWidth : 250,
41586     /*
41587      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41588      */
41589     preferredTabWidth : 175,
41590     /*
41591      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41592      */
41593     resizeTabs : false,
41594     /*
41595      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41596      */
41597     monitorResize : true,
41598     /*
41599      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41600      */
41601     toolbar : false,  // set by caller..
41602     
41603     region : false, /// set by caller
41604     
41605     disableTooltips : true, // not used yet...
41606
41607     /**
41608      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41609      * @param {String} id The id of the div to use <b>or create</b>
41610      * @param {String} text The text for the tab
41611      * @param {String} content (optional) Content to put in the TabPanelItem body
41612      * @param {Boolean} closable (optional) True to create a close icon on the tab
41613      * @return {Roo.TabPanelItem} The created TabPanelItem
41614      */
41615     addTab : function(id, text, content, closable, tpl)
41616     {
41617         var item = new Roo.bootstrap.panel.TabItem({
41618             panel: this,
41619             id : id,
41620             text : text,
41621             closable : closable,
41622             tpl : tpl
41623         });
41624         this.addTabItem(item);
41625         if(content){
41626             item.setContent(content);
41627         }
41628         return item;
41629     },
41630
41631     /**
41632      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41633      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41634      * @return {Roo.TabPanelItem}
41635      */
41636     getTab : function(id){
41637         return this.items[id];
41638     },
41639
41640     /**
41641      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41642      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41643      */
41644     hideTab : function(id){
41645         var t = this.items[id];
41646         if(!t.isHidden()){
41647            t.setHidden(true);
41648            this.hiddenCount++;
41649            this.autoSizeTabs();
41650         }
41651     },
41652
41653     /**
41654      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41655      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41656      */
41657     unhideTab : function(id){
41658         var t = this.items[id];
41659         if(t.isHidden()){
41660            t.setHidden(false);
41661            this.hiddenCount--;
41662            this.autoSizeTabs();
41663         }
41664     },
41665
41666     /**
41667      * Adds an existing {@link Roo.TabPanelItem}.
41668      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41669      */
41670     addTabItem : function(item)
41671     {
41672         this.items[item.id] = item;
41673         this.items.push(item);
41674         this.autoSizeTabs();
41675       //  if(this.resizeTabs){
41676     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41677   //         this.autoSizeTabs();
41678 //        }else{
41679 //            item.autoSize();
41680        // }
41681     },
41682
41683     /**
41684      * Removes a {@link Roo.TabPanelItem}.
41685      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41686      */
41687     removeTab : function(id){
41688         var items = this.items;
41689         var tab = items[id];
41690         if(!tab) { return; }
41691         var index = items.indexOf(tab);
41692         if(this.active == tab && items.length > 1){
41693             var newTab = this.getNextAvailable(index);
41694             if(newTab) {
41695                 newTab.activate();
41696             }
41697         }
41698         this.stripEl.dom.removeChild(tab.pnode.dom);
41699         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41700             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41701         }
41702         items.splice(index, 1);
41703         delete this.items[tab.id];
41704         tab.fireEvent("close", tab);
41705         tab.purgeListeners();
41706         this.autoSizeTabs();
41707     },
41708
41709     getNextAvailable : function(start){
41710         var items = this.items;
41711         var index = start;
41712         // look for a next tab that will slide over to
41713         // replace the one being removed
41714         while(index < items.length){
41715             var item = items[++index];
41716             if(item && !item.isHidden()){
41717                 return item;
41718             }
41719         }
41720         // if one isn't found select the previous tab (on the left)
41721         index = start;
41722         while(index >= 0){
41723             var item = items[--index];
41724             if(item && !item.isHidden()){
41725                 return item;
41726             }
41727         }
41728         return null;
41729     },
41730
41731     /**
41732      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41733      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41734      */
41735     disableTab : function(id){
41736         var tab = this.items[id];
41737         if(tab && this.active != tab){
41738             tab.disable();
41739         }
41740     },
41741
41742     /**
41743      * Enables a {@link Roo.TabPanelItem} that is disabled.
41744      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41745      */
41746     enableTab : function(id){
41747         var tab = this.items[id];
41748         tab.enable();
41749     },
41750
41751     /**
41752      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41753      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41754      * @return {Roo.TabPanelItem} The TabPanelItem.
41755      */
41756     activate : function(id)
41757     {
41758         //Roo.log('activite:'  + id);
41759         
41760         var tab = this.items[id];
41761         if(!tab){
41762             return null;
41763         }
41764         if(tab == this.active || tab.disabled){
41765             return tab;
41766         }
41767         var e = {};
41768         this.fireEvent("beforetabchange", this, e, tab);
41769         if(e.cancel !== true && !tab.disabled){
41770             if(this.active){
41771                 this.active.hide();
41772             }
41773             this.active = this.items[id];
41774             this.active.show();
41775             this.fireEvent("tabchange", this, this.active);
41776         }
41777         return tab;
41778     },
41779
41780     /**
41781      * Gets the active {@link Roo.TabPanelItem}.
41782      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41783      */
41784     getActiveTab : function(){
41785         return this.active;
41786     },
41787
41788     /**
41789      * Updates the tab body element to fit the height of the container element
41790      * for overflow scrolling
41791      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41792      */
41793     syncHeight : function(targetHeight){
41794         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41795         var bm = this.bodyEl.getMargins();
41796         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41797         this.bodyEl.setHeight(newHeight);
41798         return newHeight;
41799     },
41800
41801     onResize : function(){
41802         if(this.monitorResize){
41803             this.autoSizeTabs();
41804         }
41805     },
41806
41807     /**
41808      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41809      */
41810     beginUpdate : function(){
41811         this.updating = true;
41812     },
41813
41814     /**
41815      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41816      */
41817     endUpdate : function(){
41818         this.updating = false;
41819         this.autoSizeTabs();
41820     },
41821
41822     /**
41823      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41824      */
41825     autoSizeTabs : function()
41826     {
41827         var count = this.items.length;
41828         var vcount = count - this.hiddenCount;
41829         
41830         if (vcount < 2) {
41831             this.stripEl.hide();
41832         } else {
41833             this.stripEl.show();
41834         }
41835         
41836         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41837             return;
41838         }
41839         
41840         
41841         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41842         var availWidth = Math.floor(w / vcount);
41843         var b = this.stripBody;
41844         if(b.getWidth() > w){
41845             var tabs = this.items;
41846             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41847             if(availWidth < this.minTabWidth){
41848                 /*if(!this.sleft){    // incomplete scrolling code
41849                     this.createScrollButtons();
41850                 }
41851                 this.showScroll();
41852                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41853             }
41854         }else{
41855             if(this.currentTabWidth < this.preferredTabWidth){
41856                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41857             }
41858         }
41859     },
41860
41861     /**
41862      * Returns the number of tabs in this TabPanel.
41863      * @return {Number}
41864      */
41865      getCount : function(){
41866          return this.items.length;
41867      },
41868
41869     /**
41870      * Resizes all the tabs to the passed width
41871      * @param {Number} The new width
41872      */
41873     setTabWidth : function(width){
41874         this.currentTabWidth = width;
41875         for(var i = 0, len = this.items.length; i < len; i++) {
41876                 if(!this.items[i].isHidden()) {
41877                 this.items[i].setWidth(width);
41878             }
41879         }
41880     },
41881
41882     /**
41883      * Destroys this TabPanel
41884      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41885      */
41886     destroy : function(removeEl){
41887         Roo.EventManager.removeResizeListener(this.onResize, this);
41888         for(var i = 0, len = this.items.length; i < len; i++){
41889             this.items[i].purgeListeners();
41890         }
41891         if(removeEl === true){
41892             this.el.update("");
41893             this.el.remove();
41894         }
41895     },
41896     
41897     createStrip : function(container)
41898     {
41899         var strip = document.createElement("nav");
41900         strip.className = Roo.bootstrap.version == 4 ?
41901             "navbar-light bg-light" : 
41902             "navbar navbar-default"; //"x-tabs-wrap";
41903         container.appendChild(strip);
41904         return strip;
41905     },
41906     
41907     createStripList : function(strip)
41908     {
41909         // div wrapper for retard IE
41910         // returns the "tr" element.
41911         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41912         //'<div class="x-tabs-strip-wrap">'+
41913           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41914           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41915         return strip.firstChild; //.firstChild.firstChild.firstChild;
41916     },
41917     createBody : function(container)
41918     {
41919         var body = document.createElement("div");
41920         Roo.id(body, "tab-body");
41921         //Roo.fly(body).addClass("x-tabs-body");
41922         Roo.fly(body).addClass("tab-content");
41923         container.appendChild(body);
41924         return body;
41925     },
41926     createItemBody :function(bodyEl, id){
41927         var body = Roo.getDom(id);
41928         if(!body){
41929             body = document.createElement("div");
41930             body.id = id;
41931         }
41932         //Roo.fly(body).addClass("x-tabs-item-body");
41933         Roo.fly(body).addClass("tab-pane");
41934          bodyEl.insertBefore(body, bodyEl.firstChild);
41935         return body;
41936     },
41937     /** @private */
41938     createStripElements :  function(stripEl, text, closable, tpl)
41939     {
41940         var td = document.createElement("li"); // was td..
41941         td.className = 'nav-item';
41942         
41943         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41944         
41945         
41946         stripEl.appendChild(td);
41947         /*if(closable){
41948             td.className = "x-tabs-closable";
41949             if(!this.closeTpl){
41950                 this.closeTpl = new Roo.Template(
41951                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41952                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41953                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41954                 );
41955             }
41956             var el = this.closeTpl.overwrite(td, {"text": text});
41957             var close = el.getElementsByTagName("div")[0];
41958             var inner = el.getElementsByTagName("em")[0];
41959             return {"el": el, "close": close, "inner": inner};
41960         } else {
41961         */
41962         // not sure what this is..
41963 //            if(!this.tabTpl){
41964                 //this.tabTpl = new Roo.Template(
41965                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41966                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41967                 //);
41968 //                this.tabTpl = new Roo.Template(
41969 //                   '<a href="#">' +
41970 //                   '<span unselectable="on"' +
41971 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41972 //                            ' >{text}</span></a>'
41973 //                );
41974 //                
41975 //            }
41976
41977
41978             var template = tpl || this.tabTpl || false;
41979             
41980             if(!template){
41981                 template =  new Roo.Template(
41982                         Roo.bootstrap.version == 4 ? 
41983                             (
41984                                 '<a class="nav-link" href="#" unselectable="on"' +
41985                                      (this.disableTooltips ? '' : ' title="{text}"') +
41986                                      ' >{text}</a>'
41987                             ) : (
41988                                 '<a class="nav-link" href="#">' +
41989                                 '<span unselectable="on"' +
41990                                          (this.disableTooltips ? '' : ' title="{text}"') +
41991                                     ' >{text}</span></a>'
41992                             )
41993                 );
41994             }
41995             
41996             switch (typeof(template)) {
41997                 case 'object' :
41998                     break;
41999                 case 'string' :
42000                     template = new Roo.Template(template);
42001                     break;
42002                 default :
42003                     break;
42004             }
42005             
42006             var el = template.overwrite(td, {"text": text});
42007             
42008             var inner = el.getElementsByTagName("span")[0];
42009             
42010             return {"el": el, "inner": inner};
42011             
42012     }
42013         
42014     
42015 });
42016
42017 /**
42018  * @class Roo.TabPanelItem
42019  * @extends Roo.util.Observable
42020  * Represents an individual item (tab plus body) in a TabPanel.
42021  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42022  * @param {String} id The id of this TabPanelItem
42023  * @param {String} text The text for the tab of this TabPanelItem
42024  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42025  */
42026 Roo.bootstrap.panel.TabItem = function(config){
42027     /**
42028      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42029      * @type Roo.TabPanel
42030      */
42031     this.tabPanel = config.panel;
42032     /**
42033      * The id for this TabPanelItem
42034      * @type String
42035      */
42036     this.id = config.id;
42037     /** @private */
42038     this.disabled = false;
42039     /** @private */
42040     this.text = config.text;
42041     /** @private */
42042     this.loaded = false;
42043     this.closable = config.closable;
42044
42045     /**
42046      * The body element for this TabPanelItem.
42047      * @type Roo.Element
42048      */
42049     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42050     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42051     this.bodyEl.setStyle("display", "block");
42052     this.bodyEl.setStyle("zoom", "1");
42053     //this.hideAction();
42054
42055     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42056     /** @private */
42057     this.el = Roo.get(els.el);
42058     this.inner = Roo.get(els.inner, true);
42059      this.textEl = Roo.bootstrap.version == 4 ?
42060         this.el : Roo.get(this.el.dom.firstChild, true);
42061
42062     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42063     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42064
42065     
42066 //    this.el.on("mousedown", this.onTabMouseDown, this);
42067     this.el.on("click", this.onTabClick, this);
42068     /** @private */
42069     if(config.closable){
42070         var c = Roo.get(els.close, true);
42071         c.dom.title = this.closeText;
42072         c.addClassOnOver("close-over");
42073         c.on("click", this.closeClick, this);
42074      }
42075
42076     this.addEvents({
42077          /**
42078          * @event activate
42079          * Fires when this tab becomes the active tab.
42080          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42081          * @param {Roo.TabPanelItem} this
42082          */
42083         "activate": true,
42084         /**
42085          * @event beforeclose
42086          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42087          * @param {Roo.TabPanelItem} this
42088          * @param {Object} e Set cancel to true on this object to cancel the close.
42089          */
42090         "beforeclose": true,
42091         /**
42092          * @event close
42093          * Fires when this tab is closed.
42094          * @param {Roo.TabPanelItem} this
42095          */
42096          "close": true,
42097         /**
42098          * @event deactivate
42099          * Fires when this tab is no longer the active tab.
42100          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42101          * @param {Roo.TabPanelItem} this
42102          */
42103          "deactivate" : true
42104     });
42105     this.hidden = false;
42106
42107     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42108 };
42109
42110 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42111            {
42112     purgeListeners : function(){
42113        Roo.util.Observable.prototype.purgeListeners.call(this);
42114        this.el.removeAllListeners();
42115     },
42116     /**
42117      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42118      */
42119     show : function(){
42120         this.status_node.addClass("active");
42121         this.showAction();
42122         if(Roo.isOpera){
42123             this.tabPanel.stripWrap.repaint();
42124         }
42125         this.fireEvent("activate", this.tabPanel, this);
42126     },
42127
42128     /**
42129      * Returns true if this tab is the active tab.
42130      * @return {Boolean}
42131      */
42132     isActive : function(){
42133         return this.tabPanel.getActiveTab() == this;
42134     },
42135
42136     /**
42137      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42138      */
42139     hide : function(){
42140         this.status_node.removeClass("active");
42141         this.hideAction();
42142         this.fireEvent("deactivate", this.tabPanel, this);
42143     },
42144
42145     hideAction : function(){
42146         this.bodyEl.hide();
42147         this.bodyEl.setStyle("position", "absolute");
42148         this.bodyEl.setLeft("-20000px");
42149         this.bodyEl.setTop("-20000px");
42150     },
42151
42152     showAction : function(){
42153         this.bodyEl.setStyle("position", "relative");
42154         this.bodyEl.setTop("");
42155         this.bodyEl.setLeft("");
42156         this.bodyEl.show();
42157     },
42158
42159     /**
42160      * Set the tooltip for the tab.
42161      * @param {String} tooltip The tab's tooltip
42162      */
42163     setTooltip : function(text){
42164         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42165             this.textEl.dom.qtip = text;
42166             this.textEl.dom.removeAttribute('title');
42167         }else{
42168             this.textEl.dom.title = text;
42169         }
42170     },
42171
42172     onTabClick : function(e){
42173         e.preventDefault();
42174         this.tabPanel.activate(this.id);
42175     },
42176
42177     onTabMouseDown : function(e){
42178         e.preventDefault();
42179         this.tabPanel.activate(this.id);
42180     },
42181 /*
42182     getWidth : function(){
42183         return this.inner.getWidth();
42184     },
42185
42186     setWidth : function(width){
42187         var iwidth = width - this.linode.getPadding("lr");
42188         this.inner.setWidth(iwidth);
42189         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42190         this.linode.setWidth(width);
42191     },
42192 */
42193     /**
42194      * Show or hide the tab
42195      * @param {Boolean} hidden True to hide or false to show.
42196      */
42197     setHidden : function(hidden){
42198         this.hidden = hidden;
42199         this.linode.setStyle("display", hidden ? "none" : "");
42200     },
42201
42202     /**
42203      * Returns true if this tab is "hidden"
42204      * @return {Boolean}
42205      */
42206     isHidden : function(){
42207         return this.hidden;
42208     },
42209
42210     /**
42211      * Returns the text for this tab
42212      * @return {String}
42213      */
42214     getText : function(){
42215         return this.text;
42216     },
42217     /*
42218     autoSize : function(){
42219         //this.el.beginMeasure();
42220         this.textEl.setWidth(1);
42221         /*
42222          *  #2804 [new] Tabs in Roojs
42223          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42224          */
42225         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42226         //this.el.endMeasure();
42227     //},
42228
42229     /**
42230      * Sets the text for the tab (Note: this also sets the tooltip text)
42231      * @param {String} text The tab's text and tooltip
42232      */
42233     setText : function(text){
42234         this.text = text;
42235         this.textEl.update(text);
42236         this.setTooltip(text);
42237         //if(!this.tabPanel.resizeTabs){
42238         //    this.autoSize();
42239         //}
42240     },
42241     /**
42242      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42243      */
42244     activate : function(){
42245         this.tabPanel.activate(this.id);
42246     },
42247
42248     /**
42249      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42250      */
42251     disable : function(){
42252         if(this.tabPanel.active != this){
42253             this.disabled = true;
42254             this.status_node.addClass("disabled");
42255         }
42256     },
42257
42258     /**
42259      * Enables this TabPanelItem if it was previously disabled.
42260      */
42261     enable : function(){
42262         this.disabled = false;
42263         this.status_node.removeClass("disabled");
42264     },
42265
42266     /**
42267      * Sets the content for this TabPanelItem.
42268      * @param {String} content The content
42269      * @param {Boolean} loadScripts true to look for and load scripts
42270      */
42271     setContent : function(content, loadScripts){
42272         this.bodyEl.update(content, loadScripts);
42273     },
42274
42275     /**
42276      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42277      * @return {Roo.UpdateManager} The UpdateManager
42278      */
42279     getUpdateManager : function(){
42280         return this.bodyEl.getUpdateManager();
42281     },
42282
42283     /**
42284      * Set a URL to be used to load the content for this TabPanelItem.
42285      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42286      * @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)
42287      * @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)
42288      * @return {Roo.UpdateManager} The UpdateManager
42289      */
42290     setUrl : function(url, params, loadOnce){
42291         if(this.refreshDelegate){
42292             this.un('activate', this.refreshDelegate);
42293         }
42294         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42295         this.on("activate", this.refreshDelegate);
42296         return this.bodyEl.getUpdateManager();
42297     },
42298
42299     /** @private */
42300     _handleRefresh : function(url, params, loadOnce){
42301         if(!loadOnce || !this.loaded){
42302             var updater = this.bodyEl.getUpdateManager();
42303             updater.update(url, params, this._setLoaded.createDelegate(this));
42304         }
42305     },
42306
42307     /**
42308      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42309      *   Will fail silently if the setUrl method has not been called.
42310      *   This does not activate the panel, just updates its content.
42311      */
42312     refresh : function(){
42313         if(this.refreshDelegate){
42314            this.loaded = false;
42315            this.refreshDelegate();
42316         }
42317     },
42318
42319     /** @private */
42320     _setLoaded : function(){
42321         this.loaded = true;
42322     },
42323
42324     /** @private */
42325     closeClick : function(e){
42326         var o = {};
42327         e.stopEvent();
42328         this.fireEvent("beforeclose", this, o);
42329         if(o.cancel !== true){
42330             this.tabPanel.removeTab(this.id);
42331         }
42332     },
42333     /**
42334      * The text displayed in the tooltip for the close icon.
42335      * @type String
42336      */
42337     closeText : "Close this tab"
42338 });
42339 /**
42340 *    This script refer to:
42341 *    Title: International Telephone Input
42342 *    Author: Jack O'Connor
42343 *    Code version:  v12.1.12
42344 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42345 **/
42346
42347 Roo.bootstrap.PhoneInputData = function() {
42348     var d = [
42349       [
42350         "Afghanistan (‫افغانستان‬‎)",
42351         "af",
42352         "93"
42353       ],
42354       [
42355         "Albania (Shqipëri)",
42356         "al",
42357         "355"
42358       ],
42359       [
42360         "Algeria (‫الجزائر‬‎)",
42361         "dz",
42362         "213"
42363       ],
42364       [
42365         "American Samoa",
42366         "as",
42367         "1684"
42368       ],
42369       [
42370         "Andorra",
42371         "ad",
42372         "376"
42373       ],
42374       [
42375         "Angola",
42376         "ao",
42377         "244"
42378       ],
42379       [
42380         "Anguilla",
42381         "ai",
42382         "1264"
42383       ],
42384       [
42385         "Antigua and Barbuda",
42386         "ag",
42387         "1268"
42388       ],
42389       [
42390         "Argentina",
42391         "ar",
42392         "54"
42393       ],
42394       [
42395         "Armenia (Հայաստան)",
42396         "am",
42397         "374"
42398       ],
42399       [
42400         "Aruba",
42401         "aw",
42402         "297"
42403       ],
42404       [
42405         "Australia",
42406         "au",
42407         "61",
42408         0
42409       ],
42410       [
42411         "Austria (Österreich)",
42412         "at",
42413         "43"
42414       ],
42415       [
42416         "Azerbaijan (Azərbaycan)",
42417         "az",
42418         "994"
42419       ],
42420       [
42421         "Bahamas",
42422         "bs",
42423         "1242"
42424       ],
42425       [
42426         "Bahrain (‫البحرين‬‎)",
42427         "bh",
42428         "973"
42429       ],
42430       [
42431         "Bangladesh (বাংলাদেশ)",
42432         "bd",
42433         "880"
42434       ],
42435       [
42436         "Barbados",
42437         "bb",
42438         "1246"
42439       ],
42440       [
42441         "Belarus (Беларусь)",
42442         "by",
42443         "375"
42444       ],
42445       [
42446         "Belgium (België)",
42447         "be",
42448         "32"
42449       ],
42450       [
42451         "Belize",
42452         "bz",
42453         "501"
42454       ],
42455       [
42456         "Benin (Bénin)",
42457         "bj",
42458         "229"
42459       ],
42460       [
42461         "Bermuda",
42462         "bm",
42463         "1441"
42464       ],
42465       [
42466         "Bhutan (འབྲུག)",
42467         "bt",
42468         "975"
42469       ],
42470       [
42471         "Bolivia",
42472         "bo",
42473         "591"
42474       ],
42475       [
42476         "Bosnia and Herzegovina (Босна и Херцеговина)",
42477         "ba",
42478         "387"
42479       ],
42480       [
42481         "Botswana",
42482         "bw",
42483         "267"
42484       ],
42485       [
42486         "Brazil (Brasil)",
42487         "br",
42488         "55"
42489       ],
42490       [
42491         "British Indian Ocean Territory",
42492         "io",
42493         "246"
42494       ],
42495       [
42496         "British Virgin Islands",
42497         "vg",
42498         "1284"
42499       ],
42500       [
42501         "Brunei",
42502         "bn",
42503         "673"
42504       ],
42505       [
42506         "Bulgaria (България)",
42507         "bg",
42508         "359"
42509       ],
42510       [
42511         "Burkina Faso",
42512         "bf",
42513         "226"
42514       ],
42515       [
42516         "Burundi (Uburundi)",
42517         "bi",
42518         "257"
42519       ],
42520       [
42521         "Cambodia (កម្ពុជា)",
42522         "kh",
42523         "855"
42524       ],
42525       [
42526         "Cameroon (Cameroun)",
42527         "cm",
42528         "237"
42529       ],
42530       [
42531         "Canada",
42532         "ca",
42533         "1",
42534         1,
42535         ["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"]
42536       ],
42537       [
42538         "Cape Verde (Kabu Verdi)",
42539         "cv",
42540         "238"
42541       ],
42542       [
42543         "Caribbean Netherlands",
42544         "bq",
42545         "599",
42546         1
42547       ],
42548       [
42549         "Cayman Islands",
42550         "ky",
42551         "1345"
42552       ],
42553       [
42554         "Central African Republic (République centrafricaine)",
42555         "cf",
42556         "236"
42557       ],
42558       [
42559         "Chad (Tchad)",
42560         "td",
42561         "235"
42562       ],
42563       [
42564         "Chile",
42565         "cl",
42566         "56"
42567       ],
42568       [
42569         "China (中国)",
42570         "cn",
42571         "86"
42572       ],
42573       [
42574         "Christmas Island",
42575         "cx",
42576         "61",
42577         2
42578       ],
42579       [
42580         "Cocos (Keeling) Islands",
42581         "cc",
42582         "61",
42583         1
42584       ],
42585       [
42586         "Colombia",
42587         "co",
42588         "57"
42589       ],
42590       [
42591         "Comoros (‫جزر القمر‬‎)",
42592         "km",
42593         "269"
42594       ],
42595       [
42596         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42597         "cd",
42598         "243"
42599       ],
42600       [
42601         "Congo (Republic) (Congo-Brazzaville)",
42602         "cg",
42603         "242"
42604       ],
42605       [
42606         "Cook Islands",
42607         "ck",
42608         "682"
42609       ],
42610       [
42611         "Costa Rica",
42612         "cr",
42613         "506"
42614       ],
42615       [
42616         "Côte d’Ivoire",
42617         "ci",
42618         "225"
42619       ],
42620       [
42621         "Croatia (Hrvatska)",
42622         "hr",
42623         "385"
42624       ],
42625       [
42626         "Cuba",
42627         "cu",
42628         "53"
42629       ],
42630       [
42631         "Curaçao",
42632         "cw",
42633         "599",
42634         0
42635       ],
42636       [
42637         "Cyprus (Κύπρος)",
42638         "cy",
42639         "357"
42640       ],
42641       [
42642         "Czech Republic (Česká republika)",
42643         "cz",
42644         "420"
42645       ],
42646       [
42647         "Denmark (Danmark)",
42648         "dk",
42649         "45"
42650       ],
42651       [
42652         "Djibouti",
42653         "dj",
42654         "253"
42655       ],
42656       [
42657         "Dominica",
42658         "dm",
42659         "1767"
42660       ],
42661       [
42662         "Dominican Republic (República Dominicana)",
42663         "do",
42664         "1",
42665         2,
42666         ["809", "829", "849"]
42667       ],
42668       [
42669         "Ecuador",
42670         "ec",
42671         "593"
42672       ],
42673       [
42674         "Egypt (‫مصر‬‎)",
42675         "eg",
42676         "20"
42677       ],
42678       [
42679         "El Salvador",
42680         "sv",
42681         "503"
42682       ],
42683       [
42684         "Equatorial Guinea (Guinea Ecuatorial)",
42685         "gq",
42686         "240"
42687       ],
42688       [
42689         "Eritrea",
42690         "er",
42691         "291"
42692       ],
42693       [
42694         "Estonia (Eesti)",
42695         "ee",
42696         "372"
42697       ],
42698       [
42699         "Ethiopia",
42700         "et",
42701         "251"
42702       ],
42703       [
42704         "Falkland Islands (Islas Malvinas)",
42705         "fk",
42706         "500"
42707       ],
42708       [
42709         "Faroe Islands (Føroyar)",
42710         "fo",
42711         "298"
42712       ],
42713       [
42714         "Fiji",
42715         "fj",
42716         "679"
42717       ],
42718       [
42719         "Finland (Suomi)",
42720         "fi",
42721         "358",
42722         0
42723       ],
42724       [
42725         "France",
42726         "fr",
42727         "33"
42728       ],
42729       [
42730         "French Guiana (Guyane française)",
42731         "gf",
42732         "594"
42733       ],
42734       [
42735         "French Polynesia (Polynésie française)",
42736         "pf",
42737         "689"
42738       ],
42739       [
42740         "Gabon",
42741         "ga",
42742         "241"
42743       ],
42744       [
42745         "Gambia",
42746         "gm",
42747         "220"
42748       ],
42749       [
42750         "Georgia (საქართველო)",
42751         "ge",
42752         "995"
42753       ],
42754       [
42755         "Germany (Deutschland)",
42756         "de",
42757         "49"
42758       ],
42759       [
42760         "Ghana (Gaana)",
42761         "gh",
42762         "233"
42763       ],
42764       [
42765         "Gibraltar",
42766         "gi",
42767         "350"
42768       ],
42769       [
42770         "Greece (Ελλάδα)",
42771         "gr",
42772         "30"
42773       ],
42774       [
42775         "Greenland (Kalaallit Nunaat)",
42776         "gl",
42777         "299"
42778       ],
42779       [
42780         "Grenada",
42781         "gd",
42782         "1473"
42783       ],
42784       [
42785         "Guadeloupe",
42786         "gp",
42787         "590",
42788         0
42789       ],
42790       [
42791         "Guam",
42792         "gu",
42793         "1671"
42794       ],
42795       [
42796         "Guatemala",
42797         "gt",
42798         "502"
42799       ],
42800       [
42801         "Guernsey",
42802         "gg",
42803         "44",
42804         1
42805       ],
42806       [
42807         "Guinea (Guinée)",
42808         "gn",
42809         "224"
42810       ],
42811       [
42812         "Guinea-Bissau (Guiné Bissau)",
42813         "gw",
42814         "245"
42815       ],
42816       [
42817         "Guyana",
42818         "gy",
42819         "592"
42820       ],
42821       [
42822         "Haiti",
42823         "ht",
42824         "509"
42825       ],
42826       [
42827         "Honduras",
42828         "hn",
42829         "504"
42830       ],
42831       [
42832         "Hong Kong (香港)",
42833         "hk",
42834         "852"
42835       ],
42836       [
42837         "Hungary (Magyarország)",
42838         "hu",
42839         "36"
42840       ],
42841       [
42842         "Iceland (Ísland)",
42843         "is",
42844         "354"
42845       ],
42846       [
42847         "India (भारत)",
42848         "in",
42849         "91"
42850       ],
42851       [
42852         "Indonesia",
42853         "id",
42854         "62"
42855       ],
42856       [
42857         "Iran (‫ایران‬‎)",
42858         "ir",
42859         "98"
42860       ],
42861       [
42862         "Iraq (‫العراق‬‎)",
42863         "iq",
42864         "964"
42865       ],
42866       [
42867         "Ireland",
42868         "ie",
42869         "353"
42870       ],
42871       [
42872         "Isle of Man",
42873         "im",
42874         "44",
42875         2
42876       ],
42877       [
42878         "Israel (‫ישראל‬‎)",
42879         "il",
42880         "972"
42881       ],
42882       [
42883         "Italy (Italia)",
42884         "it",
42885         "39",
42886         0
42887       ],
42888       [
42889         "Jamaica",
42890         "jm",
42891         "1876"
42892       ],
42893       [
42894         "Japan (日本)",
42895         "jp",
42896         "81"
42897       ],
42898       [
42899         "Jersey",
42900         "je",
42901         "44",
42902         3
42903       ],
42904       [
42905         "Jordan (‫الأردن‬‎)",
42906         "jo",
42907         "962"
42908       ],
42909       [
42910         "Kazakhstan (Казахстан)",
42911         "kz",
42912         "7",
42913         1
42914       ],
42915       [
42916         "Kenya",
42917         "ke",
42918         "254"
42919       ],
42920       [
42921         "Kiribati",
42922         "ki",
42923         "686"
42924       ],
42925       [
42926         "Kosovo",
42927         "xk",
42928         "383"
42929       ],
42930       [
42931         "Kuwait (‫الكويت‬‎)",
42932         "kw",
42933         "965"
42934       ],
42935       [
42936         "Kyrgyzstan (Кыргызстан)",
42937         "kg",
42938         "996"
42939       ],
42940       [
42941         "Laos (ລາວ)",
42942         "la",
42943         "856"
42944       ],
42945       [
42946         "Latvia (Latvija)",
42947         "lv",
42948         "371"
42949       ],
42950       [
42951         "Lebanon (‫لبنان‬‎)",
42952         "lb",
42953         "961"
42954       ],
42955       [
42956         "Lesotho",
42957         "ls",
42958         "266"
42959       ],
42960       [
42961         "Liberia",
42962         "lr",
42963         "231"
42964       ],
42965       [
42966         "Libya (‫ليبيا‬‎)",
42967         "ly",
42968         "218"
42969       ],
42970       [
42971         "Liechtenstein",
42972         "li",
42973         "423"
42974       ],
42975       [
42976         "Lithuania (Lietuva)",
42977         "lt",
42978         "370"
42979       ],
42980       [
42981         "Luxembourg",
42982         "lu",
42983         "352"
42984       ],
42985       [
42986         "Macau (澳門)",
42987         "mo",
42988         "853"
42989       ],
42990       [
42991         "Macedonia (FYROM) (Македонија)",
42992         "mk",
42993         "389"
42994       ],
42995       [
42996         "Madagascar (Madagasikara)",
42997         "mg",
42998         "261"
42999       ],
43000       [
43001         "Malawi",
43002         "mw",
43003         "265"
43004       ],
43005       [
43006         "Malaysia",
43007         "my",
43008         "60"
43009       ],
43010       [
43011         "Maldives",
43012         "mv",
43013         "960"
43014       ],
43015       [
43016         "Mali",
43017         "ml",
43018         "223"
43019       ],
43020       [
43021         "Malta",
43022         "mt",
43023         "356"
43024       ],
43025       [
43026         "Marshall Islands",
43027         "mh",
43028         "692"
43029       ],
43030       [
43031         "Martinique",
43032         "mq",
43033         "596"
43034       ],
43035       [
43036         "Mauritania (‫موريتانيا‬‎)",
43037         "mr",
43038         "222"
43039       ],
43040       [
43041         "Mauritius (Moris)",
43042         "mu",
43043         "230"
43044       ],
43045       [
43046         "Mayotte",
43047         "yt",
43048         "262",
43049         1
43050       ],
43051       [
43052         "Mexico (México)",
43053         "mx",
43054         "52"
43055       ],
43056       [
43057         "Micronesia",
43058         "fm",
43059         "691"
43060       ],
43061       [
43062         "Moldova (Republica Moldova)",
43063         "md",
43064         "373"
43065       ],
43066       [
43067         "Monaco",
43068         "mc",
43069         "377"
43070       ],
43071       [
43072         "Mongolia (Монгол)",
43073         "mn",
43074         "976"
43075       ],
43076       [
43077         "Montenegro (Crna Gora)",
43078         "me",
43079         "382"
43080       ],
43081       [
43082         "Montserrat",
43083         "ms",
43084         "1664"
43085       ],
43086       [
43087         "Morocco (‫المغرب‬‎)",
43088         "ma",
43089         "212",
43090         0
43091       ],
43092       [
43093         "Mozambique (Moçambique)",
43094         "mz",
43095         "258"
43096       ],
43097       [
43098         "Myanmar (Burma) (မြန်မာ)",
43099         "mm",
43100         "95"
43101       ],
43102       [
43103         "Namibia (Namibië)",
43104         "na",
43105         "264"
43106       ],
43107       [
43108         "Nauru",
43109         "nr",
43110         "674"
43111       ],
43112       [
43113         "Nepal (नेपाल)",
43114         "np",
43115         "977"
43116       ],
43117       [
43118         "Netherlands (Nederland)",
43119         "nl",
43120         "31"
43121       ],
43122       [
43123         "New Caledonia (Nouvelle-Calédonie)",
43124         "nc",
43125         "687"
43126       ],
43127       [
43128         "New Zealand",
43129         "nz",
43130         "64"
43131       ],
43132       [
43133         "Nicaragua",
43134         "ni",
43135         "505"
43136       ],
43137       [
43138         "Niger (Nijar)",
43139         "ne",
43140         "227"
43141       ],
43142       [
43143         "Nigeria",
43144         "ng",
43145         "234"
43146       ],
43147       [
43148         "Niue",
43149         "nu",
43150         "683"
43151       ],
43152       [
43153         "Norfolk Island",
43154         "nf",
43155         "672"
43156       ],
43157       [
43158         "North Korea (조선 민주주의 인민 공화국)",
43159         "kp",
43160         "850"
43161       ],
43162       [
43163         "Northern Mariana Islands",
43164         "mp",
43165         "1670"
43166       ],
43167       [
43168         "Norway (Norge)",
43169         "no",
43170         "47",
43171         0
43172       ],
43173       [
43174         "Oman (‫عُمان‬‎)",
43175         "om",
43176         "968"
43177       ],
43178       [
43179         "Pakistan (‫پاکستان‬‎)",
43180         "pk",
43181         "92"
43182       ],
43183       [
43184         "Palau",
43185         "pw",
43186         "680"
43187       ],
43188       [
43189         "Palestine (‫فلسطين‬‎)",
43190         "ps",
43191         "970"
43192       ],
43193       [
43194         "Panama (Panamá)",
43195         "pa",
43196         "507"
43197       ],
43198       [
43199         "Papua New Guinea",
43200         "pg",
43201         "675"
43202       ],
43203       [
43204         "Paraguay",
43205         "py",
43206         "595"
43207       ],
43208       [
43209         "Peru (Perú)",
43210         "pe",
43211         "51"
43212       ],
43213       [
43214         "Philippines",
43215         "ph",
43216         "63"
43217       ],
43218       [
43219         "Poland (Polska)",
43220         "pl",
43221         "48"
43222       ],
43223       [
43224         "Portugal",
43225         "pt",
43226         "351"
43227       ],
43228       [
43229         "Puerto Rico",
43230         "pr",
43231         "1",
43232         3,
43233         ["787", "939"]
43234       ],
43235       [
43236         "Qatar (‫قطر‬‎)",
43237         "qa",
43238         "974"
43239       ],
43240       [
43241         "Réunion (La Réunion)",
43242         "re",
43243         "262",
43244         0
43245       ],
43246       [
43247         "Romania (România)",
43248         "ro",
43249         "40"
43250       ],
43251       [
43252         "Russia (Россия)",
43253         "ru",
43254         "7",
43255         0
43256       ],
43257       [
43258         "Rwanda",
43259         "rw",
43260         "250"
43261       ],
43262       [
43263         "Saint Barthélemy",
43264         "bl",
43265         "590",
43266         1
43267       ],
43268       [
43269         "Saint Helena",
43270         "sh",
43271         "290"
43272       ],
43273       [
43274         "Saint Kitts and Nevis",
43275         "kn",
43276         "1869"
43277       ],
43278       [
43279         "Saint Lucia",
43280         "lc",
43281         "1758"
43282       ],
43283       [
43284         "Saint Martin (Saint-Martin (partie française))",
43285         "mf",
43286         "590",
43287         2
43288       ],
43289       [
43290         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43291         "pm",
43292         "508"
43293       ],
43294       [
43295         "Saint Vincent and the Grenadines",
43296         "vc",
43297         "1784"
43298       ],
43299       [
43300         "Samoa",
43301         "ws",
43302         "685"
43303       ],
43304       [
43305         "San Marino",
43306         "sm",
43307         "378"
43308       ],
43309       [
43310         "São Tomé and Príncipe (São Tomé e Príncipe)",
43311         "st",
43312         "239"
43313       ],
43314       [
43315         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43316         "sa",
43317         "966"
43318       ],
43319       [
43320         "Senegal (Sénégal)",
43321         "sn",
43322         "221"
43323       ],
43324       [
43325         "Serbia (Србија)",
43326         "rs",
43327         "381"
43328       ],
43329       [
43330         "Seychelles",
43331         "sc",
43332         "248"
43333       ],
43334       [
43335         "Sierra Leone",
43336         "sl",
43337         "232"
43338       ],
43339       [
43340         "Singapore",
43341         "sg",
43342         "65"
43343       ],
43344       [
43345         "Sint Maarten",
43346         "sx",
43347         "1721"
43348       ],
43349       [
43350         "Slovakia (Slovensko)",
43351         "sk",
43352         "421"
43353       ],
43354       [
43355         "Slovenia (Slovenija)",
43356         "si",
43357         "386"
43358       ],
43359       [
43360         "Solomon Islands",
43361         "sb",
43362         "677"
43363       ],
43364       [
43365         "Somalia (Soomaaliya)",
43366         "so",
43367         "252"
43368       ],
43369       [
43370         "South Africa",
43371         "za",
43372         "27"
43373       ],
43374       [
43375         "South Korea (대한민국)",
43376         "kr",
43377         "82"
43378       ],
43379       [
43380         "South Sudan (‫جنوب السودان‬‎)",
43381         "ss",
43382         "211"
43383       ],
43384       [
43385         "Spain (España)",
43386         "es",
43387         "34"
43388       ],
43389       [
43390         "Sri Lanka (ශ්‍රී ලංකාව)",
43391         "lk",
43392         "94"
43393       ],
43394       [
43395         "Sudan (‫السودان‬‎)",
43396         "sd",
43397         "249"
43398       ],
43399       [
43400         "Suriname",
43401         "sr",
43402         "597"
43403       ],
43404       [
43405         "Svalbard and Jan Mayen",
43406         "sj",
43407         "47",
43408         1
43409       ],
43410       [
43411         "Swaziland",
43412         "sz",
43413         "268"
43414       ],
43415       [
43416         "Sweden (Sverige)",
43417         "se",
43418         "46"
43419       ],
43420       [
43421         "Switzerland (Schweiz)",
43422         "ch",
43423         "41"
43424       ],
43425       [
43426         "Syria (‫سوريا‬‎)",
43427         "sy",
43428         "963"
43429       ],
43430       [
43431         "Taiwan (台灣)",
43432         "tw",
43433         "886"
43434       ],
43435       [
43436         "Tajikistan",
43437         "tj",
43438         "992"
43439       ],
43440       [
43441         "Tanzania",
43442         "tz",
43443         "255"
43444       ],
43445       [
43446         "Thailand (ไทย)",
43447         "th",
43448         "66"
43449       ],
43450       [
43451         "Timor-Leste",
43452         "tl",
43453         "670"
43454       ],
43455       [
43456         "Togo",
43457         "tg",
43458         "228"
43459       ],
43460       [
43461         "Tokelau",
43462         "tk",
43463         "690"
43464       ],
43465       [
43466         "Tonga",
43467         "to",
43468         "676"
43469       ],
43470       [
43471         "Trinidad and Tobago",
43472         "tt",
43473         "1868"
43474       ],
43475       [
43476         "Tunisia (‫تونس‬‎)",
43477         "tn",
43478         "216"
43479       ],
43480       [
43481         "Turkey (Türkiye)",
43482         "tr",
43483         "90"
43484       ],
43485       [
43486         "Turkmenistan",
43487         "tm",
43488         "993"
43489       ],
43490       [
43491         "Turks and Caicos Islands",
43492         "tc",
43493         "1649"
43494       ],
43495       [
43496         "Tuvalu",
43497         "tv",
43498         "688"
43499       ],
43500       [
43501         "U.S. Virgin Islands",
43502         "vi",
43503         "1340"
43504       ],
43505       [
43506         "Uganda",
43507         "ug",
43508         "256"
43509       ],
43510       [
43511         "Ukraine (Україна)",
43512         "ua",
43513         "380"
43514       ],
43515       [
43516         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43517         "ae",
43518         "971"
43519       ],
43520       [
43521         "United Kingdom",
43522         "gb",
43523         "44",
43524         0
43525       ],
43526       [
43527         "United States",
43528         "us",
43529         "1",
43530         0
43531       ],
43532       [
43533         "Uruguay",
43534         "uy",
43535         "598"
43536       ],
43537       [
43538         "Uzbekistan (Oʻzbekiston)",
43539         "uz",
43540         "998"
43541       ],
43542       [
43543         "Vanuatu",
43544         "vu",
43545         "678"
43546       ],
43547       [
43548         "Vatican City (Città del Vaticano)",
43549         "va",
43550         "39",
43551         1
43552       ],
43553       [
43554         "Venezuela",
43555         "ve",
43556         "58"
43557       ],
43558       [
43559         "Vietnam (Việt Nam)",
43560         "vn",
43561         "84"
43562       ],
43563       [
43564         "Wallis and Futuna (Wallis-et-Futuna)",
43565         "wf",
43566         "681"
43567       ],
43568       [
43569         "Western Sahara (‫الصحراء الغربية‬‎)",
43570         "eh",
43571         "212",
43572         1
43573       ],
43574       [
43575         "Yemen (‫اليمن‬‎)",
43576         "ye",
43577         "967"
43578       ],
43579       [
43580         "Zambia",
43581         "zm",
43582         "260"
43583       ],
43584       [
43585         "Zimbabwe",
43586         "zw",
43587         "263"
43588       ],
43589       [
43590         "Åland Islands",
43591         "ax",
43592         "358",
43593         1
43594       ]
43595   ];
43596   
43597   return d;
43598 }/**
43599 *    This script refer to:
43600 *    Title: International Telephone Input
43601 *    Author: Jack O'Connor
43602 *    Code version:  v12.1.12
43603 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43604 **/
43605
43606 /**
43607  * @class Roo.bootstrap.PhoneInput
43608  * @extends Roo.bootstrap.TriggerField
43609  * An input with International dial-code selection
43610  
43611  * @cfg {String} defaultDialCode default '+852'
43612  * @cfg {Array} preferedCountries default []
43613   
43614  * @constructor
43615  * Create a new PhoneInput.
43616  * @param {Object} config Configuration options
43617  */
43618
43619 Roo.bootstrap.PhoneInput = function(config) {
43620     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43621 };
43622
43623 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43624         
43625         listWidth: undefined,
43626         
43627         selectedClass: 'active',
43628         
43629         invalidClass : "has-warning",
43630         
43631         validClass: 'has-success',
43632         
43633         allowed: '0123456789',
43634         
43635         max_length: 15,
43636         
43637         /**
43638          * @cfg {String} defaultDialCode The default dial code when initializing the input
43639          */
43640         defaultDialCode: '+852',
43641         
43642         /**
43643          * @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
43644          */
43645         preferedCountries: false,
43646         
43647         getAutoCreate : function()
43648         {
43649             var data = Roo.bootstrap.PhoneInputData();
43650             var align = this.labelAlign || this.parentLabelAlign();
43651             var id = Roo.id();
43652             
43653             this.allCountries = [];
43654             this.dialCodeMapping = [];
43655             
43656             for (var i = 0; i < data.length; i++) {
43657               var c = data[i];
43658               this.allCountries[i] = {
43659                 name: c[0],
43660                 iso2: c[1],
43661                 dialCode: c[2],
43662                 priority: c[3] || 0,
43663                 areaCodes: c[4] || null
43664               };
43665               this.dialCodeMapping[c[2]] = {
43666                   name: c[0],
43667                   iso2: c[1],
43668                   priority: c[3] || 0,
43669                   areaCodes: c[4] || null
43670               };
43671             }
43672             
43673             var cfg = {
43674                 cls: 'form-group',
43675                 cn: []
43676             };
43677             
43678             var input =  {
43679                 tag: 'input',
43680                 id : id,
43681                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43682                 maxlength: this.max_length,
43683                 cls : 'form-control tel-input',
43684                 autocomplete: 'new-password'
43685             };
43686             
43687             var hiddenInput = {
43688                 tag: 'input',
43689                 type: 'hidden',
43690                 cls: 'hidden-tel-input'
43691             };
43692             
43693             if (this.name) {
43694                 hiddenInput.name = this.name;
43695             }
43696             
43697             if (this.disabled) {
43698                 input.disabled = true;
43699             }
43700             
43701             var flag_container = {
43702                 tag: 'div',
43703                 cls: 'flag-box',
43704                 cn: [
43705                     {
43706                         tag: 'div',
43707                         cls: 'flag'
43708                     },
43709                     {
43710                         tag: 'div',
43711                         cls: 'caret'
43712                     }
43713                 ]
43714             };
43715             
43716             var box = {
43717                 tag: 'div',
43718                 cls: this.hasFeedback ? 'has-feedback' : '',
43719                 cn: [
43720                     hiddenInput,
43721                     input,
43722                     {
43723                         tag: 'input',
43724                         cls: 'dial-code-holder',
43725                         disabled: true
43726                     }
43727                 ]
43728             };
43729             
43730             var container = {
43731                 cls: 'roo-select2-container input-group',
43732                 cn: [
43733                     flag_container,
43734                     box
43735                 ]
43736             };
43737             
43738             if (this.fieldLabel.length) {
43739                 var indicator = {
43740                     tag: 'i',
43741                     tooltip: 'This field is required'
43742                 };
43743                 
43744                 var label = {
43745                     tag: 'label',
43746                     'for':  id,
43747                     cls: 'control-label',
43748                     cn: []
43749                 };
43750                 
43751                 var label_text = {
43752                     tag: 'span',
43753                     html: this.fieldLabel
43754                 };
43755                 
43756                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43757                 label.cn = [
43758                     indicator,
43759                     label_text
43760                 ];
43761                 
43762                 if(this.indicatorpos == 'right') {
43763                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43764                     label.cn = [
43765                         label_text,
43766                         indicator
43767                     ];
43768                 }
43769                 
43770                 if(align == 'left') {
43771                     container = {
43772                         tag: 'div',
43773                         cn: [
43774                             container
43775                         ]
43776                     };
43777                     
43778                     if(this.labelWidth > 12){
43779                         label.style = "width: " + this.labelWidth + 'px';
43780                     }
43781                     if(this.labelWidth < 13 && this.labelmd == 0){
43782                         this.labelmd = this.labelWidth;
43783                     }
43784                     if(this.labellg > 0){
43785                         label.cls += ' col-lg-' + this.labellg;
43786                         input.cls += ' col-lg-' + (12 - this.labellg);
43787                     }
43788                     if(this.labelmd > 0){
43789                         label.cls += ' col-md-' + this.labelmd;
43790                         container.cls += ' col-md-' + (12 - this.labelmd);
43791                     }
43792                     if(this.labelsm > 0){
43793                         label.cls += ' col-sm-' + this.labelsm;
43794                         container.cls += ' col-sm-' + (12 - this.labelsm);
43795                     }
43796                     if(this.labelxs > 0){
43797                         label.cls += ' col-xs-' + this.labelxs;
43798                         container.cls += ' col-xs-' + (12 - this.labelxs);
43799                     }
43800                 }
43801             }
43802             
43803             cfg.cn = [
43804                 label,
43805                 container
43806             ];
43807             
43808             var settings = this;
43809             
43810             ['xs','sm','md','lg'].map(function(size){
43811                 if (settings[size]) {
43812                     cfg.cls += ' col-' + size + '-' + settings[size];
43813                 }
43814             });
43815             
43816             this.store = new Roo.data.Store({
43817                 proxy : new Roo.data.MemoryProxy({}),
43818                 reader : new Roo.data.JsonReader({
43819                     fields : [
43820                         {
43821                             'name' : 'name',
43822                             'type' : 'string'
43823                         },
43824                         {
43825                             'name' : 'iso2',
43826                             'type' : 'string'
43827                         },
43828                         {
43829                             'name' : 'dialCode',
43830                             'type' : 'string'
43831                         },
43832                         {
43833                             'name' : 'priority',
43834                             'type' : 'string'
43835                         },
43836                         {
43837                             'name' : 'areaCodes',
43838                             'type' : 'string'
43839                         }
43840                     ]
43841                 })
43842             });
43843             
43844             if(!this.preferedCountries) {
43845                 this.preferedCountries = [
43846                     'hk',
43847                     'gb',
43848                     'us'
43849                 ];
43850             }
43851             
43852             var p = this.preferedCountries.reverse();
43853             
43854             if(p) {
43855                 for (var i = 0; i < p.length; i++) {
43856                     for (var j = 0; j < this.allCountries.length; j++) {
43857                         if(this.allCountries[j].iso2 == p[i]) {
43858                             var t = this.allCountries[j];
43859                             this.allCountries.splice(j,1);
43860                             this.allCountries.unshift(t);
43861                         }
43862                     } 
43863                 }
43864             }
43865             
43866             this.store.proxy.data = {
43867                 success: true,
43868                 data: this.allCountries
43869             };
43870             
43871             return cfg;
43872         },
43873         
43874         initEvents : function()
43875         {
43876             this.createList();
43877             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43878             
43879             this.indicator = this.indicatorEl();
43880             this.flag = this.flagEl();
43881             this.dialCodeHolder = this.dialCodeHolderEl();
43882             
43883             this.trigger = this.el.select('div.flag-box',true).first();
43884             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43885             
43886             var _this = this;
43887             
43888             (function(){
43889                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43890                 _this.list.setWidth(lw);
43891             }).defer(100);
43892             
43893             this.list.on('mouseover', this.onViewOver, this);
43894             this.list.on('mousemove', this.onViewMove, this);
43895             this.inputEl().on("keyup", this.onKeyUp, this);
43896             this.inputEl().on("keypress", this.onKeyPress, this);
43897             
43898             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43899
43900             this.view = new Roo.View(this.list, this.tpl, {
43901                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43902             });
43903             
43904             this.view.on('click', this.onViewClick, this);
43905             this.setValue(this.defaultDialCode);
43906         },
43907         
43908         onTriggerClick : function(e)
43909         {
43910             Roo.log('trigger click');
43911             if(this.disabled){
43912                 return;
43913             }
43914             
43915             if(this.isExpanded()){
43916                 this.collapse();
43917                 this.hasFocus = false;
43918             }else {
43919                 this.store.load({});
43920                 this.hasFocus = true;
43921                 this.expand();
43922             }
43923         },
43924         
43925         isExpanded : function()
43926         {
43927             return this.list.isVisible();
43928         },
43929         
43930         collapse : function()
43931         {
43932             if(!this.isExpanded()){
43933                 return;
43934             }
43935             this.list.hide();
43936             Roo.get(document).un('mousedown', this.collapseIf, this);
43937             Roo.get(document).un('mousewheel', this.collapseIf, this);
43938             this.fireEvent('collapse', this);
43939             this.validate();
43940         },
43941         
43942         expand : function()
43943         {
43944             Roo.log('expand');
43945
43946             if(this.isExpanded() || !this.hasFocus){
43947                 return;
43948             }
43949             
43950             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43951             this.list.setWidth(lw);
43952             
43953             this.list.show();
43954             this.restrictHeight();
43955             
43956             Roo.get(document).on('mousedown', this.collapseIf, this);
43957             Roo.get(document).on('mousewheel', this.collapseIf, this);
43958             
43959             this.fireEvent('expand', this);
43960         },
43961         
43962         restrictHeight : function()
43963         {
43964             this.list.alignTo(this.inputEl(), this.listAlign);
43965             this.list.alignTo(this.inputEl(), this.listAlign);
43966         },
43967         
43968         onViewOver : function(e, t)
43969         {
43970             if(this.inKeyMode){
43971                 return;
43972             }
43973             var item = this.view.findItemFromChild(t);
43974             
43975             if(item){
43976                 var index = this.view.indexOf(item);
43977                 this.select(index, false);
43978             }
43979         },
43980
43981         // private
43982         onViewClick : function(view, doFocus, el, e)
43983         {
43984             var index = this.view.getSelectedIndexes()[0];
43985             
43986             var r = this.store.getAt(index);
43987             
43988             if(r){
43989                 this.onSelect(r, index);
43990             }
43991             if(doFocus !== false && !this.blockFocus){
43992                 this.inputEl().focus();
43993             }
43994         },
43995         
43996         onViewMove : function(e, t)
43997         {
43998             this.inKeyMode = false;
43999         },
44000         
44001         select : function(index, scrollIntoView)
44002         {
44003             this.selectedIndex = index;
44004             this.view.select(index);
44005             if(scrollIntoView !== false){
44006                 var el = this.view.getNode(index);
44007                 if(el){
44008                     this.list.scrollChildIntoView(el, false);
44009                 }
44010             }
44011         },
44012         
44013         createList : function()
44014         {
44015             this.list = Roo.get(document.body).createChild({
44016                 tag: 'ul',
44017                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44018                 style: 'display:none'
44019             });
44020             
44021             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44022         },
44023         
44024         collapseIf : function(e)
44025         {
44026             var in_combo  = e.within(this.el);
44027             var in_list =  e.within(this.list);
44028             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44029             
44030             if (in_combo || in_list || is_list) {
44031                 return;
44032             }
44033             this.collapse();
44034         },
44035         
44036         onSelect : function(record, index)
44037         {
44038             if(this.fireEvent('beforeselect', this, record, index) !== false){
44039                 
44040                 this.setFlagClass(record.data.iso2);
44041                 this.setDialCode(record.data.dialCode);
44042                 this.hasFocus = false;
44043                 this.collapse();
44044                 this.fireEvent('select', this, record, index);
44045             }
44046         },
44047         
44048         flagEl : function()
44049         {
44050             var flag = this.el.select('div.flag',true).first();
44051             if(!flag){
44052                 return false;
44053             }
44054             return flag;
44055         },
44056         
44057         dialCodeHolderEl : function()
44058         {
44059             var d = this.el.select('input.dial-code-holder',true).first();
44060             if(!d){
44061                 return false;
44062             }
44063             return d;
44064         },
44065         
44066         setDialCode : function(v)
44067         {
44068             this.dialCodeHolder.dom.value = '+'+v;
44069         },
44070         
44071         setFlagClass : function(n)
44072         {
44073             this.flag.dom.className = 'flag '+n;
44074         },
44075         
44076         getValue : function()
44077         {
44078             var v = this.inputEl().getValue();
44079             if(this.dialCodeHolder) {
44080                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44081             }
44082             return v;
44083         },
44084         
44085         setValue : function(v)
44086         {
44087             var d = this.getDialCode(v);
44088             
44089             //invalid dial code
44090             if(v.length == 0 || !d || d.length == 0) {
44091                 if(this.rendered){
44092                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44093                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44094                 }
44095                 return;
44096             }
44097             
44098             //valid dial code
44099             this.setFlagClass(this.dialCodeMapping[d].iso2);
44100             this.setDialCode(d);
44101             this.inputEl().dom.value = v.replace('+'+d,'');
44102             this.hiddenEl().dom.value = this.getValue();
44103             
44104             this.validate();
44105         },
44106         
44107         getDialCode : function(v)
44108         {
44109             v = v ||  '';
44110             
44111             if (v.length == 0) {
44112                 return this.dialCodeHolder.dom.value;
44113             }
44114             
44115             var dialCode = "";
44116             if (v.charAt(0) != "+") {
44117                 return false;
44118             }
44119             var numericChars = "";
44120             for (var i = 1; i < v.length; i++) {
44121               var c = v.charAt(i);
44122               if (!isNaN(c)) {
44123                 numericChars += c;
44124                 if (this.dialCodeMapping[numericChars]) {
44125                   dialCode = v.substr(1, i);
44126                 }
44127                 if (numericChars.length == 4) {
44128                   break;
44129                 }
44130               }
44131             }
44132             return dialCode;
44133         },
44134         
44135         reset : function()
44136         {
44137             this.setValue(this.defaultDialCode);
44138             this.validate();
44139         },
44140         
44141         hiddenEl : function()
44142         {
44143             return this.el.select('input.hidden-tel-input',true).first();
44144         },
44145         
44146         // after setting val
44147         onKeyUp : function(e){
44148             this.setValue(this.getValue());
44149         },
44150         
44151         onKeyPress : function(e){
44152             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44153                 e.stopEvent();
44154             }
44155         }
44156         
44157 });
44158 /**
44159  * @class Roo.bootstrap.MoneyField
44160  * @extends Roo.bootstrap.ComboBox
44161  * Bootstrap MoneyField class
44162  * 
44163  * @constructor
44164  * Create a new MoneyField.
44165  * @param {Object} config Configuration options
44166  */
44167
44168 Roo.bootstrap.MoneyField = function(config) {
44169     
44170     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44171     
44172 };
44173
44174 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44175     
44176     /**
44177      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44178      */
44179     allowDecimals : true,
44180     /**
44181      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44182      */
44183     decimalSeparator : ".",
44184     /**
44185      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44186      */
44187     decimalPrecision : 0,
44188     /**
44189      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44190      */
44191     allowNegative : true,
44192     /**
44193      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44194      */
44195     allowZero: true,
44196     /**
44197      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44198      */
44199     minValue : Number.NEGATIVE_INFINITY,
44200     /**
44201      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44202      */
44203     maxValue : Number.MAX_VALUE,
44204     /**
44205      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44206      */
44207     minText : "The minimum value for this field is {0}",
44208     /**
44209      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44210      */
44211     maxText : "The maximum value for this field is {0}",
44212     /**
44213      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44214      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44215      */
44216     nanText : "{0} is not a valid number",
44217     /**
44218      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44219      */
44220     castInt : true,
44221     /**
44222      * @cfg {String} defaults currency of the MoneyField
44223      * value should be in lkey
44224      */
44225     defaultCurrency : false,
44226     /**
44227      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44228      */
44229     thousandsDelimiter : false,
44230     /**
44231      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44232      */
44233     max_length: false,
44234     
44235     inputlg : 9,
44236     inputmd : 9,
44237     inputsm : 9,
44238     inputxs : 6,
44239     
44240     store : false,
44241     
44242     getAutoCreate : function()
44243     {
44244         var align = this.labelAlign || this.parentLabelAlign();
44245         
44246         var id = Roo.id();
44247
44248         var cfg = {
44249             cls: 'form-group',
44250             cn: []
44251         };
44252
44253         var input =  {
44254             tag: 'input',
44255             id : id,
44256             cls : 'form-control roo-money-amount-input',
44257             autocomplete: 'new-password'
44258         };
44259         
44260         var hiddenInput = {
44261             tag: 'input',
44262             type: 'hidden',
44263             id: Roo.id(),
44264             cls: 'hidden-number-input'
44265         };
44266         
44267         if(this.max_length) {
44268             input.maxlength = this.max_length; 
44269         }
44270         
44271         if (this.name) {
44272             hiddenInput.name = this.name;
44273         }
44274
44275         if (this.disabled) {
44276             input.disabled = true;
44277         }
44278
44279         var clg = 12 - this.inputlg;
44280         var cmd = 12 - this.inputmd;
44281         var csm = 12 - this.inputsm;
44282         var cxs = 12 - this.inputxs;
44283         
44284         var container = {
44285             tag : 'div',
44286             cls : 'row roo-money-field',
44287             cn : [
44288                 {
44289                     tag : 'div',
44290                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44291                     cn : [
44292                         {
44293                             tag : 'div',
44294                             cls: 'roo-select2-container input-group',
44295                             cn: [
44296                                 {
44297                                     tag : 'input',
44298                                     cls : 'form-control roo-money-currency-input',
44299                                     autocomplete: 'new-password',
44300                                     readOnly : 1,
44301                                     name : this.currencyName
44302                                 },
44303                                 {
44304                                     tag :'span',
44305                                     cls : 'input-group-addon',
44306                                     cn : [
44307                                         {
44308                                             tag: 'span',
44309                                             cls: 'caret'
44310                                         }
44311                                     ]
44312                                 }
44313                             ]
44314                         }
44315                     ]
44316                 },
44317                 {
44318                     tag : 'div',
44319                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44320                     cn : [
44321                         {
44322                             tag: 'div',
44323                             cls: this.hasFeedback ? 'has-feedback' : '',
44324                             cn: [
44325                                 input
44326                             ]
44327                         }
44328                     ]
44329                 }
44330             ]
44331             
44332         };
44333         
44334         if (this.fieldLabel.length) {
44335             var indicator = {
44336                 tag: 'i',
44337                 tooltip: 'This field is required'
44338             };
44339
44340             var label = {
44341                 tag: 'label',
44342                 'for':  id,
44343                 cls: 'control-label',
44344                 cn: []
44345             };
44346
44347             var label_text = {
44348                 tag: 'span',
44349                 html: this.fieldLabel
44350             };
44351
44352             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44353             label.cn = [
44354                 indicator,
44355                 label_text
44356             ];
44357
44358             if(this.indicatorpos == 'right') {
44359                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44360                 label.cn = [
44361                     label_text,
44362                     indicator
44363                 ];
44364             }
44365
44366             if(align == 'left') {
44367                 container = {
44368                     tag: 'div',
44369                     cn: [
44370                         container
44371                     ]
44372                 };
44373
44374                 if(this.labelWidth > 12){
44375                     label.style = "width: " + this.labelWidth + 'px';
44376                 }
44377                 if(this.labelWidth < 13 && this.labelmd == 0){
44378                     this.labelmd = this.labelWidth;
44379                 }
44380                 if(this.labellg > 0){
44381                     label.cls += ' col-lg-' + this.labellg;
44382                     input.cls += ' col-lg-' + (12 - this.labellg);
44383                 }
44384                 if(this.labelmd > 0){
44385                     label.cls += ' col-md-' + this.labelmd;
44386                     container.cls += ' col-md-' + (12 - this.labelmd);
44387                 }
44388                 if(this.labelsm > 0){
44389                     label.cls += ' col-sm-' + this.labelsm;
44390                     container.cls += ' col-sm-' + (12 - this.labelsm);
44391                 }
44392                 if(this.labelxs > 0){
44393                     label.cls += ' col-xs-' + this.labelxs;
44394                     container.cls += ' col-xs-' + (12 - this.labelxs);
44395                 }
44396             }
44397         }
44398
44399         cfg.cn = [
44400             label,
44401             container,
44402             hiddenInput
44403         ];
44404         
44405         var settings = this;
44406
44407         ['xs','sm','md','lg'].map(function(size){
44408             if (settings[size]) {
44409                 cfg.cls += ' col-' + size + '-' + settings[size];
44410             }
44411         });
44412         
44413         return cfg;
44414     },
44415     
44416     initEvents : function()
44417     {
44418         this.indicator = this.indicatorEl();
44419         
44420         this.initCurrencyEvent();
44421         
44422         this.initNumberEvent();
44423     },
44424     
44425     initCurrencyEvent : function()
44426     {
44427         if (!this.store) {
44428             throw "can not find store for combo";
44429         }
44430         
44431         this.store = Roo.factory(this.store, Roo.data);
44432         this.store.parent = this;
44433         
44434         this.createList();
44435         
44436         this.triggerEl = this.el.select('.input-group-addon', true).first();
44437         
44438         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44439         
44440         var _this = this;
44441         
44442         (function(){
44443             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44444             _this.list.setWidth(lw);
44445         }).defer(100);
44446         
44447         this.list.on('mouseover', this.onViewOver, this);
44448         this.list.on('mousemove', this.onViewMove, this);
44449         this.list.on('scroll', this.onViewScroll, this);
44450         
44451         if(!this.tpl){
44452             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44453         }
44454         
44455         this.view = new Roo.View(this.list, this.tpl, {
44456             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44457         });
44458         
44459         this.view.on('click', this.onViewClick, this);
44460         
44461         this.store.on('beforeload', this.onBeforeLoad, this);
44462         this.store.on('load', this.onLoad, this);
44463         this.store.on('loadexception', this.onLoadException, this);
44464         
44465         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44466             "up" : function(e){
44467                 this.inKeyMode = true;
44468                 this.selectPrev();
44469             },
44470
44471             "down" : function(e){
44472                 if(!this.isExpanded()){
44473                     this.onTriggerClick();
44474                 }else{
44475                     this.inKeyMode = true;
44476                     this.selectNext();
44477                 }
44478             },
44479
44480             "enter" : function(e){
44481                 this.collapse();
44482                 
44483                 if(this.fireEvent("specialkey", this, e)){
44484                     this.onViewClick(false);
44485                 }
44486                 
44487                 return true;
44488             },
44489
44490             "esc" : function(e){
44491                 this.collapse();
44492             },
44493
44494             "tab" : function(e){
44495                 this.collapse();
44496                 
44497                 if(this.fireEvent("specialkey", this, e)){
44498                     this.onViewClick(false);
44499                 }
44500                 
44501                 return true;
44502             },
44503
44504             scope : this,
44505
44506             doRelay : function(foo, bar, hname){
44507                 if(hname == 'down' || this.scope.isExpanded()){
44508                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44509                 }
44510                 return true;
44511             },
44512
44513             forceKeyDown: true
44514         });
44515         
44516         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44517         
44518     },
44519     
44520     initNumberEvent : function(e)
44521     {
44522         this.inputEl().on("keydown" , this.fireKey,  this);
44523         this.inputEl().on("focus", this.onFocus,  this);
44524         this.inputEl().on("blur", this.onBlur,  this);
44525         
44526         this.inputEl().relayEvent('keyup', this);
44527         
44528         if(this.indicator){
44529             this.indicator.addClass('invisible');
44530         }
44531  
44532         this.originalValue = this.getValue();
44533         
44534         if(this.validationEvent == 'keyup'){
44535             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44536             this.inputEl().on('keyup', this.filterValidation, this);
44537         }
44538         else if(this.validationEvent !== false){
44539             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44540         }
44541         
44542         if(this.selectOnFocus){
44543             this.on("focus", this.preFocus, this);
44544             
44545         }
44546         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44547             this.inputEl().on("keypress", this.filterKeys, this);
44548         } else {
44549             this.inputEl().relayEvent('keypress', this);
44550         }
44551         
44552         var allowed = "0123456789";
44553         
44554         if(this.allowDecimals){
44555             allowed += this.decimalSeparator;
44556         }
44557         
44558         if(this.allowNegative){
44559             allowed += "-";
44560         }
44561         
44562         if(this.thousandsDelimiter) {
44563             allowed += ",";
44564         }
44565         
44566         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44567         
44568         var keyPress = function(e){
44569             
44570             var k = e.getKey();
44571             
44572             var c = e.getCharCode();
44573             
44574             if(
44575                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44576                     allowed.indexOf(String.fromCharCode(c)) === -1
44577             ){
44578                 e.stopEvent();
44579                 return;
44580             }
44581             
44582             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44583                 return;
44584             }
44585             
44586             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44587                 e.stopEvent();
44588             }
44589         };
44590         
44591         this.inputEl().on("keypress", keyPress, this);
44592         
44593     },
44594     
44595     onTriggerClick : function(e)
44596     {   
44597         if(this.disabled){
44598             return;
44599         }
44600         
44601         this.page = 0;
44602         this.loadNext = false;
44603         
44604         if(this.isExpanded()){
44605             this.collapse();
44606             return;
44607         }
44608         
44609         this.hasFocus = true;
44610         
44611         if(this.triggerAction == 'all') {
44612             this.doQuery(this.allQuery, true);
44613             return;
44614         }
44615         
44616         this.doQuery(this.getRawValue());
44617     },
44618     
44619     getCurrency : function()
44620     {   
44621         var v = this.currencyEl().getValue();
44622         
44623         return v;
44624     },
44625     
44626     restrictHeight : function()
44627     {
44628         this.list.alignTo(this.currencyEl(), this.listAlign);
44629         this.list.alignTo(this.currencyEl(), this.listAlign);
44630     },
44631     
44632     onViewClick : function(view, doFocus, el, e)
44633     {
44634         var index = this.view.getSelectedIndexes()[0];
44635         
44636         var r = this.store.getAt(index);
44637         
44638         if(r){
44639             this.onSelect(r, index);
44640         }
44641     },
44642     
44643     onSelect : function(record, index){
44644         
44645         if(this.fireEvent('beforeselect', this, record, index) !== false){
44646         
44647             this.setFromCurrencyData(index > -1 ? record.data : false);
44648             
44649             this.collapse();
44650             
44651             this.fireEvent('select', this, record, index);
44652         }
44653     },
44654     
44655     setFromCurrencyData : function(o)
44656     {
44657         var currency = '';
44658         
44659         this.lastCurrency = o;
44660         
44661         if (this.currencyField) {
44662             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44663         } else {
44664             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44665         }
44666         
44667         this.lastSelectionText = currency;
44668         
44669         //setting default currency
44670         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44671             this.setCurrency(this.defaultCurrency);
44672             return;
44673         }
44674         
44675         this.setCurrency(currency);
44676     },
44677     
44678     setFromData : function(o)
44679     {
44680         var c = {};
44681         
44682         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44683         
44684         this.setFromCurrencyData(c);
44685         
44686         var value = '';
44687         
44688         if (this.name) {
44689             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44690         } else {
44691             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44692         }
44693         
44694         this.setValue(value);
44695         
44696     },
44697     
44698     setCurrency : function(v)
44699     {   
44700         this.currencyValue = v;
44701         
44702         if(this.rendered){
44703             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44704             this.validate();
44705         }
44706     },
44707     
44708     setValue : function(v)
44709     {
44710         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44711         
44712         this.value = v;
44713         
44714         if(this.rendered){
44715             
44716             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44717             
44718             this.inputEl().dom.value = (v == '') ? '' :
44719                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44720             
44721             if(!this.allowZero && v === '0') {
44722                 this.hiddenEl().dom.value = '';
44723                 this.inputEl().dom.value = '';
44724             }
44725             
44726             this.validate();
44727         }
44728     },
44729     
44730     getRawValue : function()
44731     {
44732         var v = this.inputEl().getValue();
44733         
44734         return v;
44735     },
44736     
44737     getValue : function()
44738     {
44739         return this.fixPrecision(this.parseValue(this.getRawValue()));
44740     },
44741     
44742     parseValue : function(value)
44743     {
44744         if(this.thousandsDelimiter) {
44745             value += "";
44746             r = new RegExp(",", "g");
44747             value = value.replace(r, "");
44748         }
44749         
44750         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44751         return isNaN(value) ? '' : value;
44752         
44753     },
44754     
44755     fixPrecision : function(value)
44756     {
44757         if(this.thousandsDelimiter) {
44758             value += "";
44759             r = new RegExp(",", "g");
44760             value = value.replace(r, "");
44761         }
44762         
44763         var nan = isNaN(value);
44764         
44765         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44766             return nan ? '' : value;
44767         }
44768         return parseFloat(value).toFixed(this.decimalPrecision);
44769     },
44770     
44771     decimalPrecisionFcn : function(v)
44772     {
44773         return Math.floor(v);
44774     },
44775     
44776     validateValue : function(value)
44777     {
44778         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44779             return false;
44780         }
44781         
44782         var num = this.parseValue(value);
44783         
44784         if(isNaN(num)){
44785             this.markInvalid(String.format(this.nanText, value));
44786             return false;
44787         }
44788         
44789         if(num < this.minValue){
44790             this.markInvalid(String.format(this.minText, this.minValue));
44791             return false;
44792         }
44793         
44794         if(num > this.maxValue){
44795             this.markInvalid(String.format(this.maxText, this.maxValue));
44796             return false;
44797         }
44798         
44799         return true;
44800     },
44801     
44802     validate : function()
44803     {
44804         if(this.disabled || this.allowBlank){
44805             this.markValid();
44806             return true;
44807         }
44808         
44809         var currency = this.getCurrency();
44810         
44811         if(this.validateValue(this.getRawValue()) && currency.length){
44812             this.markValid();
44813             return true;
44814         }
44815         
44816         this.markInvalid();
44817         return false;
44818     },
44819     
44820     getName: function()
44821     {
44822         return this.name;
44823     },
44824     
44825     beforeBlur : function()
44826     {
44827         if(!this.castInt){
44828             return;
44829         }
44830         
44831         var v = this.parseValue(this.getRawValue());
44832         
44833         if(v || v == 0){
44834             this.setValue(v);
44835         }
44836     },
44837     
44838     onBlur : function()
44839     {
44840         this.beforeBlur();
44841         
44842         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44843             //this.el.removeClass(this.focusClass);
44844         }
44845         
44846         this.hasFocus = false;
44847         
44848         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44849             this.validate();
44850         }
44851         
44852         var v = this.getValue();
44853         
44854         if(String(v) !== String(this.startValue)){
44855             this.fireEvent('change', this, v, this.startValue);
44856         }
44857         
44858         this.fireEvent("blur", this);
44859     },
44860     
44861     inputEl : function()
44862     {
44863         return this.el.select('.roo-money-amount-input', true).first();
44864     },
44865     
44866     currencyEl : function()
44867     {
44868         return this.el.select('.roo-money-currency-input', true).first();
44869     },
44870     
44871     hiddenEl : function()
44872     {
44873         return this.el.select('input.hidden-number-input',true).first();
44874     }
44875     
44876 });/**
44877  * @class Roo.bootstrap.BezierSignature
44878  * @extends Roo.bootstrap.Component
44879  * Bootstrap BezierSignature class
44880  * This script refer to:
44881  *    Title: Signature Pad
44882  *    Author: szimek
44883  *    Availability: https://github.com/szimek/signature_pad
44884  *
44885  * @constructor
44886  * Create a new BezierSignature
44887  * @param {Object} config The config object
44888  */
44889
44890 Roo.bootstrap.BezierSignature = function(config){
44891     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44892     this.addEvents({
44893         "resize" : true
44894     });
44895 };
44896
44897 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44898 {
44899      
44900     curve_data: [],
44901     
44902     is_empty: true,
44903     
44904     mouse_btn_down: true,
44905     
44906     /**
44907      * @cfg {int} canvas height
44908      */
44909     canvas_height: '200px',
44910     
44911     /**
44912      * @cfg {float|function} Radius of a single dot.
44913      */ 
44914     dot_size: false,
44915     
44916     /**
44917      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44918      */
44919     min_width: 0.5,
44920     
44921     /**
44922      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44923      */
44924     max_width: 2.5,
44925     
44926     /**
44927      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44928      */
44929     throttle: 16,
44930     
44931     /**
44932      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44933      */
44934     min_distance: 5,
44935     
44936     /**
44937      * @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.
44938      */
44939     bg_color: 'rgba(0, 0, 0, 0)',
44940     
44941     /**
44942      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44943      */
44944     dot_color: 'black',
44945     
44946     /**
44947      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44948      */ 
44949     velocity_filter_weight: 0.7,
44950     
44951     /**
44952      * @cfg {function} Callback when stroke begin. 
44953      */
44954     onBegin: false,
44955     
44956     /**
44957      * @cfg {function} Callback when stroke end.
44958      */
44959     onEnd: false,
44960     
44961     getAutoCreate : function()
44962     {
44963         var cls = 'roo-signature column';
44964         
44965         if(this.cls){
44966             cls += ' ' + this.cls;
44967         }
44968         
44969         var col_sizes = [
44970             'lg',
44971             'md',
44972             'sm',
44973             'xs'
44974         ];
44975         
44976         for(var i = 0; i < col_sizes.length; i++) {
44977             if(this[col_sizes[i]]) {
44978                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44979             }
44980         }
44981         
44982         var cfg = {
44983             tag: 'div',
44984             cls: cls,
44985             cn: [
44986                 {
44987                     tag: 'div',
44988                     cls: 'roo-signature-body',
44989                     cn: [
44990                         {
44991                             tag: 'canvas',
44992                             cls: 'roo-signature-body-canvas',
44993                             height: this.canvas_height,
44994                             width: this.canvas_width
44995                         }
44996                     ]
44997                 },
44998                 {
44999                     tag: 'input',
45000                     type: 'file',
45001                     style: 'display: none'
45002                 }
45003             ]
45004         };
45005         
45006         return cfg;
45007     },
45008     
45009     initEvents: function() 
45010     {
45011         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45012         
45013         var canvas = this.canvasEl();
45014         
45015         // mouse && touch event swapping...
45016         canvas.dom.style.touchAction = 'none';
45017         canvas.dom.style.msTouchAction = 'none';
45018         
45019         this.mouse_btn_down = false;
45020         canvas.on('mousedown', this._handleMouseDown, this);
45021         canvas.on('mousemove', this._handleMouseMove, this);
45022         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45023         
45024         if (window.PointerEvent) {
45025             canvas.on('pointerdown', this._handleMouseDown, this);
45026             canvas.on('pointermove', this._handleMouseMove, this);
45027             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45028         }
45029         
45030         if ('ontouchstart' in window) {
45031             canvas.on('touchstart', this._handleTouchStart, this);
45032             canvas.on('touchmove', this._handleTouchMove, this);
45033             canvas.on('touchend', this._handleTouchEnd, this);
45034         }
45035         
45036         Roo.EventManager.onWindowResize(this.resize, this, true);
45037         
45038         // file input event
45039         this.fileEl().on('change', this.uploadImage, this);
45040         
45041         this.clear();
45042         
45043         this.resize();
45044     },
45045     
45046     resize: function(){
45047         
45048         var canvas = this.canvasEl().dom;
45049         var ctx = this.canvasElCtx();
45050         var img_data = false;
45051         
45052         if(canvas.width > 0) {
45053             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45054         }
45055         // setting canvas width will clean img data
45056         canvas.width = 0;
45057         
45058         var style = window.getComputedStyle ? 
45059             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45060             
45061         var padding_left = parseInt(style.paddingLeft) || 0;
45062         var padding_right = parseInt(style.paddingRight) || 0;
45063         
45064         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45065         
45066         if(img_data) {
45067             ctx.putImageData(img_data, 0, 0);
45068         }
45069     },
45070     
45071     _handleMouseDown: function(e)
45072     {
45073         if (e.browserEvent.which === 1) {
45074             this.mouse_btn_down = true;
45075             this.strokeBegin(e);
45076         }
45077     },
45078     
45079     _handleMouseMove: function (e)
45080     {
45081         if (this.mouse_btn_down) {
45082             this.strokeMoveUpdate(e);
45083         }
45084     },
45085     
45086     _handleMouseUp: function (e)
45087     {
45088         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45089             this.mouse_btn_down = false;
45090             this.strokeEnd(e);
45091         }
45092     },
45093     
45094     _handleTouchStart: function (e) {
45095         
45096         e.preventDefault();
45097         if (e.browserEvent.targetTouches.length === 1) {
45098             // var touch = e.browserEvent.changedTouches[0];
45099             // this.strokeBegin(touch);
45100             
45101              this.strokeBegin(e); // assume e catching the correct xy...
45102         }
45103     },
45104     
45105     _handleTouchMove: function (e) {
45106         e.preventDefault();
45107         // var touch = event.targetTouches[0];
45108         // _this._strokeMoveUpdate(touch);
45109         this.strokeMoveUpdate(e);
45110     },
45111     
45112     _handleTouchEnd: function (e) {
45113         var wasCanvasTouched = e.target === this.canvasEl().dom;
45114         if (wasCanvasTouched) {
45115             e.preventDefault();
45116             // var touch = event.changedTouches[0];
45117             // _this._strokeEnd(touch);
45118             this.strokeEnd(e);
45119         }
45120     },
45121     
45122     reset: function () {
45123         this._lastPoints = [];
45124         this._lastVelocity = 0;
45125         this._lastWidth = (this.min_width + this.max_width) / 2;
45126         this.canvasElCtx().fillStyle = this.dot_color;
45127     },
45128     
45129     strokeMoveUpdate: function(e)
45130     {
45131         this.strokeUpdate(e);
45132         
45133         if (this.throttle) {
45134             this.throttleStroke(this.strokeUpdate, this.throttle);
45135         }
45136         else {
45137             this.strokeUpdate(e);
45138         }
45139     },
45140     
45141     strokeBegin: function(e)
45142     {
45143         var newPointGroup = {
45144             color: this.dot_color,
45145             points: []
45146         };
45147         
45148         if (typeof this.onBegin === 'function') {
45149             this.onBegin(e);
45150         }
45151         
45152         this.curve_data.push(newPointGroup);
45153         this.reset();
45154         this.strokeUpdate(e);
45155     },
45156     
45157     strokeUpdate: function(e)
45158     {
45159         var rect = this.canvasEl().dom.getBoundingClientRect();
45160         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45161         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45162         var lastPoints = lastPointGroup.points;
45163         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45164         var isLastPointTooClose = lastPoint
45165             ? point.distanceTo(lastPoint) <= this.min_distance
45166             : false;
45167         var color = lastPointGroup.color;
45168         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45169             var curve = this.addPoint(point);
45170             if (!lastPoint) {
45171                 this.drawDot({color: color, point: point});
45172             }
45173             else if (curve) {
45174                 this.drawCurve({color: color, curve: curve});
45175             }
45176             lastPoints.push({
45177                 time: point.time,
45178                 x: point.x,
45179                 y: point.y
45180             });
45181         }
45182     },
45183     
45184     strokeEnd: function(e)
45185     {
45186         this.strokeUpdate(e);
45187         if (typeof this.onEnd === 'function') {
45188             this.onEnd(e);
45189         }
45190     },
45191     
45192     addPoint:  function (point) {
45193         var _lastPoints = this._lastPoints;
45194         _lastPoints.push(point);
45195         if (_lastPoints.length > 2) {
45196             if (_lastPoints.length === 3) {
45197                 _lastPoints.unshift(_lastPoints[0]);
45198             }
45199             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45200             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45201             _lastPoints.shift();
45202             return curve;
45203         }
45204         return null;
45205     },
45206     
45207     calculateCurveWidths: function (startPoint, endPoint) {
45208         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45209             (1 - this.velocity_filter_weight) * this._lastVelocity;
45210
45211         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45212         var widths = {
45213             end: newWidth,
45214             start: this._lastWidth
45215         };
45216         
45217         this._lastVelocity = velocity;
45218         this._lastWidth = newWidth;
45219         return widths;
45220     },
45221     
45222     drawDot: function (_a) {
45223         var color = _a.color, point = _a.point;
45224         var ctx = this.canvasElCtx();
45225         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45226         ctx.beginPath();
45227         this.drawCurveSegment(point.x, point.y, width);
45228         ctx.closePath();
45229         ctx.fillStyle = color;
45230         ctx.fill();
45231     },
45232     
45233     drawCurve: function (_a) {
45234         var color = _a.color, curve = _a.curve;
45235         var ctx = this.canvasElCtx();
45236         var widthDelta = curve.endWidth - curve.startWidth;
45237         var drawSteps = Math.floor(curve.length()) * 2;
45238         ctx.beginPath();
45239         ctx.fillStyle = color;
45240         for (var i = 0; i < drawSteps; i += 1) {
45241         var t = i / drawSteps;
45242         var tt = t * t;
45243         var ttt = tt * t;
45244         var u = 1 - t;
45245         var uu = u * u;
45246         var uuu = uu * u;
45247         var x = uuu * curve.startPoint.x;
45248         x += 3 * uu * t * curve.control1.x;
45249         x += 3 * u * tt * curve.control2.x;
45250         x += ttt * curve.endPoint.x;
45251         var y = uuu * curve.startPoint.y;
45252         y += 3 * uu * t * curve.control1.y;
45253         y += 3 * u * tt * curve.control2.y;
45254         y += ttt * curve.endPoint.y;
45255         var width = curve.startWidth + ttt * widthDelta;
45256         this.drawCurveSegment(x, y, width);
45257         }
45258         ctx.closePath();
45259         ctx.fill();
45260     },
45261     
45262     drawCurveSegment: function (x, y, width) {
45263         var ctx = this.canvasElCtx();
45264         ctx.moveTo(x, y);
45265         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45266         this.is_empty = false;
45267     },
45268     
45269     clear: function()
45270     {
45271         var ctx = this.canvasElCtx();
45272         var canvas = this.canvasEl().dom;
45273         ctx.fillStyle = this.bg_color;
45274         ctx.clearRect(0, 0, canvas.width, canvas.height);
45275         ctx.fillRect(0, 0, canvas.width, canvas.height);
45276         this.curve_data = [];
45277         this.reset();
45278         this.is_empty = true;
45279     },
45280     
45281     fileEl: function()
45282     {
45283         return  this.el.select('input',true).first();
45284     },
45285     
45286     canvasEl: function()
45287     {
45288         return this.el.select('canvas',true).first();
45289     },
45290     
45291     canvasElCtx: function()
45292     {
45293         return this.el.select('canvas',true).first().dom.getContext('2d');
45294     },
45295     
45296     getImage: function(type)
45297     {
45298         if(this.is_empty) {
45299             return false;
45300         }
45301         
45302         // encryption ?
45303         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45304     },
45305     
45306     drawFromImage: function(img_src)
45307     {
45308         var img = new Image();
45309         
45310         img.onload = function(){
45311             this.canvasElCtx().drawImage(img, 0, 0);
45312         }.bind(this);
45313         
45314         img.src = img_src;
45315         
45316         this.is_empty = false;
45317     },
45318     
45319     selectImage: function()
45320     {
45321         this.fileEl().dom.click();
45322     },
45323     
45324     uploadImage: function(e)
45325     {
45326         var reader = new FileReader();
45327         
45328         reader.onload = function(e){
45329             var img = new Image();
45330             img.onload = function(){
45331                 this.reset();
45332                 this.canvasElCtx().drawImage(img, 0, 0);
45333             }.bind(this);
45334             img.src = e.target.result;
45335         }.bind(this);
45336         
45337         reader.readAsDataURL(e.target.files[0]);
45338     },
45339     
45340     // Bezier Point Constructor
45341     Point: (function () {
45342         function Point(x, y, time) {
45343             this.x = x;
45344             this.y = y;
45345             this.time = time || Date.now();
45346         }
45347         Point.prototype.distanceTo = function (start) {
45348             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45349         };
45350         Point.prototype.equals = function (other) {
45351             return this.x === other.x && this.y === other.y && this.time === other.time;
45352         };
45353         Point.prototype.velocityFrom = function (start) {
45354             return this.time !== start.time
45355             ? this.distanceTo(start) / (this.time - start.time)
45356             : 0;
45357         };
45358         return Point;
45359     }()),
45360     
45361     
45362     // Bezier Constructor
45363     Bezier: (function () {
45364         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45365             this.startPoint = startPoint;
45366             this.control2 = control2;
45367             this.control1 = control1;
45368             this.endPoint = endPoint;
45369             this.startWidth = startWidth;
45370             this.endWidth = endWidth;
45371         }
45372         Bezier.fromPoints = function (points, widths, scope) {
45373             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45374             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45375             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45376         };
45377         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45378             var dx1 = s1.x - s2.x;
45379             var dy1 = s1.y - s2.y;
45380             var dx2 = s2.x - s3.x;
45381             var dy2 = s2.y - s3.y;
45382             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45383             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45384             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45385             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45386             var dxm = m1.x - m2.x;
45387             var dym = m1.y - m2.y;
45388             var k = l2 / (l1 + l2);
45389             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45390             var tx = s2.x - cm.x;
45391             var ty = s2.y - cm.y;
45392             return {
45393                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45394                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45395             };
45396         };
45397         Bezier.prototype.length = function () {
45398             var steps = 10;
45399             var length = 0;
45400             var px;
45401             var py;
45402             for (var i = 0; i <= steps; i += 1) {
45403                 var t = i / steps;
45404                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45405                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45406                 if (i > 0) {
45407                     var xdiff = cx - px;
45408                     var ydiff = cy - py;
45409                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45410                 }
45411                 px = cx;
45412                 py = cy;
45413             }
45414             return length;
45415         };
45416         Bezier.prototype.point = function (t, start, c1, c2, end) {
45417             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45418             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45419             + (3.0 * c2 * (1.0 - t) * t * t)
45420             + (end * t * t * t);
45421         };
45422         return Bezier;
45423     }()),
45424     
45425     throttleStroke: function(fn, wait) {
45426       if (wait === void 0) { wait = 250; }
45427       var previous = 0;
45428       var timeout = null;
45429       var result;
45430       var storedContext;
45431       var storedArgs;
45432       var later = function () {
45433           previous = Date.now();
45434           timeout = null;
45435           result = fn.apply(storedContext, storedArgs);
45436           if (!timeout) {
45437               storedContext = null;
45438               storedArgs = [];
45439           }
45440       };
45441       return function wrapper() {
45442           var args = [];
45443           for (var _i = 0; _i < arguments.length; _i++) {
45444               args[_i] = arguments[_i];
45445           }
45446           var now = Date.now();
45447           var remaining = wait - (now - previous);
45448           storedContext = this;
45449           storedArgs = args;
45450           if (remaining <= 0 || remaining > wait) {
45451               if (timeout) {
45452                   clearTimeout(timeout);
45453                   timeout = null;
45454               }
45455               previous = now;
45456               result = fn.apply(storedContext, storedArgs);
45457               if (!timeout) {
45458                   storedContext = null;
45459                   storedArgs = [];
45460               }
45461           }
45462           else if (!timeout) {
45463               timeout = window.setTimeout(later, remaining);
45464           }
45465           return result;
45466       };
45467   }
45468   
45469 });
45470
45471  
45472
45473