better support for mailchimp emails
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * Bootstrap Component base class
227  * @cfg {String} cls css class
228  * @cfg {String} style any extra css
229  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
230  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
231  * @cfg {string} dataId cutomer id
232  * @cfg {string} name Specifies name attribute
233  * @cfg {string} tooltip  Text for the tooltip
234  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
235  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
236  
237  * @constructor
238  * Do not use directly - it does not do anything..
239  * @param {Object} config The config object
240  */
241
242
243
244 Roo.bootstrap.Component = function(config){
245     Roo.bootstrap.Component.superclass.constructor.call(this, config);
246        
247     this.addEvents({
248         /**
249          * @event childrenrendered
250          * Fires when the children have been rendered..
251          * @param {Roo.bootstrap.Component} this
252          */
253         "childrenrendered" : true
254         
255         
256         
257     });
258     
259     
260 };
261
262 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
263     
264     
265     allowDomMove : false, // to stop relocations in parent onRender...
266     
267     cls : false,
268     
269     style : false,
270     
271     autoCreate : false,
272     
273     tooltip : null,
274     /**
275      * Initialize Events for the element
276      */
277     initEvents : function() { },
278     
279     xattr : false,
280     
281     parentId : false,
282     
283     can_build_overlaid : true,
284     
285     container_method : false,
286     
287     dataId : false,
288     
289     name : false,
290     
291     parent: function() {
292         // returns the parent component..
293         return Roo.ComponentMgr.get(this.parentId)
294         
295         
296     },
297     
298     // private
299     onRender : function(ct, position)
300     {
301        // Roo.log("Call onRender: " + this.xtype);
302         
303         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
304         
305         if(this.el){
306             if (this.el.attr('xtype')) {
307                 this.el.attr('xtypex', this.el.attr('xtype'));
308                 this.el.dom.removeAttribute('xtype');
309                 
310                 this.initEvents();
311             }
312             
313             return;
314         }
315         
316          
317         
318         var cfg = Roo.apply({},  this.getAutoCreate());
319         
320         cfg.id = this.id || Roo.id();
321         
322         // fill in the extra attributes 
323         if (this.xattr && typeof(this.xattr) =='object') {
324             for (var i in this.xattr) {
325                 cfg[i] = this.xattr[i];
326             }
327         }
328         
329         if(this.dataId){
330             cfg.dataId = this.dataId;
331         }
332         
333         if (this.cls) {
334             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
335         }
336         
337         if (this.style) { // fixme needs to support more complex style data.
338             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
339         }
340         
341         if(this.name){
342             cfg.name = this.name;
343         }
344         
345         this.el = ct.createChild(cfg, position);
346         
347         if (this.tooltip) {
348             this.tooltipEl().attr('tooltip', this.tooltip);
349         }
350         
351         if(this.tabIndex !== undefined){
352             this.el.dom.setAttribute('tabIndex', this.tabIndex);
353         }
354         
355         this.initEvents();
356         
357     },
358     /**
359      * Fetch the element to add children to
360      * @return {Roo.Element} defaults to this.el
361      */
362     getChildContainer : function()
363     {
364         return this.el;
365     },
366     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367     {
368         return Roo.get(document.body);
369     },
370     
371     /**
372      * Fetch the element to display the tooltip on.
373      * @return {Roo.Element} defaults to this.el
374      */
375     tooltipEl : function()
376     {
377         return this.el;
378     },
379         
380     addxtype  : function(tree,cntr)
381     {
382         var cn = this;
383         
384         cn = Roo.factory(tree);
385         //Roo.log(['addxtype', cn]);
386            
387         cn.parentType = this.xtype; //??
388         cn.parentId = this.id;
389         
390         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
391         if (typeof(cn.container_method) == 'string') {
392             cntr = cn.container_method;
393         }
394         
395         
396         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
397         
398         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
399         
400         var build_from_html =  Roo.XComponent.build_from_html;
401           
402         var is_body  = (tree.xtype == 'Body') ;
403           
404         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405           
406         var self_cntr_el = Roo.get(this[cntr](false));
407         
408         // do not try and build conditional elements 
409         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
410             return false;
411         }
412         
413         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
414             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
415                 return this.addxtypeChild(tree,cntr, is_body);
416             }
417             
418             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
419                 
420             if(echild){
421                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
422             }
423             
424             Roo.log('skipping render');
425             return cn;
426             
427         }
428         
429         var ret = false;
430         if (!build_from_html) {
431             return false;
432         }
433         
434         // this i think handles overlaying multiple children of the same type
435         // with the sam eelement.. - which might be buggy..
436         while (true) {
437             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
438             
439             if (!echild) {
440                 break;
441             }
442             
443             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
444                 break;
445             }
446             
447             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448         }
449        
450         return ret;
451     },
452     
453     
454     addxtypeChild : function (tree, cntr, is_body)
455     {
456         Roo.debug && Roo.log('addxtypeChild:' + cntr);
457         var cn = this;
458         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
459         
460         
461         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
462                     (typeof(tree['flexy:foreach']) != 'undefined');
463           
464     
465         
466         skip_children = false;
467         // render the element if it's not BODY.
468         if (!is_body) {
469             
470             // if parent was disabled, then do not try and create the children..
471             if(!this[cntr](true)){
472                 tree.items = [];
473                 return tree;
474             }
475            
476             cn = Roo.factory(tree);
477            
478             cn.parentType = this.xtype; //??
479             cn.parentId = this.id;
480             
481             var build_from_html =  Roo.XComponent.build_from_html;
482             
483             
484             // does the container contain child eleemnts with 'xtype' attributes.
485             // that match this xtype..
486             // note - when we render we create these as well..
487             // so we should check to see if body has xtype set.
488             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489                
490                 var self_cntr_el = Roo.get(this[cntr](false));
491                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492                 if (echild) { 
493                     //Roo.log(Roo.XComponent.build_from_html);
494                     //Roo.log("got echild:");
495                     //Roo.log(echild);
496                 }
497                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
498                 // and are not displayed -this causes this to use up the wrong element when matching.
499                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
500                 
501                 
502                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
503                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
504                   
505                   
506                   
507                     cn.el = echild;
508                   //  Roo.log("GOT");
509                     //echild.dom.removeAttribute('xtype');
510                 } else {
511                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
512                     Roo.debug && Roo.log(self_cntr_el);
513                     Roo.debug && Roo.log(echild);
514                     Roo.debug && Roo.log(cn);
515                 }
516             }
517            
518             
519            
520             // if object has flexy:if - then it may or may not be rendered.
521             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
522                 // skip a flexy if element.
523                 Roo.debug && Roo.log('skipping render');
524                 Roo.debug && Roo.log(tree);
525                 if (!cn.el) {
526                     Roo.debug && Roo.log('skipping all children');
527                     skip_children = true;
528                 }
529                 
530              } else {
531                  
532                 // actually if flexy:foreach is found, we really want to create 
533                 // multiple copies here...
534                 //Roo.log('render');
535                 //Roo.log(this[cntr]());
536                 // some elements do not have render methods.. like the layouts...
537                 /*
538                 if(this[cntr](true) === false){
539                     cn.items = [];
540                     return cn;
541                 }
542                 */
543                 cn.render && cn.render(this[cntr](true));
544                 
545              }
546             // then add the element..
547         }
548          
549         // handle the kids..
550         
551         var nitems = [];
552         /*
553         if (typeof (tree.menu) != 'undefined') {
554             tree.menu.parentType = cn.xtype;
555             tree.menu.triggerEl = cn.el;
556             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
557             
558         }
559         */
560         if (!tree.items || !tree.items.length) {
561             cn.items = nitems;
562             //Roo.log(["no children", this]);
563             
564             return cn;
565         }
566          
567         var items = tree.items;
568         delete tree.items;
569         
570         //Roo.log(items.length);
571             // add the items..
572         if (!skip_children) {    
573             for(var i =0;i < items.length;i++) {
574               //  Roo.log(['add child', items[i]]);
575                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
576             }
577         }
578         
579         cn.items = nitems;
580         
581         //Roo.log("fire childrenrendered");
582         
583         cn.fireEvent('childrenrendered', this);
584         
585         return cn;
586     },
587     
588     /**
589      * Set the element that will be used to show or hide
590      */
591     setVisibilityEl : function(el)
592     {
593         this.visibilityEl = el;
594     },
595     
596      /**
597      * Get the element that will be used to show or hide
598      */
599     getVisibilityEl : function()
600     {
601         if (typeof(this.visibilityEl) == 'object') {
602             return this.visibilityEl;
603         }
604         
605         if (typeof(this.visibilityEl) == 'string') {
606             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607         }
608         
609         return this.getEl();
610     },
611     
612     /**
613      * Show a component - removes 'hidden' class
614      */
615     show : function()
616     {
617         if(!this.getVisibilityEl()){
618             return;
619         }
620          
621         this.getVisibilityEl().removeClass(['hidden','d-none']);
622         
623         this.fireEvent('show', this);
624         
625         
626     },
627     /**
628      * Hide a component - adds 'hidden' class
629      */
630     hide: function()
631     {
632         if(!this.getVisibilityEl()){
633             return;
634         }
635         
636         this.getVisibilityEl().addClass(['hidden','d-none']);
637         
638         this.fireEvent('hide', this);
639         
640     }
641 });
642
643  /*
644  * - LGPL
645  *
646  * element
647  * 
648  */
649
650 /**
651  * @class Roo.bootstrap.Element
652  * @extends Roo.bootstrap.Component
653  * Bootstrap Element class
654  * @cfg {String} html contents of the element
655  * @cfg {String} tag tag of the element
656  * @cfg {String} cls class of the element
657  * @cfg {Boolean} preventDefault (true|false) default false
658  * @cfg {Boolean} clickable (true|false) default false
659  * @cfg {String} role default blank - set to button to force cursor pointer
660  
661  * 
662  * @constructor
663  * Create a new Element
664  * @param {Object} config The config object
665  */
666
667 Roo.bootstrap.Element = function(config){
668     Roo.bootstrap.Element.superclass.constructor.call(this, config);
669     
670     this.addEvents({
671         // raw events
672         /**
673          * @event click
674          * When a element is chick
675          * @param {Roo.bootstrap.Element} this
676          * @param {Roo.EventObject} e
677          */
678         "click" : true 
679         
680       
681     });
682 };
683
684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
685     
686     tag: 'div',
687     cls: '',
688     html: '',
689     preventDefault: false, 
690     clickable: false,
691     tapedTwice : false,
692     role : false,
693     
694     getAutoCreate : function(){
695         
696         var cfg = {
697             tag: this.tag,
698             // cls: this.cls, double assign in parent class Component.js :: onRender
699             html: this.html
700         };
701         if (this.role !== false) {
702             cfg.role = this.role;
703         }
704         
705         return cfg;
706     },
707     
708     initEvents: function() 
709     {
710         Roo.bootstrap.Element.superclass.initEvents.call(this);
711         
712         if(this.clickable){
713             this.el.on('click', this.onClick, this);
714         }
715         
716         
717     },
718     
719     onClick : function(e)
720     {
721         if(this.preventDefault){
722             e.preventDefault();
723         }
724         
725         this.fireEvent('click', this, e); // why was this double click before?
726     },
727     
728     
729     
730
731     
732     
733     getValue : function()
734     {
735         return this.el.dom.innerHTML;
736     },
737     
738     setValue : function(value)
739     {
740         this.el.dom.innerHTML = value;
741     }
742    
743 });
744
745  
746
747  /*
748  * - LGPL
749  *
750  * dropable area
751  * 
752  */
753
754 /**
755  * @class Roo.bootstrap.DropTarget
756  * @extends Roo.bootstrap.Element
757  * Bootstrap DropTarget class
758  
759  * @cfg {string} name dropable name
760  * 
761  * @constructor
762  * Create a new Dropable Area
763  * @param {Object} config The config object
764  */
765
766 Roo.bootstrap.DropTarget = function(config){
767     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
768     
769     this.addEvents({
770         // raw events
771         /**
772          * @event click
773          * When a element is chick
774          * @param {Roo.bootstrap.Element} this
775          * @param {Roo.EventObject} e
776          */
777         "drop" : true
778     });
779 };
780
781 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
782     
783     
784     getAutoCreate : function(){
785         
786          
787     },
788     
789     initEvents: function() 
790     {
791         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
792         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
793             ddGroup: this.name,
794             listeners : {
795                 drop : this.dragDrop.createDelegate(this),
796                 enter : this.dragEnter.createDelegate(this),
797                 out : this.dragOut.createDelegate(this),
798                 over : this.dragOver.createDelegate(this)
799             }
800             
801         });
802         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
803     },
804     
805     dragDrop : function(source,e,data)
806     {
807         // user has to decide how to impliment this.
808         Roo.log('drop');
809         Roo.log(this);
810         //this.fireEvent('drop', this, source, e ,data);
811         return false;
812     },
813     
814     dragEnter : function(n, dd, e, data)
815     {
816         // probably want to resize the element to match the dropped element..
817         Roo.log("enter");
818         this.originalSize = this.el.getSize();
819         this.el.setSize( n.el.getSize());
820         this.dropZone.DDM.refreshCache(this.name);
821         Roo.log([n, dd, e, data]);
822     },
823     
824     dragOut : function(value)
825     {
826         // resize back to normal
827         Roo.log("out");
828         this.el.setSize(this.originalSize);
829         this.dropZone.resetConstraints();
830     },
831     
832     dragOver : function()
833     {
834         // ??? do nothing?
835     }
836    
837 });
838
839  
840
841  /*
842  * - LGPL
843  *
844  * Body
845  *
846  */
847
848 /**
849  * @class Roo.bootstrap.Body
850  * @extends Roo.bootstrap.Component
851  * Bootstrap Body class
852  *
853  * @constructor
854  * Create a new body
855  * @param {Object} config The config object
856  */
857
858 Roo.bootstrap.Body = function(config){
859
860     config = config || {};
861
862     Roo.bootstrap.Body.superclass.constructor.call(this, config);
863     this.el = Roo.get(config.el ? config.el : document.body );
864     if (this.cls && this.cls.length) {
865         Roo.get(document.body).addClass(this.cls);
866     }
867 };
868
869 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
870
871     is_body : true,// just to make sure it's constructed?
872
873         autoCreate : {
874         cls: 'container'
875     },
876     onRender : function(ct, position)
877     {
878        /* Roo.log("Roo.bootstrap.Body - onRender");
879         if (this.cls && this.cls.length) {
880             Roo.get(document.body).addClass(this.cls);
881         }
882         // style??? xttr???
883         */
884     }
885
886
887
888
889 });
890 /*
891  * - LGPL
892  *
893  * button group
894  * 
895  */
896
897
898 /**
899  * @class Roo.bootstrap.ButtonGroup
900  * @extends Roo.bootstrap.Component
901  * Bootstrap ButtonGroup class
902  * @cfg {String} size lg | sm | xs (default empty normal)
903  * @cfg {String} align vertical | justified  (default none)
904  * @cfg {String} direction up | down (default down)
905  * @cfg {Boolean} toolbar false | true
906  * @cfg {Boolean} btn true | false
907  * 
908  * 
909  * @constructor
910  * Create a new Input
911  * @param {Object} config The config object
912  */
913
914 Roo.bootstrap.ButtonGroup = function(config){
915     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
916 };
917
918 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
919     
920     size: '',
921     align: '',
922     direction: '',
923     toolbar: false,
924     btn: true,
925
926     getAutoCreate : function(){
927         var cfg = {
928             cls: 'btn-group',
929             html : null
930         };
931         
932         cfg.html = this.html || cfg.html;
933         
934         if (this.toolbar) {
935             cfg = {
936                 cls: 'btn-toolbar',
937                 html: null
938             };
939             
940             return cfg;
941         }
942         
943         if (['vertical','justified'].indexOf(this.align)!==-1) {
944             cfg.cls = 'btn-group-' + this.align;
945             
946             if (this.align == 'justified') {
947                 console.log(this.items);
948             }
949         }
950         
951         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
952             cfg.cls += ' btn-group-' + this.size;
953         }
954         
955         if (this.direction == 'up') {
956             cfg.cls += ' dropup' ;
957         }
958         
959         return cfg;
960     },
961     /**
962      * Add a button to the group (similar to NavItem API.)
963      */
964     addItem : function(cfg)
965     {
966         var cn = new Roo.bootstrap.Button(cfg);
967         //this.register(cn);
968         cn.parentId = this.id;
969         cn.onRender(this.el, null);
970         return cn;
971     }
972    
973 });
974
975  /*
976  * - LGPL
977  *
978  * button
979  * 
980  */
981
982 /**
983  * @class Roo.bootstrap.Button
984  * @extends Roo.bootstrap.Component
985  * Bootstrap Button class
986  * @cfg {String} html The button content
987  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
988  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
989  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
990  * @cfg {String} size (lg|sm|xs)
991  * @cfg {String} tag (a|input|submit)
992  * @cfg {String} href empty or href
993  * @cfg {Boolean} disabled default false;
994  * @cfg {Boolean} isClose default false;
995  * @cfg {String} glyphicon depricated - use fa
996  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
997  * @cfg {String} badge text for badge
998  * @cfg {String} theme (default|glow)  
999  * @cfg {Boolean} inverse dark themed version
1000  * @cfg {Boolean} toggle is it a slidy toggle button
1001  * @cfg {Boolean} pressed   default null - if the button ahs active state
1002  * @cfg {String} ontext text for on slidy toggle state
1003  * @cfg {String} offtext text for off slidy toggle state
1004  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1005  * @cfg {Boolean} removeClass remove the standard class..
1006  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1007  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1008  * 
1009  * @constructor
1010  * Create a new button
1011  * @param {Object} config The config object
1012  */
1013
1014
1015 Roo.bootstrap.Button = function(config){
1016     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020         /**
1021          * @event click
1022          * When a button is pressed
1023          * @param {Roo.bootstrap.Button} btn
1024          * @param {Roo.EventObject} e
1025          */
1026         "click" : true,
1027         /**
1028          * @event dblclick
1029          * When a button is double clicked
1030          * @param {Roo.bootstrap.Button} btn
1031          * @param {Roo.EventObject} e
1032          */
1033         "dblclick" : true,
1034          /**
1035          * @event toggle
1036          * After the button has been toggles
1037          * @param {Roo.bootstrap.Button} btn
1038          * @param {Roo.EventObject} e
1039          * @param {boolean} pressed (also available as button.pressed)
1040          */
1041         "toggle" : true
1042     });
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1046     html: false,
1047     active: false,
1048     weight: '',
1049     badge_weight: '',
1050     outline : false,
1051     size: '',
1052     tag: 'button',
1053     href: '',
1054     disabled: false,
1055     isClose: false,
1056     glyphicon: '',
1057     fa: '',
1058     badge: '',
1059     theme: 'default',
1060     inverse: false,
1061     
1062     toggle: false,
1063     ontext: 'ON',
1064     offtext: 'OFF',
1065     defaulton: true,
1066     preventDefault: true,
1067     removeClass: false,
1068     name: false,
1069     target: false,
1070     group : false,
1071      
1072     pressed : null,
1073      
1074     
1075     getAutoCreate : function(){
1076         
1077         var cfg = {
1078             tag : 'button',
1079             cls : 'roo-button',
1080             html: ''
1081         };
1082         
1083         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1084             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1085             this.tag = 'button';
1086         } else {
1087             cfg.tag = this.tag;
1088         }
1089         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090         
1091         if (this.toggle == true) {
1092             cfg={
1093                 tag: 'div',
1094                 cls: 'slider-frame roo-button',
1095                 cn: [
1096                     {
1097                         tag: 'span',
1098                         'data-on-text':'ON',
1099                         'data-off-text':'OFF',
1100                         cls: 'slider-button',
1101                         html: this.offtext
1102                     }
1103                 ]
1104             };
1105             // why are we validating the weights?
1106             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1107                 cfg.cls +=  ' ' + this.weight;
1108             }
1109             
1110             return cfg;
1111         }
1112         
1113         if (this.isClose) {
1114             cfg.cls += ' close';
1115             
1116             cfg["aria-hidden"] = true;
1117             
1118             cfg.html = "&times;";
1119             
1120             return cfg;
1121         }
1122              
1123         
1124         if (this.theme==='default') {
1125             cfg.cls = 'btn roo-button';
1126             
1127             //if (this.parentType != 'Navbar') {
1128             this.weight = this.weight.length ?  this.weight : 'default';
1129             //}
1130             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131                 
1132                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1133                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1134                 cfg.cls += ' btn-' + outline + weight;
1135                 if (this.weight == 'default') {
1136                     // BC
1137                     cfg.cls += ' btn-' + this.weight;
1138                 }
1139             }
1140         } else if (this.theme==='glow') {
1141             
1142             cfg.tag = 'a';
1143             cfg.cls = 'btn-glow roo-button';
1144             
1145             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146                 
1147                 cfg.cls += ' ' + this.weight;
1148             }
1149         }
1150    
1151         
1152         if (this.inverse) {
1153             this.cls += ' inverse';
1154         }
1155         
1156         
1157         if (this.active || this.pressed === true) {
1158             cfg.cls += ' active';
1159         }
1160         
1161         if (this.disabled) {
1162             cfg.disabled = 'disabled';
1163         }
1164         
1165         if (this.items) {
1166             Roo.log('changing to ul' );
1167             cfg.tag = 'ul';
1168             this.glyphicon = 'caret';
1169             if (Roo.bootstrap.version == 4) {
1170                 this.fa = 'caret-down';
1171             }
1172             
1173         }
1174         
1175         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176          
1177         //gsRoo.log(this.parentType);
1178         if (this.parentType === 'Navbar' && !this.parent().bar) {
1179             Roo.log('changing to li?');
1180             
1181             cfg.tag = 'li';
1182             
1183             cfg.cls = '';
1184             cfg.cn =  [{
1185                 tag : 'a',
1186                 cls : 'roo-button',
1187                 html : this.html,
1188                 href : this.href || '#'
1189             }];
1190             if (this.menu) {
1191                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1192                 cfg.cls += ' dropdown';
1193             }   
1194             
1195             delete cfg.html;
1196             
1197         }
1198         
1199        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1200         
1201         if (this.glyphicon) {
1202             cfg.html = ' ' + cfg.html;
1203             
1204             cfg.cn = [
1205                 {
1206                     tag: 'span',
1207                     cls: 'glyphicon glyphicon-' + this.glyphicon
1208                 }
1209             ];
1210         }
1211         if (this.fa) {
1212             cfg.html = ' ' + cfg.html;
1213             
1214             cfg.cn = [
1215                 {
1216                     tag: 'i',
1217                     cls: 'fa fas fa-' + this.fa
1218                 }
1219             ];
1220         }
1221         
1222         if (this.badge) {
1223             cfg.html += ' ';
1224             
1225             cfg.tag = 'a';
1226             
1227 //            cfg.cls='btn roo-button';
1228             
1229             cfg.href=this.href;
1230             
1231             var value = cfg.html;
1232             
1233             if(this.glyphicon){
1234                 value = {
1235                     tag: 'span',
1236                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1237                     html: this.html
1238                 };
1239             }
1240             if(this.fa){
1241                 value = {
1242                     tag: 'i',
1243                     cls: 'fa fas fa-' + this.fa,
1244                     html: this.html
1245                 };
1246             }
1247             
1248             var bw = this.badge_weight.length ? this.badge_weight :
1249                 (this.weight.length ? this.weight : 'secondary');
1250             bw = bw == 'default' ? 'secondary' : bw;
1251             
1252             cfg.cn = [
1253                 value,
1254                 {
1255                     tag: 'span',
1256                     cls: 'badge badge-' + bw,
1257                     html: this.badge
1258                 }
1259             ];
1260             
1261             cfg.html='';
1262         }
1263         
1264         if (this.menu) {
1265             cfg.cls += ' dropdown';
1266             cfg.html = typeof(cfg.html) != 'undefined' ?
1267                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1268         }
1269         
1270         if (cfg.tag !== 'a' && this.href !== '') {
1271             throw "Tag must be a to set href.";
1272         } else if (this.href.length > 0) {
1273             cfg.href = this.href;
1274         }
1275         
1276         if(this.removeClass){
1277             cfg.cls = '';
1278         }
1279         
1280         if(this.target){
1281             cfg.target = this.target;
1282         }
1283         
1284         return cfg;
1285     },
1286     initEvents: function() {
1287        // Roo.log('init events?');
1288 //        Roo.log(this.el.dom);
1289         // add the menu...
1290         
1291         if (typeof (this.menu) != 'undefined') {
1292             this.menu.parentType = this.xtype;
1293             this.menu.triggerEl = this.el;
1294             this.addxtype(Roo.apply({}, this.menu));
1295         }
1296
1297
1298         if (this.el.hasClass('roo-button')) {
1299              this.el.on('click', this.onClick, this);
1300              this.el.on('dblclick', this.onDblClick, this);
1301         } else {
1302              this.el.select('.roo-button').on('click', this.onClick, this);
1303              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1304              
1305         }
1306         // why?
1307         if(this.removeClass){
1308             this.el.on('click', this.onClick, this);
1309         }
1310         
1311         if (this.group === true) {
1312              if (this.pressed === false || this.pressed === true) {
1313                 // nothing
1314             } else {
1315                 this.pressed = false;
1316                 this.setActive(this.pressed);
1317             }
1318             
1319         }
1320         
1321         this.el.enableDisplayMode();
1322         
1323     },
1324     onClick : function(e)
1325     {
1326         if (this.disabled) {
1327             return;
1328         }
1329         
1330         Roo.log('button on click ');
1331         if(this.preventDefault){
1332             e.preventDefault();
1333         }
1334         
1335         if (this.group) {
1336             if (this.pressed) {
1337                 // do nothing -
1338                 return;
1339             }
1340             this.setActive(true);
1341             var pi = this.parent().items;
1342             for (var i = 0;i < pi.length;i++) {
1343                 if (this == pi[i]) {
1344                     continue;
1345                 }
1346                 if (pi[i].el.hasClass('roo-button')) {
1347                     pi[i].setActive(false);
1348                 }
1349             }
1350             this.fireEvent('click', this, e);            
1351             return;
1352         }
1353         
1354         if (this.pressed === true || this.pressed === false) {
1355             this.toggleActive(e);
1356         }
1357         
1358         
1359         this.fireEvent('click', this, e);
1360     },
1361     onDblClick: function(e)
1362     {
1363         if (this.disabled) {
1364             return;
1365         }
1366         if(this.preventDefault){
1367             e.preventDefault();
1368         }
1369         this.fireEvent('dblclick', this, e);
1370     },
1371     /**
1372      * Enables this button
1373      */
1374     enable : function()
1375     {
1376         this.disabled = false;
1377         this.el.removeClass('disabled');
1378         this.el.dom.removeAttribute("disabled");
1379     },
1380     
1381     /**
1382      * Disable this button
1383      */
1384     disable : function()
1385     {
1386         this.disabled = true;
1387         this.el.addClass('disabled');
1388         this.el.attr("disabled", "disabled")
1389     },
1390      /**
1391      * sets the active state on/off, 
1392      * @param {Boolean} state (optional) Force a particular state
1393      */
1394     setActive : function(v) {
1395         
1396         this.el[v ? 'addClass' : 'removeClass']('active');
1397         this.pressed = v;
1398     },
1399      /**
1400      * toggles the current active state 
1401      */
1402     toggleActive : function(e)
1403     {
1404         this.setActive(!this.pressed); // this modifies pressed...
1405         this.fireEvent('toggle', this, e, this.pressed);
1406     },
1407      /**
1408      * get the current active state
1409      * @return {boolean} true if it's active
1410      */
1411     isActive : function()
1412     {
1413         return this.el.hasClass('active');
1414     },
1415     /**
1416      * set the text of the first selected button
1417      */
1418     setText : function(str)
1419     {
1420         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1421     },
1422     /**
1423      * get the text of the first selected button
1424      */
1425     getText : function()
1426     {
1427         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1428     },
1429     
1430     setWeight : function(str)
1431     {
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1433         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434         this.weight = str;
1435         var outline = this.outline ? 'outline-' : '';
1436         if (str == 'default') {
1437             this.el.addClass('btn-default btn-outline-secondary');        
1438             return;
1439         }
1440         this.el.addClass('btn-' + outline + str);        
1441     }
1442     
1443     
1444 });
1445 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446
1447 Roo.bootstrap.Button.weights = [
1448     'default',
1449     'secondary' ,
1450     'primary',
1451     'success',
1452     'info',
1453     'warning',
1454     'danger',
1455     'link',
1456     'light',
1457     'dark'              
1458    
1459 ];/*
1460  * - LGPL
1461  *
1462  * column
1463  * 
1464  */
1465
1466 /**
1467  * @class Roo.bootstrap.Column
1468  * @extends Roo.bootstrap.Component
1469  * Bootstrap Column class
1470  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1471  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1472  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1473  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1474  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1475  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1476  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1477  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1478  *
1479  * 
1480  * @cfg {Boolean} hidden (true|false) hide the element
1481  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1482  * @cfg {String} fa (ban|check|...) font awesome icon
1483  * @cfg {Number} fasize (1|2|....) font awsome size
1484
1485  * @cfg {String} icon (info-sign|check|...) glyphicon name
1486
1487  * @cfg {String} html content of column.
1488  * 
1489  * @constructor
1490  * Create a new Column
1491  * @param {Object} config The config object
1492  */
1493
1494 Roo.bootstrap.Column = function(config){
1495     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1496 };
1497
1498 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1499     
1500     xs: false,
1501     sm: false,
1502     md: false,
1503     lg: false,
1504     xsoff: false,
1505     smoff: false,
1506     mdoff: false,
1507     lgoff: false,
1508     html: '',
1509     offset: 0,
1510     alert: false,
1511     fa: false,
1512     icon : false,
1513     hidden : false,
1514     fasize : 1,
1515     
1516     getAutoCreate : function(){
1517         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1518         
1519         cfg = {
1520             tag: 'div',
1521             cls: 'column'
1522         };
1523         
1524         var settings=this;
1525         var sizes =   ['xs','sm','md','lg'];
1526         sizes.map(function(size ,ix){
1527             //Roo.log( size + ':' + settings[size]);
1528             
1529             if (settings[size+'off'] !== false) {
1530                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1531             }
1532             
1533             if (settings[size] === false) {
1534                 return;
1535             }
1536             
1537             if (!settings[size]) { // 0 = hidden
1538                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539                 // bootsrap4
1540                 for (var i = ix; i > -1; i--) {
1541                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1542                 }
1543                 
1544                 
1545                 return;
1546             }
1547             cfg.cls += ' col-' + size + '-' + settings[size] + (
1548                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1549             );
1550             
1551         });
1552         
1553         if (this.hidden) {
1554             cfg.cls += ' hidden';
1555         }
1556         
1557         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1558             cfg.cls +=' alert alert-' + this.alert;
1559         }
1560         
1561         
1562         if (this.html.length) {
1563             cfg.html = this.html;
1564         }
1565         if (this.fa) {
1566             var fasize = '';
1567             if (this.fasize > 1) {
1568                 fasize = ' fa-' + this.fasize + 'x';
1569             }
1570             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1571             
1572             
1573         }
1574         if (this.icon) {
1575             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1576         }
1577         
1578         return cfg;
1579     }
1580    
1581 });
1582
1583  
1584
1585  /*
1586  * - LGPL
1587  *
1588  * page container.
1589  * 
1590  */
1591
1592
1593 /**
1594  * @class Roo.bootstrap.Container
1595  * @extends Roo.bootstrap.Component
1596  * Bootstrap Container class
1597  * @cfg {Boolean} jumbotron is it a jumbotron element
1598  * @cfg {String} html content of element
1599  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1600  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1601  * @cfg {String} header content of header (for panel)
1602  * @cfg {String} footer content of footer (for panel)
1603  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1604  * @cfg {String} tag (header|aside|section) type of HTML tag.
1605  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1606  * @cfg {String} fa font awesome icon
1607  * @cfg {String} icon (info-sign|check|...) glyphicon name
1608  * @cfg {Boolean} hidden (true|false) hide the element
1609  * @cfg {Boolean} expandable (true|false) default false
1610  * @cfg {Boolean} expanded (true|false) default true
1611  * @cfg {String} rheader contet on the right of header
1612  * @cfg {Boolean} clickable (true|false) default false
1613
1614  *     
1615  * @constructor
1616  * Create a new Container
1617  * @param {Object} config The config object
1618  */
1619
1620 Roo.bootstrap.Container = function(config){
1621     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1622     
1623     this.addEvents({
1624         // raw events
1625          /**
1626          * @event expand
1627          * After the panel has been expand
1628          * 
1629          * @param {Roo.bootstrap.Container} this
1630          */
1631         "expand" : true,
1632         /**
1633          * @event collapse
1634          * After the panel has been collapsed
1635          * 
1636          * @param {Roo.bootstrap.Container} this
1637          */
1638         "collapse" : true,
1639         /**
1640          * @event click
1641          * When a element is chick
1642          * @param {Roo.bootstrap.Container} this
1643          * @param {Roo.EventObject} e
1644          */
1645         "click" : true
1646     });
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1650     
1651     jumbotron : false,
1652     well: '',
1653     panel : '',
1654     header: '',
1655     footer : '',
1656     sticky: '',
1657     tag : false,
1658     alert : false,
1659     fa: false,
1660     icon : false,
1661     expandable : false,
1662     rheader : '',
1663     expanded : true,
1664     clickable: false,
1665   
1666      
1667     getChildContainer : function() {
1668         
1669         if(!this.el){
1670             return false;
1671         }
1672         
1673         if (this.panel.length) {
1674             return this.el.select('.panel-body',true).first();
1675         }
1676         
1677         return this.el;
1678     },
1679     
1680     
1681     getAutoCreate : function(){
1682         
1683         var cfg = {
1684             tag : this.tag || 'div',
1685             html : '',
1686             cls : ''
1687         };
1688         if (this.jumbotron) {
1689             cfg.cls = 'jumbotron';
1690         }
1691         
1692         
1693         
1694         // - this is applied by the parent..
1695         //if (this.cls) {
1696         //    cfg.cls = this.cls + '';
1697         //}
1698         
1699         if (this.sticky.length) {
1700             
1701             var bd = Roo.get(document.body);
1702             if (!bd.hasClass('bootstrap-sticky')) {
1703                 bd.addClass('bootstrap-sticky');
1704                 Roo.select('html',true).setStyle('height', '100%');
1705             }
1706              
1707             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1708         }
1709         
1710         
1711         if (this.well.length) {
1712             switch (this.well) {
1713                 case 'lg':
1714                 case 'sm':
1715                     cfg.cls +=' well well-' +this.well;
1716                     break;
1717                 default:
1718                     cfg.cls +=' well';
1719                     break;
1720             }
1721         }
1722         
1723         if (this.hidden) {
1724             cfg.cls += ' hidden';
1725         }
1726         
1727         
1728         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1729             cfg.cls +=' alert alert-' + this.alert;
1730         }
1731         
1732         var body = cfg;
1733         
1734         if (this.panel.length) {
1735             cfg.cls += ' panel panel-' + this.panel;
1736             cfg.cn = [];
1737             if (this.header.length) {
1738                 
1739                 var h = [];
1740                 
1741                 if(this.expandable){
1742                     
1743                     cfg.cls = cfg.cls + ' expandable';
1744                     
1745                     h.push({
1746                         tag: 'i',
1747                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1748                     });
1749                     
1750                 }
1751                 
1752                 h.push(
1753                     {
1754                         tag: 'span',
1755                         cls : 'panel-title',
1756                         html : (this.expandable ? '&nbsp;' : '') + this.header
1757                     },
1758                     {
1759                         tag: 'span',
1760                         cls: 'panel-header-right',
1761                         html: this.rheader
1762                     }
1763                 );
1764                 
1765                 cfg.cn.push({
1766                     cls : 'panel-heading',
1767                     style : this.expandable ? 'cursor: pointer' : '',
1768                     cn : h
1769                 });
1770                 
1771             }
1772             
1773             body = false;
1774             cfg.cn.push({
1775                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1776                 html : this.html
1777             });
1778             
1779             
1780             if (this.footer.length) {
1781                 cfg.cn.push({
1782                     cls : 'panel-footer',
1783                     html : this.footer
1784                     
1785                 });
1786             }
1787             
1788         }
1789         
1790         if (body) {
1791             body.html = this.html || cfg.html;
1792             // prefix with the icons..
1793             if (this.fa) {
1794                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1795             }
1796             if (this.icon) {
1797                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1798             }
1799             
1800             
1801         }
1802         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1803             cfg.cls =  'container';
1804         }
1805         
1806         return cfg;
1807     },
1808     
1809     initEvents: function() 
1810     {
1811         if(this.expandable){
1812             var headerEl = this.headerEl();
1813         
1814             if(headerEl){
1815                 headerEl.on('click', this.onToggleClick, this);
1816             }
1817         }
1818         
1819         if(this.clickable){
1820             this.el.on('click', this.onClick, this);
1821         }
1822         
1823     },
1824     
1825     onToggleClick : function()
1826     {
1827         var headerEl = this.headerEl();
1828         
1829         if(!headerEl){
1830             return;
1831         }
1832         
1833         if(this.expanded){
1834             this.collapse();
1835             return;
1836         }
1837         
1838         this.expand();
1839     },
1840     
1841     expand : function()
1842     {
1843         if(this.fireEvent('expand', this)) {
1844             
1845             this.expanded = true;
1846             
1847             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848             
1849             this.el.select('.panel-body',true).first().removeClass('hide');
1850             
1851             var toggleEl = this.toggleEl();
1852
1853             if(!toggleEl){
1854                 return;
1855             }
1856
1857             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1858         }
1859         
1860     },
1861     
1862     collapse : function()
1863     {
1864         if(this.fireEvent('collapse', this)) {
1865             
1866             this.expanded = false;
1867             
1868             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1869             this.el.select('.panel-body',true).first().addClass('hide');
1870         
1871             var toggleEl = this.toggleEl();
1872
1873             if(!toggleEl){
1874                 return;
1875             }
1876
1877             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1878         }
1879     },
1880     
1881     toggleEl : function()
1882     {
1883         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1884             return;
1885         }
1886         
1887         return this.el.select('.panel-heading .fa',true).first();
1888     },
1889     
1890     headerEl : function()
1891     {
1892         if(!this.el || !this.panel.length || !this.header.length){
1893             return;
1894         }
1895         
1896         return this.el.select('.panel-heading',true).first()
1897     },
1898     
1899     bodyEl : function()
1900     {
1901         if(!this.el || !this.panel.length){
1902             return;
1903         }
1904         
1905         return this.el.select('.panel-body',true).first()
1906     },
1907     
1908     titleEl : function()
1909     {
1910         if(!this.el || !this.panel.length || !this.header.length){
1911             return;
1912         }
1913         
1914         return this.el.select('.panel-title',true).first();
1915     },
1916     
1917     setTitle : function(v)
1918     {
1919         var titleEl = this.titleEl();
1920         
1921         if(!titleEl){
1922             return;
1923         }
1924         
1925         titleEl.dom.innerHTML = v;
1926     },
1927     
1928     getTitle : function()
1929     {
1930         
1931         var titleEl = this.titleEl();
1932         
1933         if(!titleEl){
1934             return '';
1935         }
1936         
1937         return titleEl.dom.innerHTML;
1938     },
1939     
1940     setRightTitle : function(v)
1941     {
1942         var t = this.el.select('.panel-header-right',true).first();
1943         
1944         if(!t){
1945             return;
1946         }
1947         
1948         t.dom.innerHTML = v;
1949     },
1950     
1951     onClick : function(e)
1952     {
1953         e.preventDefault();
1954         
1955         this.fireEvent('click', this, e);
1956     }
1957 });
1958
1959  /*
1960  *  - LGPL
1961  *
1962  *  This is BS4's Card element.. - similar to our containers probably..
1963  * 
1964  */
1965 /**
1966  * @class Roo.bootstrap.Card
1967  * @extends Roo.bootstrap.Component
1968  * Bootstrap Card class
1969  *
1970  *
1971  * possible... may not be implemented..
1972  * @cfg {String} header_image  src url of image.
1973  * @cfg {String|Object} header
1974  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1975  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1976  * 
1977  * @cfg {String} title
1978  * @cfg {String} subtitle
1979  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1980  * @cfg {String} footer
1981  
1982  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983  * 
1984  * @cfg {String} margin (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1990  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991  *
1992  * @cfg {String} padding (0|1|2|3|4|5)
1993  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1994  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1995  * @cfg {String} padding_left (0|1|2|3|4|5)
1996  * @cfg {String} padding_right (0|1|2|3|4|5)
1997  * @cfg {String} padding_x (0|1|2|3|4|5)
1998  * @cfg {String} padding_y (0|1|2|3|4|5)
1999  *
2000  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005  
2006  * @config {Boolean} dragable  if this card can be dragged.
2007  * @config {String} drag_group  group for drag
2008  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2009  * @config {String} drop_group  group for drag
2010  * 
2011  * @config {Boolean} collapsable can the body be collapsed.
2012  * @config {Boolean} collapsed is the body collapsed when rendered...
2013  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2014  * @config {Boolean} rotated is the body rotated when rendered...
2015  * 
2016  * @constructor
2017  * Create a new Container
2018  * @param {Object} config The config object
2019  */
2020
2021 Roo.bootstrap.Card = function(config){
2022     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2023     
2024     this.addEvents({
2025          // raw events
2026         /**
2027          * @event drop
2028          * When a element a card is dropped
2029          * @param {Roo.bootstrap.Card} this
2030          *
2031          * 
2032          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2033          * @param {String} position 'above' or 'below'
2034          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2035         
2036          */
2037         'drop' : true,
2038          /**
2039          * @event rotate
2040          * When a element a card is rotate
2041          * @param {Roo.bootstrap.Card} this
2042          * @param {Roo.Element} n the node being dropped?
2043          * @param {Boolean} rotate status
2044          */
2045         'rotate' : true,
2046         /**
2047          * @event cardover
2048          * When a card element is dragged over ready to drop (return false to block dropable)
2049          * @param {Roo.bootstrap.Card} this
2050          * @param {Object} data from dragdrop 
2051          */
2052          'cardover' : true
2053          
2054     });
2055 };
2056
2057
2058 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2059     
2060     
2061     weight : '',
2062     
2063     margin: '', /// may be better in component?
2064     margin_top: '', 
2065     margin_bottom: '', 
2066     margin_left: '',
2067     margin_right: '',
2068     margin_x: '',
2069     margin_y: '',
2070     
2071     padding : '',
2072     padding_top: '', 
2073     padding_bottom: '', 
2074     padding_left: '',
2075     padding_right: '',
2076     padding_x: '',
2077     padding_y: '',
2078     
2079     display: '', 
2080     display_xs: '', 
2081     display_sm: '', 
2082     display_lg: '',
2083     display_xl: '',
2084  
2085     header_image  : '',
2086     header : '',
2087     header_size : 0,
2088     title : '',
2089     subtitle : '',
2090     html : '',
2091     footer: '',
2092
2093     collapsable : false,
2094     collapsed : false,
2095     rotateable : false,
2096     rotated : false,
2097     
2098     dragable : false,
2099     drag_group : false,
2100     dropable : false,
2101     drop_group : false,
2102     childContainer : false,
2103     dropEl : false, /// the dom placeholde element that indicates drop location.
2104     containerEl: false, // body container
2105     bodyEl: false, // card-body
2106     headerContainerEl : false, //
2107     headerEl : false,
2108     header_imageEl : false,
2109     
2110     
2111     layoutCls : function()
2112     {
2113         var cls = '';
2114         var t = this;
2115         Roo.log(this.margin_bottom.length);
2116         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2117             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118             
2119             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2120                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2121             }
2122             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2123                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2124             }
2125         });
2126         
2127         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2128             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2130             }
2131         });
2132         
2133         // more generic support?
2134         if (this.hidden) {
2135             cls += ' d-none';
2136         }
2137         
2138         return cls;
2139     },
2140  
2141        // Roo.log("Call onRender: " + this.xtype);
2142         /*  We are looking at something like this.
2143 <div class="card">
2144     <img src="..." class="card-img-top" alt="...">
2145     <div class="card-body">
2146         <h5 class="card-title">Card title</h5>
2147          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148
2149         >> this bit is really the body...
2150         <div> << we will ad dthis in hopefully it will not break shit.
2151         
2152         ** card text does not actually have any styling...
2153         
2154             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2155         
2156         </div> <<
2157           <a href="#" class="card-link">Card link</a>
2158           
2159     </div>
2160     <div class="card-footer">
2161         <small class="text-muted">Last updated 3 mins ago</small>
2162     </div>
2163 </div>
2164          */
2165     getAutoCreate : function(){
2166         
2167         var cfg = {
2168             tag : 'div',
2169             cls : 'card',
2170             cn : [ ]
2171         };
2172         
2173         if (this.weight.length && this.weight != 'light') {
2174             cfg.cls += ' text-white';
2175         } else {
2176             cfg.cls += ' text-dark'; // need as it's nested..
2177         }
2178         if (this.weight.length) {
2179             cfg.cls += ' bg-' + this.weight;
2180         }
2181         
2182         cfg.cls += ' ' + this.layoutCls(); 
2183         
2184         var hdr = false;
2185         var hdr_ctr = false;
2186         if (this.header.length) {
2187             hdr = {
2188                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2189                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2190                 cn : []
2191             };
2192             cfg.cn.push(hdr);
2193             hdr_ctr = hdr;
2194         } else {
2195             hdr = {
2196                 tag : 'div',
2197                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2198                 cn : []
2199             };
2200             cfg.cn.push(hdr);
2201             hdr_ctr = hdr;
2202         }
2203         if (this.collapsable) {
2204             hdr_ctr = {
2205             tag : 'a',
2206             cls : 'd-block user-select-none',
2207             cn: [
2208                     {
2209                         tag: 'i',
2210                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2211                     }
2212                    
2213                 ]
2214             };
2215             hdr.cn.push(hdr_ctr);
2216         }
2217         
2218         hdr_ctr.cn.push(        {
2219             tag: 'span',
2220             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2221             html : this.header
2222         });
2223         
2224         
2225         if (this.header_image.length) {
2226             cfg.cn.push({
2227                 tag : 'img',
2228                 cls : 'card-img-top',
2229                 src: this.header_image // escape?
2230             });
2231         } else {
2232             cfg.cn.push({
2233                     tag : 'div',
2234                     cls : 'card-img-top d-none' 
2235                 });
2236         }
2237             
2238         var body = {
2239             tag : 'div',
2240             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2241             cn : []
2242         };
2243         var obody = body;
2244         if (this.collapsable || this.rotateable) {
2245             obody = {
2246                 tag: 'div',
2247                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2248                 cn : [  body ]
2249             };
2250         }
2251         
2252         cfg.cn.push(obody);
2253         
2254         if (this.title.length) {
2255             body.cn.push({
2256                 tag : 'div',
2257                 cls : 'card-title',
2258                 src: this.title // escape?
2259             });
2260         }  
2261         
2262         if (this.subtitle.length) {
2263             body.cn.push({
2264                 tag : 'div',
2265                 cls : 'card-title',
2266                 src: this.subtitle // escape?
2267             });
2268         }
2269         
2270         body.cn.push({
2271             tag : 'div',
2272             cls : 'roo-card-body-ctr'
2273         });
2274         
2275         if (this.html.length) {
2276             body.cn.push({
2277                 tag: 'div',
2278                 html : this.html
2279             });
2280         }
2281         // fixme ? handle objects?
2282         
2283         if (this.footer.length) {
2284            
2285             cfg.cn.push({
2286                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2287                 html : this.footer
2288             });
2289             
2290         } else {
2291             cfg.cn.push({cls : 'card-footer d-none'});
2292         }
2293         
2294         // footer...
2295         
2296         return cfg;
2297     },
2298     
2299     
2300     getCardHeader : function()
2301     {
2302         var  ret = this.el.select('.card-header',true).first();
2303         if (ret.hasClass('d-none')) {
2304             ret.removeClass('d-none');
2305         }
2306         
2307         return ret;
2308     },
2309     getCardFooter : function()
2310     {
2311         var  ret = this.el.select('.card-footer',true).first();
2312         if (ret.hasClass('d-none')) {
2313             ret.removeClass('d-none');
2314         }
2315         
2316         return ret;
2317     },
2318     getCardImageTop : function()
2319     {
2320         var  ret = this.header_imageEl;
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324             
2325         return ret;
2326     },
2327     
2328     getChildContainer : function()
2329     {
2330         
2331         if(!this.el){
2332             return false;
2333         }
2334         return this.el.select('.roo-card-body-ctr',true).first();    
2335     },
2336     
2337     initEvents: function() 
2338     {
2339         this.bodyEl = this.el.select('.card-body',true).first(); 
2340         this.containerEl = this.getChildContainer();
2341         if(this.dragable){
2342             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2343                     containerScroll: true,
2344                     ddGroup: this.drag_group || 'default_card_drag_group'
2345             });
2346             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347         }
2348         if (this.dropable) {
2349             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2350                 containerScroll: true,
2351                 ddGroup: this.drop_group || 'default_card_drag_group'
2352             });
2353             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2354             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2355             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2356             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2357             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2358         }
2359         
2360         if (this.collapsable) {
2361             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362         }
2363         if (this.rotateable) {
2364             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365         }
2366         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367          
2368         this.footerEl = this.el.select('.card-footer',true).first();
2369         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2370         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2371         this.headerEl = this.el.select('.card-header',true).first();
2372         
2373         if (this.rotated) {
2374             this.el.addClass('roo-card-rotated');
2375             this.fireEvent('rotate', this, true);
2376         }
2377         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2378         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2379         
2380     },
2381     getDragData : function(e)
2382     {
2383         var target = this.getEl();
2384         if (target) {
2385             //this.handleSelection(e);
2386             
2387             var dragData = {
2388                 source: this,
2389                 copy: false,
2390                 nodes: this.getEl(),
2391                 records: []
2392             };
2393             
2394             
2395             dragData.ddel = target.dom ;    // the div element
2396             Roo.log(target.getWidth( ));
2397             dragData.ddel.style.width = target.getWidth() + 'px';
2398             
2399             return dragData;
2400         }
2401         return false;
2402     },
2403     /**
2404     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2405     *    whole Element becomes the target, and this causes the drop gesture to append.
2406     *
2407     *    Returns an object:
2408     *     {
2409            
2410            position : 'below' or 'above'
2411            card  : relateive to card OBJECT (or true for no cards listed)
2412            items_n : relative to nth item in list
2413            card_n : relative to  nth card in list
2414     }
2415     *
2416     *    
2417     */
2418     getTargetFromEvent : function(e, dragged_card_el)
2419     {
2420         var target = e.getTarget();
2421         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2422             target = target.parentNode;
2423         }
2424         
2425         var ret = {
2426             position: '',
2427             cards : [],
2428             card_n : -1,
2429             items_n : -1,
2430             card : false 
2431         };
2432         
2433         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2434         // see if target is one of the 'cards'...
2435         
2436         
2437         //Roo.log(this.items.length);
2438         var pos = false;
2439         
2440         var last_card_n = 0;
2441         var cards_len  = 0;
2442         for (var i = 0;i< this.items.length;i++) {
2443             
2444             if (!this.items[i].el.hasClass('card')) {
2445                  continue;
2446             }
2447             pos = this.getDropPoint(e, this.items[i].el.dom);
2448             
2449             cards_len = ret.cards.length;
2450             //Roo.log(this.items[i].el.dom.id);
2451             ret.cards.push(this.items[i]);
2452             last_card_n  = i;
2453             if (ret.card_n < 0 && pos == 'above') {
2454                 ret.position = cards_len > 0 ? 'below' : pos;
2455                 ret.items_n = i > 0 ? i - 1 : 0;
2456                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2457                 ret.card = ret.cards[ret.card_n];
2458             }
2459         }
2460         if (!ret.cards.length) {
2461             ret.card = true;
2462             ret.position = 'below';
2463             ret.items_n;
2464             return ret;
2465         }
2466         // could not find a card.. stick it at the end..
2467         if (ret.card_n < 0) {
2468             ret.card_n = last_card_n;
2469             ret.card = ret.cards[last_card_n];
2470             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2471             ret.position = 'below';
2472         }
2473         
2474         if (this.items[ret.items_n].el == dragged_card_el) {
2475             return false;
2476         }
2477         
2478         if (ret.position == 'below') {
2479             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480             
2481             if (card_after  && card_after.el == dragged_card_el) {
2482                 return false;
2483             }
2484             return ret;
2485         }
2486         
2487         // its's after ..
2488         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489         
2490         if (card_before  && card_before.el == dragged_card_el) {
2491             return false;
2492         }
2493         
2494         return ret;
2495     },
2496     
2497     onNodeEnter : function(n, dd, e, data){
2498         return false;
2499     },
2500     onNodeOver : function(n, dd, e, data)
2501     {
2502        
2503         var target_info = this.getTargetFromEvent(e,data.source.el);
2504         if (target_info === false) {
2505             this.dropPlaceHolder('hide');
2506             return false;
2507         }
2508         Roo.log(['getTargetFromEvent', target_info ]);
2509         
2510         
2511         if (this.fireEvent('cardover', this, [ data ]) === false) {
2512             return false;
2513         }
2514         
2515         this.dropPlaceHolder('show', target_info,data);
2516         
2517         return false; 
2518     },
2519     onNodeOut : function(n, dd, e, data){
2520         this.dropPlaceHolder('hide');
2521      
2522     },
2523     onNodeDrop : function(n, dd, e, data)
2524     {
2525         
2526         // call drop - return false if
2527         
2528         // this could actually fail - if the Network drops..
2529         // we will ignore this at present..- client should probably reload
2530         // the whole set of cards if stuff like that fails.
2531         
2532         
2533         var info = this.getTargetFromEvent(e,data.source.el);
2534         if (info === false) {
2535             return false;
2536         }
2537         this.dropPlaceHolder('hide');
2538   
2539           
2540     
2541         this.acceptCard(data.source, info.position, info.card, info.items_n);
2542         return true;
2543          
2544     },
2545     firstChildCard : function()
2546     {
2547         for (var i = 0;i< this.items.length;i++) {
2548             
2549             if (!this.items[i].el.hasClass('card')) {
2550                  continue;
2551             }
2552             return this.items[i];
2553         }
2554         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2555     },
2556     /**
2557      * accept card
2558      *
2559      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2560      */
2561     acceptCard : function(move_card,  position, next_to_card )
2562     {
2563         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2564             return false;
2565         }
2566         
2567         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568         
2569         move_card.parent().removeCard(move_card);
2570         
2571         
2572         var dom = move_card.el.dom;
2573         dom.style.width = ''; // clear with - which is set by drag.
2574         
2575         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2576             var cardel = next_to_card.el.dom;
2577             
2578             if (position == 'above' ) {
2579                 cardel.parentNode.insertBefore(dom, cardel);
2580             } else if (cardel.nextSibling) {
2581                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582             } else {
2583                 cardel.parentNode.append(dom);
2584             }
2585         } else {
2586             // card container???
2587             this.containerEl.dom.append(dom);
2588         }
2589         
2590         //FIXME HANDLE card = true 
2591         
2592         // add this to the correct place in items.
2593         
2594         // remove Card from items.
2595         
2596        
2597         if (this.items.length) {
2598             var nitems = [];
2599             //Roo.log([info.items_n, info.position, this.items.length]);
2600             for (var i =0; i < this.items.length; i++) {
2601                 if (i == to_items_n && position == 'above') {
2602                     nitems.push(move_card);
2603                 }
2604                 nitems.push(this.items[i]);
2605                 if (i == to_items_n && position == 'below') {
2606                     nitems.push(move_card);
2607                 }
2608             }
2609             this.items = nitems;
2610             Roo.log(this.items);
2611         } else {
2612             this.items.push(move_card);
2613         }
2614         
2615         move_card.parentId = this.id;
2616         
2617         return true;
2618         
2619         
2620     },
2621     removeCard : function(c)
2622     {
2623         this.items = this.items.filter(function(e) { return e != c });
2624  
2625         var dom = c.el.dom;
2626         dom.parentNode.removeChild(dom);
2627         dom.style.width = ''; // clear with - which is set by drag.
2628         c.parentId = false;
2629         
2630     },
2631     
2632     /**    Decide whether to drop above or below a View node. */
2633     getDropPoint : function(e, n, dd)
2634     {
2635         if (dd) {
2636              return false;
2637         }
2638         if (n == this.containerEl.dom) {
2639             return "above";
2640         }
2641         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2642         var c = t + (b - t) / 2;
2643         var y = Roo.lib.Event.getPageY(e);
2644         if(y <= c) {
2645             return "above";
2646         }else{
2647             return "below";
2648         }
2649     },
2650     onToggleCollapse : function(e)
2651         {
2652         if (this.collapsed) {
2653             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2654             this.collapsableEl.addClass('show');
2655             this.collapsed = false;
2656             return;
2657         }
2658         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2659         this.collapsableEl.removeClass('show');
2660         this.collapsed = true;
2661         
2662     
2663     },
2664     
2665     onToggleRotate : function(e)
2666     {
2667         this.collapsableEl.removeClass('show');
2668         this.footerEl.removeClass('d-none');
2669         this.el.removeClass('roo-card-rotated');
2670         this.el.removeClass('d-none');
2671         if (this.rotated) {
2672             
2673             this.collapsableEl.addClass('show');
2674             this.rotated = false;
2675             this.fireEvent('rotate', this, this.rotated);
2676             return;
2677         }
2678         this.el.addClass('roo-card-rotated');
2679         this.footerEl.addClass('d-none');
2680         this.el.select('.roo-collapsable').removeClass('show');
2681         
2682         this.rotated = true;
2683         this.fireEvent('rotate', this, this.rotated);
2684     
2685     },
2686     
2687     dropPlaceHolder: function (action, info, data)
2688     {
2689         if (this.dropEl === false) {
2690             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2691             cls : 'd-none'
2692             },true);
2693         }
2694         this.dropEl.removeClass(['d-none', 'd-block']);        
2695         if (action == 'hide') {
2696             
2697             this.dropEl.addClass('d-none');
2698             return;
2699         }
2700         // FIXME - info.card == true!!!
2701         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702         
2703         if (info.card !== true) {
2704             var cardel = info.card.el.dom;
2705             
2706             if (info.position == 'above') {
2707                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2708             } else if (cardel.nextSibling) {
2709                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710             } else {
2711                 cardel.parentNode.append(this.dropEl.dom);
2712             }
2713         } else {
2714             // card container???
2715             this.containerEl.dom.append(this.dropEl.dom);
2716         }
2717         
2718         this.dropEl.addClass('d-block roo-card-dropzone');
2719         
2720         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2721         
2722         
2723     
2724     
2725     
2726     },
2727     setHeaderText: function(html)
2728     {
2729         this.header = html;
2730         if (this.headerContainerEl) {
2731             this.headerContainerEl.dom.innerHTML = html;
2732         }
2733     },
2734     onHeaderImageLoad : function(ev, he)
2735     {
2736         if (!this.header_image_fit_square) {
2737             return;
2738         }
2739         
2740         var hw = he.naturalHeight / he.naturalWidth;
2741         // wide image = < 0
2742         // tall image = > 1
2743         //var w = he.dom.naturalWidth;
2744         var ww = he.width;
2745         he.style.left =  0;
2746         he.style.position =  'relative';
2747         if (hw > 1) {
2748             var nw = (ww * (1/hw));
2749             Roo.get(he).setSize( ww * (1/hw),  ww);
2750             he.style.left =  ((ww - nw)/ 2) + 'px';
2751             he.style.position =  'relative';
2752         }
2753
2754     }
2755
2756     
2757 });
2758
2759 /*
2760  * - LGPL
2761  *
2762  * Card header - holder for the card header elements.
2763  * 
2764  */
2765
2766 /**
2767  * @class Roo.bootstrap.CardHeader
2768  * @extends Roo.bootstrap.Element
2769  * Bootstrap CardHeader class
2770  * @constructor
2771  * Create a new Card Header - that you can embed children into
2772  * @param {Object} config The config object
2773  */
2774
2775 Roo.bootstrap.CardHeader = function(config){
2776     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2777 };
2778
2779 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2780     
2781     
2782     container_method : 'getCardHeader' 
2783     
2784      
2785     
2786     
2787    
2788 });
2789
2790  
2791
2792  /*
2793  * - LGPL
2794  *
2795  * Card footer - holder for the card footer elements.
2796  * 
2797  */
2798
2799 /**
2800  * @class Roo.bootstrap.CardFooter
2801  * @extends Roo.bootstrap.Element
2802  * Bootstrap CardFooter class
2803  * @constructor
2804  * Create a new Card Footer - that you can embed children into
2805  * @param {Object} config The config object
2806  */
2807
2808 Roo.bootstrap.CardFooter = function(config){
2809     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2810 };
2811
2812 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2813     
2814     
2815     container_method : 'getCardFooter' 
2816     
2817      
2818     
2819     
2820    
2821 });
2822
2823  
2824
2825  /*
2826  * - LGPL
2827  *
2828  * Card header - holder for the card header elements.
2829  * 
2830  */
2831
2832 /**
2833  * @class Roo.bootstrap.CardImageTop
2834  * @extends Roo.bootstrap.Element
2835  * Bootstrap CardImageTop class
2836  * @constructor
2837  * Create a new Card Image Top container
2838  * @param {Object} config The config object
2839  */
2840
2841 Roo.bootstrap.CardImageTop = function(config){
2842     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2843 };
2844
2845 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2846     
2847    
2848     container_method : 'getCardImageTop' 
2849     
2850      
2851     
2852    
2853 });
2854
2855  
2856
2857  
2858 /*
2859 * Licence: LGPL
2860 */
2861
2862 /**
2863  * @class Roo.bootstrap.ButtonUploader
2864  * @extends Roo.bootstrap.Button
2865  * Bootstrap Button Uploader class - it's a button which when you add files to it
2866  *
2867  * 
2868  * @cfg {Number} errorTimeout default 3000
2869  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2870  * @cfg {Array}  html The button text.
2871  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2872  *
2873  * @constructor
2874  * Create a new CardUploader
2875  * @param {Object} config The config object
2876  */
2877
2878 Roo.bootstrap.ButtonUploader = function(config){
2879     
2880  
2881     
2882     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2883     
2884      
2885      this.addEvents({
2886          // raw events
2887         /**
2888          * @event beforeselect
2889          * When button is pressed, before show upload files dialog is shown
2890          * @param {Roo.bootstrap.UploaderButton} this
2891          *
2892          */
2893         'beforeselect' : true,
2894          /**
2895          * @event fired when files have been selected, 
2896          * When a the download link is clicked
2897          * @param {Roo.bootstrap.UploaderButton} this
2898          * @param {Array} Array of files that have been uploaded
2899          */
2900         'uploaded' : true
2901         
2902     });
2903 };
2904  
2905 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2906     
2907      
2908     errorTimeout : 3000,
2909      
2910     images : false,
2911    
2912     fileCollection : false,
2913     allowBlank : true,
2914     
2915     multiple : true,
2916     
2917     getAutoCreate : function()
2918     {
2919         var im = {
2920             tag: 'input',
2921             type : 'file',
2922             cls : 'd-none  roo-card-upload-selector' 
2923           
2924         };
2925         if (this.multiple) {
2926             im.multiple = 'multiple';
2927         }
2928         
2929         return  {
2930             cls :'div' ,
2931             cn : [
2932                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2933                 im
2934
2935             ]
2936         };
2937            
2938          
2939     },
2940      
2941    
2942     initEvents : function()
2943     {
2944         
2945         Roo.bootstrap.Button.prototype.initEvents.call(this);
2946         
2947         
2948         
2949         
2950         
2951         this.urlAPI = (window.createObjectURL && window) || 
2952                                 (window.URL && URL.revokeObjectURL && URL) || 
2953                                 (window.webkitURL && webkitURL);
2954                         
2955          
2956          
2957          
2958         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959         
2960         this.selectorEl.on('change', this.onFileSelected, this);
2961          
2962          
2963        
2964     },
2965     
2966    
2967     onClick : function(e)
2968     {
2969         e.preventDefault();
2970         
2971         if ( this.fireEvent('beforeselect', this) === false) {
2972             return;
2973         }
2974          
2975         this.selectorEl.dom.click();
2976          
2977     },
2978     
2979     onFileSelected : function(e)
2980     {
2981         e.preventDefault();
2982         
2983         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2984             return;
2985         }
2986         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2987         this.selectorEl.dom.value  = '';// hopefully reset..
2988         
2989         this.fireEvent('uploaded', this,  files );
2990         
2991     },
2992     
2993        
2994    
2995     
2996     /**
2997      * addCard - add an Attachment to the uploader
2998      * @param data - the data about the image to upload
2999      *
3000      * {
3001           id : 123
3002           title : "Title of file",
3003           is_uploaded : false,
3004           src : "http://.....",
3005           srcfile : { the File upload object },
3006           mimetype : file.type,
3007           preview : false,
3008           is_deleted : 0
3009           .. any other data...
3010         }
3011      *
3012      * 
3013     */
3014      
3015     reset: function()
3016     {
3017          
3018          this.selectorEl
3019     } 
3020     
3021     
3022     
3023     
3024 });
3025  /*
3026  * - LGPL
3027  *
3028  * image
3029  * 
3030  */
3031
3032
3033 /**
3034  * @class Roo.bootstrap.Img
3035  * @extends Roo.bootstrap.Component
3036  * Bootstrap Img class
3037  * @cfg {Boolean} imgResponsive false | true
3038  * @cfg {String} border rounded | circle | thumbnail
3039  * @cfg {String} src image source
3040  * @cfg {String} alt image alternative text
3041  * @cfg {String} href a tag href
3042  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3043  * @cfg {String} xsUrl xs image source
3044  * @cfg {String} smUrl sm image source
3045  * @cfg {String} mdUrl md image source
3046  * @cfg {String} lgUrl lg image source
3047  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3048  * 
3049  * @constructor
3050  * Create a new Input
3051  * @param {Object} config The config object
3052  */
3053
3054 Roo.bootstrap.Img = function(config){
3055     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3056     
3057     this.addEvents({
3058         // img events
3059         /**
3060          * @event click
3061          * The img click event for the img.
3062          * @param {Roo.EventObject} e
3063          */
3064         "click" : true,
3065         /**
3066          * @event load
3067          * The when any image loads
3068          * @param {Roo.EventObject} e
3069          */
3070         "load" : true
3071     });
3072 };
3073
3074 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3075     
3076     imgResponsive: true,
3077     border: '',
3078     src: 'about:blank',
3079     href: false,
3080     target: false,
3081     xsUrl: '',
3082     smUrl: '',
3083     mdUrl: '',
3084     lgUrl: '',
3085     backgroundContain : false,
3086
3087     getAutoCreate : function()
3088     {   
3089         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3090             return this.createSingleImg();
3091         }
3092         
3093         var cfg = {
3094             tag: 'div',
3095             cls: 'roo-image-responsive-group',
3096             cn: []
3097         };
3098         var _this = this;
3099         
3100         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101             
3102             if(!_this[size + 'Url']){
3103                 return;
3104             }
3105             
3106             var img = {
3107                 tag: 'img',
3108                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3109                 html: _this.html || cfg.html,
3110                 src: _this[size + 'Url']
3111             };
3112             
3113             img.cls += ' roo-image-responsive-' + size;
3114             
3115             var s = ['xs', 'sm', 'md', 'lg'];
3116             
3117             s.splice(s.indexOf(size), 1);
3118             
3119             Roo.each(s, function(ss){
3120                 img.cls += ' hidden-' + ss;
3121             });
3122             
3123             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3124                 cfg.cls += ' img-' + _this.border;
3125             }
3126             
3127             if(_this.alt){
3128                 cfg.alt = _this.alt;
3129             }
3130             
3131             if(_this.href){
3132                 var a = {
3133                     tag: 'a',
3134                     href: _this.href,
3135                     cn: [
3136                         img
3137                     ]
3138                 };
3139
3140                 if(this.target){
3141                     a.target = _this.target;
3142                 }
3143             }
3144             
3145             cfg.cn.push((_this.href) ? a : img);
3146             
3147         });
3148         
3149         return cfg;
3150     },
3151     
3152     createSingleImg : function()
3153     {
3154         var cfg = {
3155             tag: 'img',
3156             cls: (this.imgResponsive) ? 'img-responsive' : '',
3157             html : null,
3158             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3159         };
3160         
3161         if (this.backgroundContain) {
3162             cfg.cls += ' background-contain';
3163         }
3164         
3165         cfg.html = this.html || cfg.html;
3166         
3167         if (this.backgroundContain) {
3168             cfg.style="background-image: url(" + this.src + ')';
3169         } else {
3170             cfg.src = this.src || cfg.src;
3171         }
3172         
3173         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3174             cfg.cls += ' img-' + this.border;
3175         }
3176         
3177         if(this.alt){
3178             cfg.alt = this.alt;
3179         }
3180         
3181         if(this.href){
3182             var a = {
3183                 tag: 'a',
3184                 href: this.href,
3185                 cn: [
3186                     cfg
3187                 ]
3188             };
3189             
3190             if(this.target){
3191                 a.target = this.target;
3192             }
3193             
3194         }
3195         
3196         return (this.href) ? a : cfg;
3197     },
3198     
3199     initEvents: function() 
3200     {
3201         if(!this.href){
3202             this.el.on('click', this.onClick, this);
3203         }
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.on('load', this.onImageLoad, this);
3206         } else {
3207             // not sure if this works.. not tested
3208             this.el.select('img', true).on('load', this.onImageLoad, this);
3209         }
3210         
3211     },
3212     
3213     onClick : function(e)
3214     {
3215         Roo.log('img onclick');
3216         this.fireEvent('click', this, e);
3217     },
3218     onImageLoad: function(e)
3219     {
3220         Roo.log('img load');
3221         this.fireEvent('load', this, e);
3222     },
3223     
3224     /**
3225      * Sets the url of the image - used to update it
3226      * @param {String} url the url of the image
3227      */
3228     
3229     setSrc : function(url)
3230     {
3231         this.src =  url;
3232         
3233         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3234             if (this.backgroundContain) {
3235                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3236             } else {
3237                 this.el.dom.src =  url;
3238             }
3239             return;
3240         }
3241         
3242         this.el.select('img', true).first().dom.src =  url;
3243     }
3244     
3245     
3246    
3247 });
3248
3249  /*
3250  * - LGPL
3251  *
3252  * image
3253  * 
3254  */
3255
3256
3257 /**
3258  * @class Roo.bootstrap.Link
3259  * @extends Roo.bootstrap.Component
3260  * Bootstrap Link Class
3261  * @cfg {String} alt image alternative text
3262  * @cfg {String} href a tag href
3263  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3264  * @cfg {String} html the content of the link.
3265  * @cfg {String} anchor name for the anchor link
3266  * @cfg {String} fa - favicon
3267
3268  * @cfg {Boolean} preventDefault (true | false) default false
3269
3270  * 
3271  * @constructor
3272  * Create a new Input
3273  * @param {Object} config The config object
3274  */
3275
3276 Roo.bootstrap.Link = function(config){
3277     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3278     
3279     this.addEvents({
3280         // img events
3281         /**
3282          * @event click
3283          * The img click event for the img.
3284          * @param {Roo.EventObject} e
3285          */
3286         "click" : true
3287     });
3288 };
3289
3290 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3291     
3292     href: false,
3293     target: false,
3294     preventDefault: false,
3295     anchor : false,
3296     alt : false,
3297     fa: false,
3298
3299
3300     getAutoCreate : function()
3301     {
3302         var html = this.html || '';
3303         
3304         if (this.fa !== false) {
3305             html = '<i class="fa fa-' + this.fa + '"></i>';
3306         }
3307         var cfg = {
3308             tag: 'a'
3309         };
3310         // anchor's do not require html/href...
3311         if (this.anchor === false) {
3312             cfg.html = html;
3313             cfg.href = this.href || '#';
3314         } else {
3315             cfg.name = this.anchor;
3316             if (this.html !== false || this.fa !== false) {
3317                 cfg.html = html;
3318             }
3319             if (this.href !== false) {
3320                 cfg.href = this.href;
3321             }
3322         }
3323         
3324         if(this.alt !== false){
3325             cfg.alt = this.alt;
3326         }
3327         
3328         
3329         if(this.target !== false) {
3330             cfg.target = this.target;
3331         }
3332         
3333         return cfg;
3334     },
3335     
3336     initEvents: function() {
3337         
3338         if(!this.href || this.preventDefault){
3339             this.el.on('click', this.onClick, this);
3340         }
3341     },
3342     
3343     onClick : function(e)
3344     {
3345         if(this.preventDefault){
3346             e.preventDefault();
3347         }
3348         //Roo.log('img onclick');
3349         this.fireEvent('click', this, e);
3350     }
3351    
3352 });
3353
3354  /*
3355  * - LGPL
3356  *
3357  * header
3358  * 
3359  */
3360
3361 /**
3362  * @class Roo.bootstrap.Header
3363  * @extends Roo.bootstrap.Component
3364  * Bootstrap Header class
3365  * @cfg {String} html content of header
3366  * @cfg {Number} level (1|2|3|4|5|6) default 1
3367  * 
3368  * @constructor
3369  * Create a new Header
3370  * @param {Object} config The config object
3371  */
3372
3373
3374 Roo.bootstrap.Header  = function(config){
3375     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3376 };
3377
3378 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3379     
3380     //href : false,
3381     html : false,
3382     level : 1,
3383     
3384     
3385     
3386     getAutoCreate : function(){
3387         
3388         
3389         
3390         var cfg = {
3391             tag: 'h' + (1 *this.level),
3392             html: this.html || ''
3393         } ;
3394         
3395         return cfg;
3396     }
3397    
3398 });
3399
3400  
3401
3402  /*
3403  * Based on:
3404  * Ext JS Library 1.1.1
3405  * Copyright(c) 2006-2007, Ext JS, LLC.
3406  *
3407  * Originally Released Under LGPL - original licence link has changed is not relivant.
3408  *
3409  * Fork - LGPL
3410  * <script type="text/javascript">
3411  */
3412  
3413 /**
3414  * @class Roo.bootstrap.MenuMgr
3415  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3416  * @singleton
3417  */
3418 Roo.bootstrap.MenuMgr = function(){
3419    var menus, active, groups = {}, attached = false, lastShow = new Date();
3420
3421    // private - called when first menu is created
3422    function init(){
3423        menus = {};
3424        active = new Roo.util.MixedCollection();
3425        Roo.get(document).addKeyListener(27, function(){
3426            if(active.length > 0){
3427                hideAll();
3428            }
3429        });
3430    }
3431
3432    // private
3433    function hideAll(){
3434        if(active && active.length > 0){
3435            var c = active.clone();
3436            c.each(function(m){
3437                m.hide();
3438            });
3439        }
3440    }
3441
3442    // private
3443    function onHide(m){
3444        active.remove(m);
3445        if(active.length < 1){
3446            Roo.get(document).un("mouseup", onMouseDown);
3447             
3448            attached = false;
3449        }
3450    }
3451
3452    // private
3453    function onShow(m){
3454        var last = active.last();
3455        lastShow = new Date();
3456        active.add(m);
3457        if(!attached){
3458           Roo.get(document).on("mouseup", onMouseDown);
3459            
3460            attached = true;
3461        }
3462        if(m.parentMenu){
3463           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3464           m.parentMenu.activeChild = m;
3465        }else if(last && last.isVisible()){
3466           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3467        }
3468    }
3469
3470    // private
3471    function onBeforeHide(m){
3472        if(m.activeChild){
3473            m.activeChild.hide();
3474        }
3475        if(m.autoHideTimer){
3476            clearTimeout(m.autoHideTimer);
3477            delete m.autoHideTimer;
3478        }
3479    }
3480
3481    // private
3482    function onBeforeShow(m){
3483        var pm = m.parentMenu;
3484        if(!pm && !m.allowOtherMenus){
3485            hideAll();
3486        }else if(pm && pm.activeChild && active != m){
3487            pm.activeChild.hide();
3488        }
3489    }
3490
3491    // private this should really trigger on mouseup..
3492    function onMouseDown(e){
3493         Roo.log("on Mouse Up");
3494         
3495         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3496             Roo.log("MenuManager hideAll");
3497             hideAll();
3498             e.stopEvent();
3499         }
3500         
3501         
3502    }
3503
3504    // private
3505    function onBeforeCheck(mi, state){
3506        if(state){
3507            var g = groups[mi.group];
3508            for(var i = 0, l = g.length; i < l; i++){
3509                if(g[i] != mi){
3510                    g[i].setChecked(false);
3511                }
3512            }
3513        }
3514    }
3515
3516    return {
3517
3518        /**
3519         * Hides all menus that are currently visible
3520         */
3521        hideAll : function(){
3522             hideAll();  
3523        },
3524
3525        // private
3526        register : function(menu){
3527            if(!menus){
3528                init();
3529            }
3530            menus[menu.id] = menu;
3531            menu.on("beforehide", onBeforeHide);
3532            menu.on("hide", onHide);
3533            menu.on("beforeshow", onBeforeShow);
3534            menu.on("show", onShow);
3535            var g = menu.group;
3536            if(g && menu.events["checkchange"]){
3537                if(!groups[g]){
3538                    groups[g] = [];
3539                }
3540                groups[g].push(menu);
3541                menu.on("checkchange", onCheck);
3542            }
3543        },
3544
3545         /**
3546          * Returns a {@link Roo.menu.Menu} object
3547          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3548          * be used to generate and return a new Menu instance.
3549          */
3550        get : function(menu){
3551            if(typeof menu == "string"){ // menu id
3552                return menus[menu];
3553            }else if(menu.events){  // menu instance
3554                return menu;
3555            }
3556            /*else if(typeof menu.length == 'number'){ // array of menu items?
3557                return new Roo.bootstrap.Menu({items:menu});
3558            }else{ // otherwise, must be a config
3559                return new Roo.bootstrap.Menu(menu);
3560            }
3561            */
3562            return false;
3563        },
3564
3565        // private
3566        unregister : function(menu){
3567            delete menus[menu.id];
3568            menu.un("beforehide", onBeforeHide);
3569            menu.un("hide", onHide);
3570            menu.un("beforeshow", onBeforeShow);
3571            menu.un("show", onShow);
3572            var g = menu.group;
3573            if(g && menu.events["checkchange"]){
3574                groups[g].remove(menu);
3575                menu.un("checkchange", onCheck);
3576            }
3577        },
3578
3579        // private
3580        registerCheckable : function(menuItem){
3581            var g = menuItem.group;
3582            if(g){
3583                if(!groups[g]){
3584                    groups[g] = [];
3585                }
3586                groups[g].push(menuItem);
3587                menuItem.on("beforecheckchange", onBeforeCheck);
3588            }
3589        },
3590
3591        // private
3592        unregisterCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                groups[g].remove(menuItem);
3596                menuItem.un("beforecheckchange", onBeforeCheck);
3597            }
3598        }
3599    };
3600 }();/*
3601  * - LGPL
3602  *
3603  * menu
3604  * 
3605  */
3606
3607 /**
3608  * @class Roo.bootstrap.Menu
3609  * @extends Roo.bootstrap.Component
3610  * Bootstrap Menu class - container for MenuItems
3611  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3612  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3613  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3614  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3615   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3616   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3617  
3618  * @constructor
3619  * Create a new Menu
3620  * @param {Object} config The config object
3621  */
3622
3623
3624 Roo.bootstrap.Menu = function(config){
3625     
3626     if (config.type == 'treeview') {
3627         // normally menu's are drawn attached to the document to handle layering etc..
3628         // however treeview (used by the docs menu is drawn into the parent element)
3629         this.container_method = 'getChildContainer'; 
3630     }
3631     
3632     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3633     if (this.registerMenu && this.type != 'treeview')  {
3634         Roo.bootstrap.MenuMgr.register(this);
3635     }
3636     
3637     
3638     this.addEvents({
3639         /**
3640          * @event beforeshow
3641          * Fires before this menu is displayed (return false to block)
3642          * @param {Roo.menu.Menu} this
3643          */
3644         beforeshow : true,
3645         /**
3646          * @event beforehide
3647          * Fires before this menu is hidden (return false to block)
3648          * @param {Roo.menu.Menu} this
3649          */
3650         beforehide : true,
3651         /**
3652          * @event show
3653          * Fires after this menu is displayed
3654          * @param {Roo.menu.Menu} this
3655          */
3656         show : true,
3657         /**
3658          * @event hide
3659          * Fires after this menu is hidden
3660          * @param {Roo.menu.Menu} this
3661          */
3662         hide : true,
3663         /**
3664          * @event click
3665          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3666          * @param {Roo.menu.Menu} this
3667          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3668          * @param {Roo.EventObject} e
3669          */
3670         click : true,
3671         /**
3672          * @event mouseover
3673          * Fires when the mouse is hovering over this menu
3674          * @param {Roo.menu.Menu} this
3675          * @param {Roo.EventObject} e
3676          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3677          */
3678         mouseover : true,
3679         /**
3680          * @event mouseout
3681          * Fires when the mouse exits this menu
3682          * @param {Roo.menu.Menu} this
3683          * @param {Roo.EventObject} e
3684          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3685          */
3686         mouseout : true,
3687         /**
3688          * @event itemclick
3689          * Fires when a menu item contained in this menu is clicked
3690          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3691          * @param {Roo.EventObject} e
3692          */
3693         itemclick: true
3694     });
3695     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3696 };
3697
3698 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3699     
3700    /// html : false,
3701    
3702     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3703     type: false,
3704     /**
3705      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706      */
3707     registerMenu : true,
3708     
3709     menuItems :false, // stores the menu items..
3710     
3711     hidden:true,
3712         
3713     parentMenu : false,
3714     
3715     stopEvent : true,
3716     
3717     isLink : false,
3718     
3719     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720     
3721     hideTrigger : false,
3722     
3723     align : 'tl-bl?',
3724     
3725     
3726     getChildContainer : function() {
3727         return this.el;  
3728     },
3729     
3730     getAutoCreate : function(){
3731          
3732         //if (['right'].indexOf(this.align)!==-1) {
3733         //    cfg.cn[1].cls += ' pull-right'
3734         //}
3735          
3736         var cfg = {
3737             tag : 'ul',
3738             cls : 'dropdown-menu shadow' ,
3739             style : 'z-index:1000'
3740             
3741         };
3742         
3743         if (this.type === 'submenu') {
3744             cfg.cls = 'submenu active';
3745         }
3746         if (this.type === 'treeview') {
3747             cfg.cls = 'treeview-menu';
3748         }
3749         
3750         return cfg;
3751     },
3752     initEvents : function() {
3753         
3754        // Roo.log("ADD event");
3755        // Roo.log(this.triggerEl.dom);
3756         if (this.triggerEl) {
3757             
3758             this.triggerEl.on('click', this.onTriggerClick, this);
3759             
3760             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761             
3762             if (!this.hideTrigger) {
3763                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3764                     // dropdown toggle on the 'a' in BS4?
3765                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766                 } else {
3767                     this.triggerEl.addClass('dropdown-toggle');
3768                 }
3769             }
3770         }
3771         
3772         if (Roo.isTouch) {
3773             this.el.on('touchstart'  , this.onTouch, this);
3774         }
3775         this.el.on('click' , this.onClick, this);
3776
3777         this.el.on("mouseover", this.onMouseOver, this);
3778         this.el.on("mouseout", this.onMouseOut, this);
3779         
3780     },
3781     
3782     findTargetItem : function(e)
3783     {
3784         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3785         if(!t){
3786             return false;
3787         }
3788         //Roo.log(t);         Roo.log(t.id);
3789         if(t && t.id){
3790             //Roo.log(this.menuitems);
3791             return this.menuitems.get(t.id);
3792             
3793             //return this.items.get(t.menuItemId);
3794         }
3795         
3796         return false;
3797     },
3798     
3799     onTouch : function(e) 
3800     {
3801         Roo.log("menu.onTouch");
3802         //e.stopEvent(); this make the user popdown broken
3803         this.onClick(e);
3804     },
3805     
3806     onClick : function(e)
3807     {
3808         Roo.log("menu.onClick");
3809         
3810         var t = this.findTargetItem(e);
3811         if(!t || t.isContainer){
3812             return;
3813         }
3814         Roo.log(e);
3815         /*
3816         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3817             if(t == this.activeItem && t.shouldDeactivate(e)){
3818                 this.activeItem.deactivate();
3819                 delete this.activeItem;
3820                 return;
3821             }
3822             if(t.canActivate){
3823                 this.setActiveItem(t, true);
3824             }
3825             return;
3826             
3827             
3828         }
3829         */
3830        
3831         Roo.log('pass click event');
3832         
3833         t.onClick(e);
3834         
3835         this.fireEvent("click", this, t, e);
3836         
3837         var _this = this;
3838         
3839         if(!t.href.length || t.href == '#'){
3840             (function() { _this.hide(); }).defer(100);
3841         }
3842         
3843     },
3844     
3845     onMouseOver : function(e){
3846         var t  = this.findTargetItem(e);
3847         //Roo.log(t);
3848         //if(t){
3849         //    if(t.canActivate && !t.disabled){
3850         //        this.setActiveItem(t, true);
3851         //    }
3852         //}
3853         
3854         this.fireEvent("mouseover", this, e, t);
3855     },
3856     isVisible : function(){
3857         return !this.hidden;
3858     },
3859     onMouseOut : function(e){
3860         var t  = this.findTargetItem(e);
3861         
3862         //if(t ){
3863         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3864         //        this.activeItem.deactivate();
3865         //        delete this.activeItem;
3866         //    }
3867         //}
3868         this.fireEvent("mouseout", this, e, t);
3869     },
3870     
3871     
3872     /**
3873      * Displays this menu relative to another element
3874      * @param {String/HTMLElement/Roo.Element} element The element to align to
3875      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3876      * the element (defaults to this.defaultAlign)
3877      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878      */
3879     show : function(el, pos, parentMenu)
3880     {
3881         if (false === this.fireEvent("beforeshow", this)) {
3882             Roo.log("show canceled");
3883             return;
3884         }
3885         this.parentMenu = parentMenu;
3886         if(!this.el){
3887             this.render();
3888         }
3889         this.el.addClass('show'); // show otherwise we do not know how big we are..
3890          
3891         var xy = this.el.getAlignToXY(el, pos);
3892         
3893         // bl-tl << left align  below
3894         // tl-bl << left align 
3895         
3896         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3897             // if it goes to far to the right.. -> align left.
3898             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3899         }
3900         if(xy[0] < 0){
3901             // was left align - go right?
3902             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3903         }
3904         
3905         // goes down the bottom
3906         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907            xy[1]  < 0 ){
3908             var a = this.align.replace('?', '').split('-');
3909             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3910             
3911         }
3912         
3913         this.showAt(  xy , parentMenu, false);
3914     },
3915      /**
3916      * Displays this menu at a specific xy position
3917      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3918      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919      */
3920     showAt : function(xy, parentMenu, /* private: */_e){
3921         this.parentMenu = parentMenu;
3922         if(!this.el){
3923             this.render();
3924         }
3925         if(_e !== false){
3926             this.fireEvent("beforeshow", this);
3927             //xy = this.el.adjustForConstraints(xy);
3928         }
3929         
3930         //this.el.show();
3931         this.hideMenuItems();
3932         this.hidden = false;
3933         if (this.triggerEl) {
3934             this.triggerEl.addClass('open');
3935         }
3936         
3937         this.el.addClass('show');
3938         
3939         
3940         
3941         // reassign x when hitting right
3942         
3943         // reassign y when hitting bottom
3944         
3945         // but the list may align on trigger left or trigger top... should it be a properity?
3946         
3947         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3948             this.el.setXY(xy);
3949         }
3950         
3951         this.focus();
3952         this.fireEvent("show", this);
3953     },
3954     
3955     focus : function(){
3956         return;
3957         if(!this.hidden){
3958             this.doFocus.defer(50, this);
3959         }
3960     },
3961
3962     doFocus : function(){
3963         if(!this.hidden){
3964             this.focusEl.focus();
3965         }
3966     },
3967
3968     /**
3969      * Hides this menu and optionally all parent menus
3970      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971      */
3972     hide : function(deep)
3973     {
3974         if (false === this.fireEvent("beforehide", this)) {
3975             Roo.log("hide canceled");
3976             return;
3977         }
3978         this.hideMenuItems();
3979         if(this.el && this.isVisible()){
3980            
3981             if(this.activeItem){
3982                 this.activeItem.deactivate();
3983                 this.activeItem = null;
3984             }
3985             if (this.triggerEl) {
3986                 this.triggerEl.removeClass('open');
3987             }
3988             
3989             this.el.removeClass('show');
3990             this.hidden = true;
3991             this.fireEvent("hide", this);
3992         }
3993         if(deep === true && this.parentMenu){
3994             this.parentMenu.hide(true);
3995         }
3996     },
3997     
3998     onTriggerClick : function(e)
3999     {
4000         Roo.log('trigger click');
4001         
4002         var target = e.getTarget();
4003         
4004         Roo.log(target.nodeName.toLowerCase());
4005         
4006         if(target.nodeName.toLowerCase() === 'i'){
4007             e.preventDefault();
4008         }
4009         
4010     },
4011     
4012     onTriggerPress  : function(e)
4013     {
4014         Roo.log('trigger press');
4015         //Roo.log(e.getTarget());
4016        // Roo.log(this.triggerEl.dom);
4017        
4018         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4019         var pel = Roo.get(e.getTarget());
4020         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4021             Roo.log('is treeview or dropdown?');
4022             return;
4023         }
4024         
4025         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4026             return;
4027         }
4028         
4029         if (this.isVisible()) {
4030             Roo.log('hide');
4031             this.hide();
4032         } else {
4033             Roo.log('show');
4034             
4035             this.show(this.triggerEl, this.align, false);
4036         }
4037         
4038         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4039             e.stopEvent();
4040         }
4041         
4042     },
4043        
4044     
4045     hideMenuItems : function()
4046     {
4047         Roo.log("hide Menu Items");
4048         if (!this.el) { 
4049             return;
4050         }
4051         
4052         this.el.select('.open',true).each(function(aa) {
4053             
4054             aa.removeClass('open');
4055          
4056         });
4057     },
4058     addxtypeChild : function (tree, cntr) {
4059         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060           
4061         this.menuitems.add(comp);
4062         return comp;
4063
4064     },
4065     getEl : function()
4066     {
4067         Roo.log(this.el);
4068         return this.el;
4069     },
4070     
4071     clear : function()
4072     {
4073         this.getEl().dom.innerHTML = '';
4074         this.menuitems.clear();
4075     }
4076 });
4077
4078  
4079  /*
4080  * - LGPL
4081  *
4082  * menu item
4083  * 
4084  */
4085
4086
4087 /**
4088  * @class Roo.bootstrap.MenuItem
4089  * @extends Roo.bootstrap.Component
4090  * Bootstrap MenuItem class
4091  * @cfg {String} html the menu label
4092  * @cfg {String} href the link
4093  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4094  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4095  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4096  * @cfg {String} fa favicon to show on left of menu item.
4097  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4098  * 
4099  * 
4100  * @constructor
4101  * Create a new MenuItem
4102  * @param {Object} config The config object
4103  */
4104
4105
4106 Roo.bootstrap.MenuItem = function(config){
4107     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4108     this.addEvents({
4109         // raw events
4110         /**
4111          * @event click
4112          * The raw click event for the entire grid.
4113          * @param {Roo.bootstrap.MenuItem} this
4114          * @param {Roo.EventObject} e
4115          */
4116         "click" : true
4117     });
4118 };
4119
4120 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4121     
4122     href : false,
4123     html : false,
4124     preventDefault: false,
4125     isContainer : false,
4126     active : false,
4127     fa: false,
4128     
4129     getAutoCreate : function(){
4130         
4131         if(this.isContainer){
4132             return {
4133                 tag: 'li',
4134                 cls: 'dropdown-menu-item '
4135             };
4136         }
4137         var ctag = {
4138             tag: 'span',
4139             html: 'Link'
4140         };
4141         
4142         var anc = {
4143             tag : 'a',
4144             cls : 'dropdown-item',
4145             href : '#',
4146             cn : [  ]
4147         };
4148         
4149         if (this.fa !== false) {
4150             anc.cn.push({
4151                 tag : 'i',
4152                 cls : 'fa fa-' + this.fa
4153             });
4154         }
4155         
4156         anc.cn.push(ctag);
4157         
4158         
4159         var cfg= {
4160             tag: 'li',
4161             cls: 'dropdown-menu-item',
4162             cn: [ anc ]
4163         };
4164         if (this.parent().type == 'treeview') {
4165             cfg.cls = 'treeview-menu';
4166         }
4167         if (this.active) {
4168             cfg.cls += ' active';
4169         }
4170         
4171         
4172         
4173         anc.href = this.href || cfg.cn[0].href ;
4174         ctag.html = this.html || cfg.cn[0].html ;
4175         return cfg;
4176     },
4177     
4178     initEvents: function()
4179     {
4180         if (this.parent().type == 'treeview') {
4181             this.el.select('a').on('click', this.onClick, this);
4182         }
4183         
4184         if (this.menu) {
4185             this.menu.parentType = this.xtype;
4186             this.menu.triggerEl = this.el;
4187             this.menu = this.addxtype(Roo.apply({}, this.menu));
4188         }
4189         
4190     },
4191     onClick : function(e)
4192     {
4193         Roo.log('item on click ');
4194         
4195         if(this.preventDefault){
4196             e.preventDefault();
4197         }
4198         //this.parent().hideMenuItems();
4199         
4200         this.fireEvent('click', this, e);
4201     },
4202     getEl : function()
4203     {
4204         return this.el;
4205     } 
4206 });
4207
4208  
4209
4210  /*
4211  * - LGPL
4212  *
4213  * menu separator
4214  * 
4215  */
4216
4217
4218 /**
4219  * @class Roo.bootstrap.MenuSeparator
4220  * @extends Roo.bootstrap.Component
4221  * Bootstrap MenuSeparator class
4222  * 
4223  * @constructor
4224  * Create a new MenuItem
4225  * @param {Object} config The config object
4226  */
4227
4228
4229 Roo.bootstrap.MenuSeparator = function(config){
4230     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4231 };
4232
4233 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4234     
4235     getAutoCreate : function(){
4236         var cfg = {
4237             cls: 'divider',
4238             tag : 'li'
4239         };
4240         
4241         return cfg;
4242     }
4243    
4244 });
4245
4246  
4247
4248  
4249 /*
4250 * Licence: LGPL
4251 */
4252
4253 /**
4254  * @class Roo.bootstrap.Modal
4255  * @extends Roo.bootstrap.Component
4256  * Bootstrap Modal class
4257  * @cfg {String} title Title of dialog
4258  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4259  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4260  * @cfg {Boolean} specificTitle default false
4261  * @cfg {Array} buttons Array of buttons or standard button set..
4262  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4263  * @cfg {Boolean} animate default true
4264  * @cfg {Boolean} allow_close default true
4265  * @cfg {Boolean} fitwindow default false
4266  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4267  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4268  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4269  * @cfg {String} size (sm|lg|xl) default empty
4270  * @cfg {Number} max_width set the max width of modal
4271  * @cfg {Boolean} editableTitle can the title be edited
4272
4273  *
4274  *
4275  * @constructor
4276  * Create a new Modal Dialog
4277  * @param {Object} config The config object
4278  */
4279
4280 Roo.bootstrap.Modal = function(config){
4281     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4282     this.addEvents({
4283         // raw events
4284         /**
4285          * @event btnclick
4286          * The raw btnclick event for the button
4287          * @param {Roo.EventObject} e
4288          */
4289         "btnclick" : true,
4290         /**
4291          * @event resize
4292          * Fire when dialog resize
4293          * @param {Roo.bootstrap.Modal} this
4294          * @param {Roo.EventObject} e
4295          */
4296         "resize" : true,
4297         /**
4298          * @event titlechanged
4299          * Fire when the editable title has been changed
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} value
4302          */
4303         "titlechanged" : true 
4304         
4305     });
4306     this.buttons = this.buttons || [];
4307
4308     if (this.tmpl) {
4309         this.tmpl = Roo.factory(this.tmpl);
4310     }
4311
4312 };
4313
4314 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4315
4316     title : 'test dialog',
4317
4318     buttons : false,
4319
4320     // set on load...
4321
4322     html: false,
4323
4324     tmp: false,
4325
4326     specificTitle: false,
4327
4328     buttonPosition: 'right',
4329
4330     allow_close : true,
4331
4332     animate : true,
4333
4334     fitwindow: false,
4335     
4336      // private
4337     dialogEl: false,
4338     bodyEl:  false,
4339     footerEl:  false,
4340     titleEl:  false,
4341     closeEl:  false,
4342
4343     size: '',
4344     
4345     max_width: 0,
4346     
4347     max_height: 0,
4348     
4349     fit_content: false,
4350     editableTitle  : false,
4351
4352     onRender : function(ct, position)
4353     {
4354         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4355
4356         if(!this.el){
4357             var cfg = Roo.apply({},  this.getAutoCreate());
4358             cfg.id = Roo.id();
4359             //if(!cfg.name){
4360             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361             //}
4362             //if (!cfg.name.length) {
4363             //    delete cfg.name;
4364            // }
4365             if (this.cls) {
4366                 cfg.cls += ' ' + this.cls;
4367             }
4368             if (this.style) {
4369                 cfg.style = this.style;
4370             }
4371             this.el = Roo.get(document.body).createChild(cfg, position);
4372         }
4373         //var type = this.el.dom.type;
4374
4375
4376         if(this.tabIndex !== undefined){
4377             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4378         }
4379
4380         this.dialogEl = this.el.select('.modal-dialog',true).first();
4381         this.bodyEl = this.el.select('.modal-body',true).first();
4382         this.closeEl = this.el.select('.modal-header .close', true).first();
4383         this.headerEl = this.el.select('.modal-header',true).first();
4384         this.titleEl = this.el.select('.modal-title',true).first();
4385         this.footerEl = this.el.select('.modal-footer',true).first();
4386
4387         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388         
4389         //this.el.addClass("x-dlg-modal");
4390
4391         if (this.buttons.length) {
4392             Roo.each(this.buttons, function(bb) {
4393                 var b = Roo.apply({}, bb);
4394                 b.xns = b.xns || Roo.bootstrap;
4395                 b.xtype = b.xtype || 'Button';
4396                 if (typeof(b.listeners) == 'undefined') {
4397                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4398                 }
4399
4400                 var btn = Roo.factory(b);
4401
4402                 btn.render(this.getButtonContainer());
4403
4404             },this);
4405         }
4406         // render the children.
4407         var nitems = [];
4408
4409         if(typeof(this.items) != 'undefined'){
4410             var items = this.items;
4411             delete this.items;
4412
4413             for(var i =0;i < items.length;i++) {
4414                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4415             }
4416         }
4417
4418         this.items = nitems;
4419
4420         // where are these used - they used to be body/close/footer
4421
4422
4423         this.initEvents();
4424         //this.el.addClass([this.fieldClass, this.cls]);
4425
4426     },
4427
4428     getAutoCreate : function()
4429     {
4430         // we will default to modal-body-overflow - might need to remove or make optional later.
4431         var bdy = {
4432                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4433                 html : this.html || ''
4434         };
4435
4436         var title = {
4437             tag: 'h5',
4438             cls : 'modal-title',
4439             html : this.title
4440         };
4441
4442         if(this.specificTitle){ // WTF is this?
4443             title = this.title;
4444         }
4445
4446         var header = [];
4447         if (this.allow_close && Roo.bootstrap.version == 3) {
4448             header.push({
4449                 tag: 'button',
4450                 cls : 'close',
4451                 html : '&times'
4452             });
4453         }
4454
4455         header.push(title);
4456
4457         if (this.editableTitle) {
4458             header.push({
4459                 cls: 'form-control roo-editable-title d-none',
4460                 tag: 'input',
4461                 type: 'text'
4462             });
4463         }
4464         
4465         if (this.allow_close && Roo.bootstrap.version == 4) {
4466             header.push({
4467                 tag: 'button',
4468                 cls : 'close',
4469                 html : '&times'
4470             });
4471         }
4472         
4473         var size = '';
4474
4475         if(this.size.length){
4476             size = 'modal-' + this.size;
4477         }
4478         
4479         var footer = Roo.bootstrap.version == 3 ?
4480             {
4481                 cls : 'modal-footer',
4482                 cn : [
4483                     {
4484                         tag: 'div',
4485                         cls: 'btn-' + this.buttonPosition
4486                     }
4487                 ]
4488
4489             } :
4490             {  // BS4 uses mr-auto on left buttons....
4491                 cls : 'modal-footer'
4492             };
4493
4494             
4495
4496         
4497         
4498         var modal = {
4499             cls: "modal",
4500              cn : [
4501                 {
4502                     cls: "modal-dialog " + size,
4503                     cn : [
4504                         {
4505                             cls : "modal-content",
4506                             cn : [
4507                                 {
4508                                     cls : 'modal-header',
4509                                     cn : header
4510                                 },
4511                                 bdy,
4512                                 footer
4513                             ]
4514
4515                         }
4516                     ]
4517
4518                 }
4519             ]
4520         };
4521
4522         if(this.animate){
4523             modal.cls += ' fade';
4524         }
4525
4526         return modal;
4527
4528     },
4529     getChildContainer : function() {
4530
4531          return this.bodyEl;
4532
4533     },
4534     getButtonContainer : function() {
4535         
4536          return Roo.bootstrap.version == 4 ?
4537             this.el.select('.modal-footer',true).first()
4538             : this.el.select('.modal-footer div',true).first();
4539
4540     },
4541     initEvents : function()
4542     {
4543         if (this.allow_close) {
4544             this.closeEl.on('click', this.hide, this);
4545         }
4546         Roo.EventManager.onWindowResize(this.resize, this, true);
4547         if (this.editableTitle) {
4548             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4549             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4550             this.headerEditEl.on('keyup', function(e) {
4551                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4552                         this.toggleHeaderInput(false)
4553                     }
4554                 }, this);
4555             this.headerEditEl.on('blur', function(e) {
4556                 this.toggleHeaderInput(false)
4557             },this);
4558         }
4559
4560     },
4561   
4562
4563     resize : function()
4564     {
4565         this.maskEl.setSize(
4566             Roo.lib.Dom.getViewWidth(true),
4567             Roo.lib.Dom.getViewHeight(true)
4568         );
4569         
4570         if (this.fitwindow) {
4571             
4572            this.dialogEl.setStyle( { 'max-width' : '100%' });
4573             this.setSize(
4574                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4575                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4576             );
4577             return;
4578         }
4579         
4580         if(this.max_width !== 0) {
4581             
4582             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4583             
4584             if(this.height) {
4585                 this.setSize(w, this.height);
4586                 return;
4587             }
4588             
4589             if(this.max_height) {
4590                 this.setSize(w,Math.min(
4591                     this.max_height,
4592                     Roo.lib.Dom.getViewportHeight(true) - 60
4593                 ));
4594                 
4595                 return;
4596             }
4597             
4598             if(!this.fit_content) {
4599                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4600                 return;
4601             }
4602             
4603             this.setSize(w, Math.min(
4604                 60 +
4605                 this.headerEl.getHeight() + 
4606                 this.footerEl.getHeight() + 
4607                 this.getChildHeight(this.bodyEl.dom.childNodes),
4608                 Roo.lib.Dom.getViewportHeight(true) - 60)
4609             );
4610         }
4611         
4612     },
4613
4614     setSize : function(w,h)
4615     {
4616         if (!w && !h) {
4617             return;
4618         }
4619         
4620         this.resizeTo(w,h);
4621     },
4622
4623     show : function() {
4624
4625         if (!this.rendered) {
4626             this.render();
4627         }
4628         this.toggleHeaderInput(false);
4629         //this.el.setStyle('display', 'block');
4630         this.el.removeClass('hideing');
4631         this.el.dom.style.display='block';
4632         
4633         Roo.get(document.body).addClass('modal-open');
4634  
4635         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4636             
4637             (function(){
4638                 this.el.addClass('show');
4639                 this.el.addClass('in');
4640             }).defer(50, this);
4641         }else{
4642             this.el.addClass('show');
4643             this.el.addClass('in');
4644         }
4645
4646         // not sure how we can show data in here..
4647         //if (this.tmpl) {
4648         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4649         //}
4650
4651         Roo.get(document.body).addClass("x-body-masked");
4652         
4653         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4654         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4655         this.maskEl.dom.style.display = 'block';
4656         this.maskEl.addClass('show');
4657         
4658         
4659         this.resize();
4660         
4661         this.fireEvent('show', this);
4662
4663         // set zindex here - otherwise it appears to be ignored...
4664         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4665
4666         (function () {
4667             this.items.forEach( function(e) {
4668                 e.layout ? e.layout() : false;
4669
4670             });
4671         }).defer(100,this);
4672
4673     },
4674     hide : function()
4675     {
4676         if(this.fireEvent("beforehide", this) !== false){
4677             
4678             this.maskEl.removeClass('show');
4679             
4680             this.maskEl.dom.style.display = '';
4681             Roo.get(document.body).removeClass("x-body-masked");
4682             this.el.removeClass('in');
4683             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684
4685             if(this.animate){ // why
4686                 this.el.addClass('hideing');
4687                 this.el.removeClass('show');
4688                 (function(){
4689                     if (!this.el.hasClass('hideing')) {
4690                         return; // it's been shown again...
4691                     }
4692                     
4693                     this.el.dom.style.display='';
4694
4695                     Roo.get(document.body).removeClass('modal-open');
4696                     this.el.removeClass('hideing');
4697                 }).defer(150,this);
4698                 
4699             }else{
4700                 this.el.removeClass('show');
4701                 this.el.dom.style.display='';
4702                 Roo.get(document.body).removeClass('modal-open');
4703
4704             }
4705             this.fireEvent('hide', this);
4706         }
4707     },
4708     isVisible : function()
4709     {
4710         
4711         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4712         
4713     },
4714
4715     addButton : function(str, cb)
4716     {
4717
4718
4719         var b = Roo.apply({}, { html : str } );
4720         b.xns = b.xns || Roo.bootstrap;
4721         b.xtype = b.xtype || 'Button';
4722         if (typeof(b.listeners) == 'undefined') {
4723             b.listeners = { click : cb.createDelegate(this)  };
4724         }
4725
4726         var btn = Roo.factory(b);
4727
4728         btn.render(this.getButtonContainer());
4729
4730         return btn;
4731
4732     },
4733
4734     setDefaultButton : function(btn)
4735     {
4736         //this.el.select('.modal-footer').()
4737     },
4738
4739     resizeTo: function(w,h)
4740     {
4741         this.dialogEl.setWidth(w);
4742         
4743         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4744
4745         this.bodyEl.setHeight(h - diff);
4746         
4747         this.fireEvent('resize', this);
4748     },
4749     
4750     setContentSize  : function(w, h)
4751     {
4752
4753     },
4754     onButtonClick: function(btn,e)
4755     {
4756         //Roo.log([a,b,c]);
4757         this.fireEvent('btnclick', btn.name, e);
4758     },
4759      /**
4760      * Set the title of the Dialog
4761      * @param {String} str new Title
4762      */
4763     setTitle: function(str) {
4764         this.titleEl.dom.innerHTML = str;
4765         this.title = str;
4766     },
4767     /**
4768      * Set the body of the Dialog
4769      * @param {String} str new Title
4770      */
4771     setBody: function(str) {
4772         this.bodyEl.dom.innerHTML = str;
4773     },
4774     /**
4775      * Set the body of the Dialog using the template
4776      * @param {Obj} data - apply this data to the template and replace the body contents.
4777      */
4778     applyBody: function(obj)
4779     {
4780         if (!this.tmpl) {
4781             Roo.log("Error - using apply Body without a template");
4782             //code
4783         }
4784         this.tmpl.overwrite(this.bodyEl, obj);
4785     },
4786     
4787     getChildHeight : function(child_nodes)
4788     {
4789         if(
4790             !child_nodes ||
4791             child_nodes.length == 0
4792         ) {
4793             return 0;
4794         }
4795         
4796         var child_height = 0;
4797         
4798         for(var i = 0; i < child_nodes.length; i++) {
4799             
4800             /*
4801             * for modal with tabs...
4802             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803                 
4804                 var layout_childs = child_nodes[i].childNodes;
4805                 
4806                 for(var j = 0; j < layout_childs.length; j++) {
4807                     
4808                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809                         
4810                         var layout_body_childs = layout_childs[j].childNodes;
4811                         
4812                         for(var k = 0; k < layout_body_childs.length; k++) {
4813                             
4814                             if(layout_body_childs[k].classList.contains('navbar')) {
4815                                 child_height += layout_body_childs[k].offsetHeight;
4816                                 continue;
4817                             }
4818                             
4819                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820                                 
4821                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822                                 
4823                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824                                     
4825                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4826                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4827                                         continue;
4828                                     }
4829                                     
4830                                 }
4831                                 
4832                             }
4833                             
4834                         }
4835                     }
4836                 }
4837                 continue;
4838             }
4839             */
4840             
4841             child_height += child_nodes[i].offsetHeight;
4842             // Roo.log(child_nodes[i].offsetHeight);
4843         }
4844         
4845         return child_height;
4846     },
4847     toggleHeaderInput : function(is_edit)
4848     {
4849         if (!this.editableTitle) {
4850             return; // not editable.
4851         }
4852         if (is_edit && this.is_header_editing) {
4853             return; // already editing..
4854         }
4855         if (is_edit) {
4856     
4857             this.headerEditEl.dom.value = this.title;
4858             this.headerEditEl.removeClass('d-none');
4859             this.headerEditEl.dom.focus();
4860             this.titleEl.addClass('d-none');
4861             
4862             this.is_header_editing = true;
4863             return
4864         }
4865         // flip back to not editing.
4866         this.title = this.headerEditEl.dom.value;
4867         this.headerEditEl.addClass('d-none');
4868         this.titleEl.removeClass('d-none');
4869         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4870         this.is_header_editing = false;
4871         this.fireEvent('titlechanged', this, this.title);
4872     
4873             
4874         
4875     }
4876
4877 });
4878
4879
4880 Roo.apply(Roo.bootstrap.Modal,  {
4881     /**
4882          * Button config that displays a single OK button
4883          * @type Object
4884          */
4885         OK :  [{
4886             name : 'ok',
4887             weight : 'primary',
4888             html : 'OK'
4889         }],
4890         /**
4891          * Button config that displays Yes and No buttons
4892          * @type Object
4893          */
4894         YESNO : [
4895             {
4896                 name  : 'no',
4897                 html : 'No'
4898             },
4899             {
4900                 name  :'yes',
4901                 weight : 'primary',
4902                 html : 'Yes'
4903             }
4904         ],
4905
4906         /**
4907          * Button config that displays OK and Cancel buttons
4908          * @type Object
4909          */
4910         OKCANCEL : [
4911             {
4912                name : 'cancel',
4913                 html : 'Cancel'
4914             },
4915             {
4916                 name : 'ok',
4917                 weight : 'primary',
4918                 html : 'OK'
4919             }
4920         ],
4921         /**
4922          * Button config that displays Yes, No and Cancel buttons
4923          * @type Object
4924          */
4925         YESNOCANCEL : [
4926             {
4927                 name : 'yes',
4928                 weight : 'primary',
4929                 html : 'Yes'
4930             },
4931             {
4932                 name : 'no',
4933                 html : 'No'
4934             },
4935             {
4936                 name : 'cancel',
4937                 html : 'Cancel'
4938             }
4939         ],
4940         
4941         zIndex : 10001
4942 });
4943
4944 /*
4945  * - LGPL
4946  *
4947  * messagebox - can be used as a replace
4948  * 
4949  */
4950 /**
4951  * @class Roo.MessageBox
4952  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4953  * Example usage:
4954  *<pre><code>
4955 // Basic alert:
4956 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957
4958 // Prompt for user data:
4959 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960     if (btn == 'ok'){
4961         // process text value...
4962     }
4963 });
4964
4965 // Show a dialog using config options:
4966 Roo.Msg.show({
4967    title:'Save Changes?',
4968    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4969    buttons: Roo.Msg.YESNOCANCEL,
4970    fn: processResult,
4971    animEl: 'elId'
4972 });
4973 </code></pre>
4974  * @singleton
4975  */
4976 Roo.bootstrap.MessageBox = function(){
4977     var dlg, opt, mask, waitTimer;
4978     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4979     var buttons, activeTextEl, bwidth;
4980
4981     
4982     // private
4983     var handleButton = function(button){
4984         dlg.hide();
4985         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4986     };
4987
4988     // private
4989     var handleHide = function(){
4990         if(opt && opt.cls){
4991             dlg.el.removeClass(opt.cls);
4992         }
4993         //if(waitTimer){
4994         //    Roo.TaskMgr.stop(waitTimer);
4995         //    waitTimer = null;
4996         //}
4997     };
4998
4999     // private
5000     var updateButtons = function(b){
5001         var width = 0;
5002         if(!b){
5003             buttons["ok"].hide();
5004             buttons["cancel"].hide();
5005             buttons["yes"].hide();
5006             buttons["no"].hide();
5007             dlg.footerEl.hide();
5008             
5009             return width;
5010         }
5011         dlg.footerEl.show();
5012         for(var k in buttons){
5013             if(typeof buttons[k] != "function"){
5014                 if(b[k]){
5015                     buttons[k].show();
5016                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5017                     width += buttons[k].el.getWidth()+15;
5018                 }else{
5019                     buttons[k].hide();
5020                 }
5021             }
5022         }
5023         return width;
5024     };
5025
5026     // private
5027     var handleEsc = function(d, k, e){
5028         if(opt && opt.closable !== false){
5029             dlg.hide();
5030         }
5031         if(e){
5032             e.stopEvent();
5033         }
5034     };
5035
5036     return {
5037         /**
5038          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5039          * @return {Roo.BasicDialog} The BasicDialog element
5040          */
5041         getDialog : function(){
5042            if(!dlg){
5043                 dlg = new Roo.bootstrap.Modal( {
5044                     //draggable: true,
5045                     //resizable:false,
5046                     //constraintoviewport:false,
5047                     //fixedcenter:true,
5048                     //collapsible : false,
5049                     //shim:true,
5050                     //modal: true,
5051                 //    width: 'auto',
5052                   //  height:100,
5053                     //buttonAlign:"center",
5054                     closeClick : function(){
5055                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5056                             handleButton("no");
5057                         }else{
5058                             handleButton("cancel");
5059                         }
5060                     }
5061                 });
5062                 dlg.render();
5063                 dlg.on("hide", handleHide);
5064                 mask = dlg.mask;
5065                 //dlg.addKeyListener(27, handleEsc);
5066                 buttons = {};
5067                 this.buttons = buttons;
5068                 var bt = this.buttonText;
5069                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5070                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5071                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5072                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073                 //Roo.log(buttons);
5074                 bodyEl = dlg.bodyEl.createChild({
5075
5076                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5077                         '<textarea class="roo-mb-textarea"></textarea>' +
5078                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5079                 });
5080                 msgEl = bodyEl.dom.firstChild;
5081                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5082                 textboxEl.enableDisplayMode();
5083                 textboxEl.addKeyListener([10,13], function(){
5084                     if(dlg.isVisible() && opt && opt.buttons){
5085                         if(opt.buttons.ok){
5086                             handleButton("ok");
5087                         }else if(opt.buttons.yes){
5088                             handleButton("yes");
5089                         }
5090                     }
5091                 });
5092                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5093                 textareaEl.enableDisplayMode();
5094                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5095                 progressEl.enableDisplayMode();
5096                 
5097                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5098                 var pf = progressEl.dom.firstChild;
5099                 if (pf) {
5100                     pp = Roo.get(pf.firstChild);
5101                     pp.setHeight(pf.offsetHeight);
5102                 }
5103                 
5104             }
5105             return dlg;
5106         },
5107
5108         /**
5109          * Updates the message box body text
5110          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5111          * the XHTML-compliant non-breaking space character '&amp;#160;')
5112          * @return {Roo.MessageBox} This message box
5113          */
5114         updateText : function(text)
5115         {
5116             if(!dlg.isVisible() && !opt.width){
5117                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5118                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119             }
5120             msgEl.innerHTML = text || '&#160;';
5121       
5122             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5123             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124             var w = Math.max(
5125                     Math.min(opt.width || cw , this.maxWidth), 
5126                     Math.max(opt.minWidth || this.minWidth, bwidth)
5127             );
5128             if(opt.prompt){
5129                 activeTextEl.setWidth(w);
5130             }
5131             if(dlg.isVisible()){
5132                 dlg.fixedcenter = false;
5133             }
5134             // to big, make it scroll. = But as usual stupid IE does not support
5135             // !important..
5136             
5137             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5138                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5139                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140             } else {
5141                 bodyEl.dom.style.height = '';
5142                 bodyEl.dom.style.overflowY = '';
5143             }
5144             if (cw > w) {
5145                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146             } else {
5147                 bodyEl.dom.style.overflowX = '';
5148             }
5149             
5150             dlg.setContentSize(w, bodyEl.getHeight());
5151             if(dlg.isVisible()){
5152                 dlg.fixedcenter = true;
5153             }
5154             return this;
5155         },
5156
5157         /**
5158          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5159          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5160          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5161          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5162          * @return {Roo.MessageBox} This message box
5163          */
5164         updateProgress : function(value, text){
5165             if(text){
5166                 this.updateText(text);
5167             }
5168             
5169             if (pp) { // weird bug on my firefox - for some reason this is not defined
5170                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5171                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5172             }
5173             return this;
5174         },        
5175
5176         /**
5177          * Returns true if the message box is currently displayed
5178          * @return {Boolean} True if the message box is visible, else false
5179          */
5180         isVisible : function(){
5181             return dlg && dlg.isVisible();  
5182         },
5183
5184         /**
5185          * Hides the message box if it is displayed
5186          */
5187         hide : function(){
5188             if(this.isVisible()){
5189                 dlg.hide();
5190             }  
5191         },
5192
5193         /**
5194          * Displays a new message box, or reinitializes an existing message box, based on the config options
5195          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5196          * The following config object properties are supported:
5197          * <pre>
5198 Property    Type             Description
5199 ----------  ---------------  ------------------------------------------------------------------------------------
5200 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5201                                    closes (defaults to undefined)
5202 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5203                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5204 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5205                                    progress and wait dialogs will ignore this property and always hide the
5206                                    close button as they can only be closed programmatically.
5207 cls               String           A custom CSS class to apply to the message box element
5208 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5209                                    displayed (defaults to 75)
5210 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5211                                    function will be btn (the name of the button that was clicked, if applicable,
5212                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5213                                    Progress and wait dialogs will ignore this option since they do not respond to
5214                                    user actions and can only be closed programmatically, so any required function
5215                                    should be called by the same code after it closes the dialog.
5216 icon              String           A CSS class that provides a background image to be used as an icon for
5217                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5218 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5219 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5220 modal             Boolean          False to allow user interaction with the page while the message box is
5221                                    displayed (defaults to true)
5222 msg               String           A string that will replace the existing message box body text (defaults
5223                                    to the XHTML-compliant non-breaking space character '&#160;')
5224 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5225 progress          Boolean          True to display a progress bar (defaults to false)
5226 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5227 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5228 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5229 title             String           The title text
5230 value             String           The string value to set into the active textbox element if displayed
5231 wait              Boolean          True to display a progress bar (defaults to false)
5232 width             Number           The width of the dialog in pixels
5233 </pre>
5234          *
5235          * Example usage:
5236          * <pre><code>
5237 Roo.Msg.show({
5238    title: 'Address',
5239    msg: 'Please enter your address:',
5240    width: 300,
5241    buttons: Roo.MessageBox.OKCANCEL,
5242    multiline: true,
5243    fn: saveAddress,
5244    animEl: 'addAddressBtn'
5245 });
5246 </code></pre>
5247          * @param {Object} config Configuration options
5248          * @return {Roo.MessageBox} This message box
5249          */
5250         show : function(options)
5251         {
5252             
5253             // this causes nightmares if you show one dialog after another
5254             // especially on callbacks..
5255              
5256             if(this.isVisible()){
5257                 
5258                 this.hide();
5259                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5260                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5261                 Roo.log("New Dialog Message:" +  options.msg )
5262                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5263                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5264                 
5265             }
5266             var d = this.getDialog();
5267             opt = options;
5268             d.setTitle(opt.title || "&#160;");
5269             d.closeEl.setDisplayed(opt.closable !== false);
5270             activeTextEl = textboxEl;
5271             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5272             if(opt.prompt){
5273                 if(opt.multiline){
5274                     textboxEl.hide();
5275                     textareaEl.show();
5276                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5277                         opt.multiline : this.defaultTextHeight);
5278                     activeTextEl = textareaEl;
5279                 }else{
5280                     textboxEl.show();
5281                     textareaEl.hide();
5282                 }
5283             }else{
5284                 textboxEl.hide();
5285                 textareaEl.hide();
5286             }
5287             progressEl.setDisplayed(opt.progress === true);
5288             if (opt.progress) {
5289                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290             }
5291             this.updateProgress(0);
5292             activeTextEl.dom.value = opt.value || "";
5293             if(opt.prompt){
5294                 dlg.setDefaultButton(activeTextEl);
5295             }else{
5296                 var bs = opt.buttons;
5297                 var db = null;
5298                 if(bs && bs.ok){
5299                     db = buttons["ok"];
5300                 }else if(bs && bs.yes){
5301                     db = buttons["yes"];
5302                 }
5303                 dlg.setDefaultButton(db);
5304             }
5305             bwidth = updateButtons(opt.buttons);
5306             this.updateText(opt.msg);
5307             if(opt.cls){
5308                 d.el.addClass(opt.cls);
5309             }
5310             d.proxyDrag = opt.proxyDrag === true;
5311             d.modal = opt.modal !== false;
5312             d.mask = opt.modal !== false ? mask : false;
5313             if(!d.isVisible()){
5314                 // force it to the end of the z-index stack so it gets a cursor in FF
5315                 document.body.appendChild(dlg.el.dom);
5316                 d.animateTarget = null;
5317                 d.show(options.animEl);
5318             }
5319             return this;
5320         },
5321
5322         /**
5323          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5324          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5325          * and closing the message box when the process is complete.
5326          * @param {String} title The title bar text
5327          * @param {String} msg The message box body text
5328          * @return {Roo.MessageBox} This message box
5329          */
5330         progress : function(title, msg){
5331             this.show({
5332                 title : title,
5333                 msg : msg,
5334                 buttons: false,
5335                 progress:true,
5336                 closable:false,
5337                 minWidth: this.minProgressWidth,
5338                 modal : true
5339             });
5340             return this;
5341         },
5342
5343         /**
5344          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5345          * If a callback function is passed it will be called after the user clicks the button, and the
5346          * id of the button that was clicked will be passed as the only parameter to the callback
5347          * (could also be the top-right close button).
5348          * @param {String} title The title bar text
5349          * @param {String} msg The message box body text
5350          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5351          * @param {Object} scope (optional) The scope of the callback function
5352          * @return {Roo.MessageBox} This message box
5353          */
5354         alert : function(title, msg, fn, scope)
5355         {
5356             this.show({
5357                 title : title,
5358                 msg : msg,
5359                 buttons: this.OK,
5360                 fn: fn,
5361                 closable : false,
5362                 scope : scope,
5363                 modal : true
5364             });
5365             return this;
5366         },
5367
5368         /**
5369          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5370          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5371          * You are responsible for closing the message box when the process is complete.
5372          * @param {String} msg The message box body text
5373          * @param {String} title (optional) The title bar text
5374          * @return {Roo.MessageBox} This message box
5375          */
5376         wait : function(msg, title){
5377             this.show({
5378                 title : title,
5379                 msg : msg,
5380                 buttons: false,
5381                 closable:false,
5382                 progress:true,
5383                 modal:true,
5384                 width:300,
5385                 wait:true
5386             });
5387             waitTimer = Roo.TaskMgr.start({
5388                 run: function(i){
5389                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5390                 },
5391                 interval: 1000
5392             });
5393             return this;
5394         },
5395
5396         /**
5397          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5398          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5399          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5400          * @param {String} title The title bar text
5401          * @param {String} msg The message box body text
5402          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5403          * @param {Object} scope (optional) The scope of the callback function
5404          * @return {Roo.MessageBox} This message box
5405          */
5406         confirm : function(title, msg, fn, scope){
5407             this.show({
5408                 title : title,
5409                 msg : msg,
5410                 buttons: this.YESNO,
5411                 fn: fn,
5412                 scope : scope,
5413                 modal : true
5414             });
5415             return this;
5416         },
5417
5418         /**
5419          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5420          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5421          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5422          * (could also be the top-right close button) and the text that was entered will be passed as the two
5423          * parameters to the callback.
5424          * @param {String} title The title bar text
5425          * @param {String} msg The message box body text
5426          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5427          * @param {Object} scope (optional) The scope of the callback function
5428          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5429          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5430          * @return {Roo.MessageBox} This message box
5431          */
5432         prompt : function(title, msg, fn, scope, multiline){
5433             this.show({
5434                 title : title,
5435                 msg : msg,
5436                 buttons: this.OKCANCEL,
5437                 fn: fn,
5438                 minWidth:250,
5439                 scope : scope,
5440                 prompt:true,
5441                 multiline: multiline,
5442                 modal : true
5443             });
5444             return this;
5445         },
5446
5447         /**
5448          * Button config that displays a single OK button
5449          * @type Object
5450          */
5451         OK : {ok:true},
5452         /**
5453          * Button config that displays Yes and No buttons
5454          * @type Object
5455          */
5456         YESNO : {yes:true, no:true},
5457         /**
5458          * Button config that displays OK and Cancel buttons
5459          * @type Object
5460          */
5461         OKCANCEL : {ok:true, cancel:true},
5462         /**
5463          * Button config that displays Yes, No and Cancel buttons
5464          * @type Object
5465          */
5466         YESNOCANCEL : {yes:true, no:true, cancel:true},
5467
5468         /**
5469          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5470          * @type Number
5471          */
5472         defaultTextHeight : 75,
5473         /**
5474          * The maximum width in pixels of the message box (defaults to 600)
5475          * @type Number
5476          */
5477         maxWidth : 600,
5478         /**
5479          * The minimum width in pixels of the message box (defaults to 100)
5480          * @type Number
5481          */
5482         minWidth : 100,
5483         /**
5484          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5485          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5486          * @type Number
5487          */
5488         minProgressWidth : 250,
5489         /**
5490          * An object containing the default button text strings that can be overriden for localized language support.
5491          * Supported properties are: ok, cancel, yes and no.
5492          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5493          * @type Object
5494          */
5495         buttonText : {
5496             ok : "OK",
5497             cancel : "Cancel",
5498             yes : "Yes",
5499             no : "No"
5500         }
5501     };
5502 }();
5503
5504 /**
5505  * Shorthand for {@link Roo.MessageBox}
5506  */
5507 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5508 Roo.Msg = Roo.Msg || Roo.MessageBox;
5509 /*
5510  * - LGPL
5511  *
5512  * navbar
5513  * 
5514  */
5515
5516 /**
5517  * @class Roo.bootstrap.Navbar
5518  * @extends Roo.bootstrap.Component
5519  * Bootstrap Navbar class
5520
5521  * @constructor
5522  * Create a new Navbar
5523  * @param {Object} config The config object
5524  */
5525
5526
5527 Roo.bootstrap.Navbar = function(config){
5528     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5529     this.addEvents({
5530         // raw events
5531         /**
5532          * @event beforetoggle
5533          * Fire before toggle the menu
5534          * @param {Roo.EventObject} e
5535          */
5536         "beforetoggle" : true
5537     });
5538 };
5539
5540 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5541     
5542     
5543    
5544     // private
5545     navItems : false,
5546     loadMask : false,
5547     
5548     
5549     getAutoCreate : function(){
5550         
5551         
5552         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5553         
5554     },
5555     
5556     initEvents :function ()
5557     {
5558         //Roo.log(this.el.select('.navbar-toggle',true));
5559         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5560         
5561         var mark = {
5562             tag: "div",
5563             cls:"x-dlg-mask"
5564         };
5565         
5566         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567         
5568         var size = this.el.getSize();
5569         this.maskEl.setSize(size.width, size.height);
5570         this.maskEl.enableDisplayMode("block");
5571         this.maskEl.hide();
5572         
5573         if(this.loadMask){
5574             this.maskEl.show();
5575         }
5576     },
5577     
5578     
5579     getChildContainer : function()
5580     {
5581         if (this.el && this.el.select('.collapse').getCount()) {
5582             return this.el.select('.collapse',true).first();
5583         }
5584         
5585         return this.el;
5586     },
5587     
5588     mask : function()
5589     {
5590         this.maskEl.show();
5591     },
5592     
5593     unmask : function()
5594     {
5595         this.maskEl.hide();
5596     },
5597     onToggle : function()
5598     {
5599         
5600         if(this.fireEvent('beforetoggle', this) === false){
5601             return;
5602         }
5603         var ce = this.el.select('.navbar-collapse',true).first();
5604       
5605         if (!ce.hasClass('show')) {
5606            this.expand();
5607         } else {
5608             this.collapse();
5609         }
5610         
5611         
5612     
5613     },
5614     /**
5615      * Expand the navbar pulldown 
5616      */
5617     expand : function ()
5618     {
5619        
5620         var ce = this.el.select('.navbar-collapse',true).first();
5621         if (ce.hasClass('collapsing')) {
5622             return;
5623         }
5624         ce.dom.style.height = '';
5625                // show it...
5626         ce.addClass('in'); // old...
5627         ce.removeClass('collapse');
5628         ce.addClass('show');
5629         var h = ce.getHeight();
5630         Roo.log(h);
5631         ce.removeClass('show');
5632         // at this point we should be able to see it..
5633         ce.addClass('collapsing');
5634         
5635         ce.setHeight(0); // resize it ...
5636         ce.on('transitionend', function() {
5637             //Roo.log('done transition');
5638             ce.removeClass('collapsing');
5639             ce.addClass('show');
5640             ce.removeClass('collapse');
5641
5642             ce.dom.style.height = '';
5643         }, this, { single: true} );
5644         ce.setHeight(h);
5645         ce.dom.scrollTop = 0;
5646     },
5647     /**
5648      * Collapse the navbar pulldown 
5649      */
5650     collapse : function()
5651     {
5652          var ce = this.el.select('.navbar-collapse',true).first();
5653        
5654         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5655             // it's collapsed or collapsing..
5656             return;
5657         }
5658         ce.removeClass('in'); // old...
5659         ce.setHeight(ce.getHeight());
5660         ce.removeClass('show');
5661         ce.addClass('collapsing');
5662         
5663         ce.on('transitionend', function() {
5664             ce.dom.style.height = '';
5665             ce.removeClass('collapsing');
5666             ce.addClass('collapse');
5667         }, this, { single: true} );
5668         ce.setHeight(0);
5669     }
5670     
5671     
5672     
5673 });
5674
5675
5676
5677  
5678
5679  /*
5680  * - LGPL
5681  *
5682  * navbar
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.NavSimplebar
5688  * @extends Roo.bootstrap.Navbar
5689  * Bootstrap Sidebar class
5690  *
5691  * @cfg {Boolean} inverse is inverted color
5692  * 
5693  * @cfg {String} type (nav | pills | tabs)
5694  * @cfg {Boolean} arrangement stacked | justified
5695  * @cfg {String} align (left | right) alignment
5696  * 
5697  * @cfg {Boolean} main (true|false) main nav bar? default false
5698  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699  * 
5700  * @cfg {String} tag (header|footer|nav|div) default is nav 
5701
5702  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5703  * 
5704  * 
5705  * @constructor
5706  * Create a new Sidebar
5707  * @param {Object} config The config object
5708  */
5709
5710
5711 Roo.bootstrap.NavSimplebar = function(config){
5712     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5713 };
5714
5715 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5716     
5717     inverse: false,
5718     
5719     type: false,
5720     arrangement: '',
5721     align : false,
5722     
5723     weight : 'light',
5724     
5725     main : false,
5726     
5727     
5728     tag : false,
5729     
5730     
5731     getAutoCreate : function(){
5732         
5733         
5734         var cfg = {
5735             tag : this.tag || 'div',
5736             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737         };
5738         if (['light','white'].indexOf(this.weight) > -1) {
5739             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740         }
5741         cfg.cls += ' bg-' + this.weight;
5742         
5743         if (this.inverse) {
5744             cfg.cls += ' navbar-inverse';
5745             
5746         }
5747         
5748         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749         
5750         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5751             return cfg;
5752         }
5753         
5754         
5755     
5756         
5757         cfg.cn = [
5758             {
5759                 cls: 'nav nav-' + this.xtype,
5760                 tag : 'ul'
5761             }
5762         ];
5763         
5764          
5765         this.type = this.type || 'nav';
5766         if (['tabs','pills'].indexOf(this.type) != -1) {
5767             cfg.cn[0].cls += ' nav-' + this.type
5768         
5769         
5770         } else {
5771             if (this.type!=='nav') {
5772                 Roo.log('nav type must be nav/tabs/pills')
5773             }
5774             cfg.cn[0].cls += ' navbar-nav'
5775         }
5776         
5777         
5778         
5779         
5780         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5781             cfg.cn[0].cls += ' nav-' + this.arrangement;
5782         }
5783         
5784         
5785         if (this.align === 'right') {
5786             cfg.cn[0].cls += ' navbar-right';
5787         }
5788         
5789         
5790         
5791         
5792         return cfg;
5793     
5794         
5795     }
5796     
5797     
5798     
5799 });
5800
5801
5802
5803  
5804
5805  
5806        /*
5807  * - LGPL
5808  *
5809  * navbar
5810  * navbar-fixed-top
5811  * navbar-expand-md  fixed-top 
5812  */
5813
5814 /**
5815  * @class Roo.bootstrap.NavHeaderbar
5816  * @extends Roo.bootstrap.NavSimplebar
5817  * Bootstrap Sidebar class
5818  *
5819  * @cfg {String} brand what is brand
5820  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5821  * @cfg {String} brand_href href of the brand
5822  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5823  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5824  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5825  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5826  * 
5827  * @constructor
5828  * Create a new Sidebar
5829  * @param {Object} config The config object
5830  */
5831
5832
5833 Roo.bootstrap.NavHeaderbar = function(config){
5834     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5835       
5836 };
5837
5838 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5839     
5840     position: '',
5841     brand: '',
5842     brand_href: false,
5843     srButton : true,
5844     autohide : false,
5845     desktopCenter : false,
5846    
5847     
5848     getAutoCreate : function(){
5849         
5850         var   cfg = {
5851             tag: this.nav || 'nav',
5852             cls: 'navbar navbar-expand-md',
5853             role: 'navigation',
5854             cn: []
5855         };
5856         
5857         var cn = cfg.cn;
5858         if (this.desktopCenter) {
5859             cn.push({cls : 'container', cn : []});
5860             cn = cn[0].cn;
5861         }
5862         
5863         if(this.srButton){
5864             var btn = {
5865                 tag: 'button',
5866                 type: 'button',
5867                 cls: 'navbar-toggle navbar-toggler',
5868                 'data-toggle': 'collapse',
5869                 cn: [
5870                     {
5871                         tag: 'span',
5872                         cls: 'sr-only',
5873                         html: 'Toggle navigation'
5874                     },
5875                     {
5876                         tag: 'span',
5877                         cls: 'icon-bar navbar-toggler-icon'
5878                     },
5879                     {
5880                         tag: 'span',
5881                         cls: 'icon-bar'
5882                     },
5883                     {
5884                         tag: 'span',
5885                         cls: 'icon-bar'
5886                     }
5887                 ]
5888             };
5889             
5890             cn.push( Roo.bootstrap.version == 4 ? btn : {
5891                 tag: 'div',
5892                 cls: 'navbar-header',
5893                 cn: [
5894                     btn
5895                 ]
5896             });
5897         }
5898         
5899         cn.push({
5900             tag: 'div',
5901             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5902             cn : []
5903         });
5904         
5905         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906         
5907         if (['light','white'].indexOf(this.weight) > -1) {
5908             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909         }
5910         cfg.cls += ' bg-' + this.weight;
5911         
5912         
5913         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5914             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915             
5916             // tag can override this..
5917             
5918             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5919         }
5920         
5921         if (this.brand !== '') {
5922             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5923             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924                 tag: 'a',
5925                 href: this.brand_href ? this.brand_href : '#',
5926                 cls: 'navbar-brand',
5927                 cn: [
5928                 this.brand
5929                 ]
5930             });
5931         }
5932         
5933         if(this.main){
5934             cfg.cls += ' main-nav';
5935         }
5936         
5937         
5938         return cfg;
5939
5940         
5941     },
5942     getHeaderChildContainer : function()
5943     {
5944         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5945             return this.el.select('.navbar-header',true).first();
5946         }
5947         
5948         return this.getChildContainer();
5949     },
5950     
5951     getChildContainer : function()
5952     {
5953          
5954         return this.el.select('.roo-navbar-collapse',true).first();
5955          
5956         
5957     },
5958     
5959     initEvents : function()
5960     {
5961         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962         
5963         if (this.autohide) {
5964             
5965             var prevScroll = 0;
5966             var ft = this.el;
5967             
5968             Roo.get(document).on('scroll',function(e) {
5969                 var ns = Roo.get(document).getScroll().top;
5970                 var os = prevScroll;
5971                 prevScroll = ns;
5972                 
5973                 if(ns > os){
5974                     ft.removeClass('slideDown');
5975                     ft.addClass('slideUp');
5976                     return;
5977                 }
5978                 ft.removeClass('slideUp');
5979                 ft.addClass('slideDown');
5980                  
5981               
5982           },this);
5983         }
5984     }    
5985     
5986 });
5987
5988
5989
5990  
5991
5992  /*
5993  * - LGPL
5994  *
5995  * navbar
5996  * 
5997  */
5998
5999 /**
6000  * @class Roo.bootstrap.NavSidebar
6001  * @extends Roo.bootstrap.Navbar
6002  * Bootstrap Sidebar class
6003  * 
6004  * @constructor
6005  * Create a new Sidebar
6006  * @param {Object} config The config object
6007  */
6008
6009
6010 Roo.bootstrap.NavSidebar = function(config){
6011     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6012 };
6013
6014 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6015     
6016     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017     
6018     getAutoCreate : function(){
6019         
6020         
6021         return  {
6022             tag: 'div',
6023             cls: 'sidebar sidebar-nav'
6024         };
6025     
6026         
6027     }
6028     
6029     
6030     
6031 });
6032
6033
6034
6035  
6036
6037  /*
6038  * - LGPL
6039  *
6040  * nav group
6041  * 
6042  */
6043
6044 /**
6045  * @class Roo.bootstrap.NavGroup
6046  * @extends Roo.bootstrap.Component
6047  * Bootstrap NavGroup class
6048  * @cfg {String} align (left|right)
6049  * @cfg {Boolean} inverse
6050  * @cfg {String} type (nav|pills|tab) default nav
6051  * @cfg {String} navId - reference Id for navbar.
6052  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6053  * 
6054  * @constructor
6055  * Create a new nav group
6056  * @param {Object} config The config object
6057  */
6058
6059 Roo.bootstrap.NavGroup = function(config){
6060     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6061     this.navItems = [];
6062    
6063     Roo.bootstrap.NavGroup.register(this);
6064      this.addEvents({
6065         /**
6066              * @event changed
6067              * Fires when the active item changes
6068              * @param {Roo.bootstrap.NavGroup} this
6069              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6070              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6071          */
6072         'changed': true
6073      });
6074     
6075 };
6076
6077 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6078     
6079     align: '',
6080     inverse: false,
6081     form: false,
6082     type: 'nav',
6083     navId : '',
6084     // private
6085     pilltype : true,
6086     
6087     navItems : false, 
6088     
6089     getAutoCreate : function()
6090     {
6091         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6092         
6093         cfg = {
6094             tag : 'ul',
6095             cls: 'nav' 
6096         };
6097         if (Roo.bootstrap.version == 4) {
6098             if (['tabs','pills'].indexOf(this.type) != -1) {
6099                 cfg.cls += ' nav-' + this.type; 
6100             } else {
6101                 // trying to remove so header bar can right align top?
6102                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6103                     // do not use on header bar... 
6104                     cfg.cls += ' navbar-nav';
6105                 }
6106             }
6107             
6108         } else {
6109             if (['tabs','pills'].indexOf(this.type) != -1) {
6110                 cfg.cls += ' nav-' + this.type
6111             } else {
6112                 if (this.type !== 'nav') {
6113                     Roo.log('nav type must be nav/tabs/pills')
6114                 }
6115                 cfg.cls += ' navbar-nav'
6116             }
6117         }
6118         
6119         if (this.parent() && this.parent().sidebar) {
6120             cfg = {
6121                 tag: 'ul',
6122                 cls: 'dashboard-menu sidebar-menu'
6123             };
6124             
6125             return cfg;
6126         }
6127         
6128         if (this.form === true) {
6129             cfg = {
6130                 tag: 'form',
6131                 cls: 'navbar-form form-inline'
6132             };
6133             //nav navbar-right ml-md-auto
6134             if (this.align === 'right') {
6135                 cfg.cls += ' navbar-right ml-md-auto';
6136             } else {
6137                 cfg.cls += ' navbar-left';
6138             }
6139         }
6140         
6141         if (this.align === 'right') {
6142             cfg.cls += ' navbar-right ml-md-auto';
6143         } else {
6144             cfg.cls += ' mr-auto';
6145         }
6146         
6147         if (this.inverse) {
6148             cfg.cls += ' navbar-inverse';
6149             
6150         }
6151         
6152         
6153         return cfg;
6154     },
6155     /**
6156     * sets the active Navigation item
6157     * @param {Roo.bootstrap.NavItem} the new current navitem
6158     */
6159     setActiveItem : function(item)
6160     {
6161         var prev = false;
6162         Roo.each(this.navItems, function(v){
6163             if (v == item) {
6164                 return ;
6165             }
6166             if (v.isActive()) {
6167                 v.setActive(false, true);
6168                 prev = v;
6169                 
6170             }
6171             
6172         });
6173
6174         item.setActive(true, true);
6175         this.fireEvent('changed', this, item, prev);
6176         
6177         
6178     },
6179     /**
6180     * gets the active Navigation item
6181     * @return {Roo.bootstrap.NavItem} the current navitem
6182     */
6183     getActive : function()
6184     {
6185         
6186         var prev = false;
6187         Roo.each(this.navItems, function(v){
6188             
6189             if (v.isActive()) {
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195         return prev;
6196     },
6197     
6198     indexOfNav : function()
6199     {
6200         
6201         var prev = false;
6202         Roo.each(this.navItems, function(v,i){
6203             
6204             if (v.isActive()) {
6205                 prev = i;
6206                 
6207             }
6208             
6209         });
6210         return prev;
6211     },
6212     /**
6213     * adds a Navigation item
6214     * @param {Roo.bootstrap.NavItem} the navitem to add
6215     */
6216     addItem : function(cfg)
6217     {
6218         if (this.form && Roo.bootstrap.version == 4) {
6219             cfg.tag = 'div';
6220         }
6221         var cn = new Roo.bootstrap.NavItem(cfg);
6222         this.register(cn);
6223         cn.parentId = this.id;
6224         cn.onRender(this.el, null);
6225         return cn;
6226     },
6227     /**
6228     * register a Navigation item
6229     * @param {Roo.bootstrap.NavItem} the navitem to add
6230     */
6231     register : function(item)
6232     {
6233         this.navItems.push( item);
6234         item.navId = this.navId;
6235     
6236     },
6237     
6238     /**
6239     * clear all the Navigation item
6240     */
6241    
6242     clearAll : function()
6243     {
6244         this.navItems = [];
6245         this.el.dom.innerHTML = '';
6246     },
6247     
6248     getNavItem: function(tabId)
6249     {
6250         var ret = false;
6251         Roo.each(this.navItems, function(e) {
6252             if (e.tabId == tabId) {
6253                ret =  e;
6254                return false;
6255             }
6256             return true;
6257             
6258         });
6259         return ret;
6260     },
6261     
6262     setActiveNext : function()
6263     {
6264         var i = this.indexOfNav(this.getActive());
6265         if (i > this.navItems.length) {
6266             return;
6267         }
6268         this.setActiveItem(this.navItems[i+1]);
6269     },
6270     setActivePrev : function()
6271     {
6272         var i = this.indexOfNav(this.getActive());
6273         if (i  < 1) {
6274             return;
6275         }
6276         this.setActiveItem(this.navItems[i-1]);
6277     },
6278     clearWasActive : function(except) {
6279         Roo.each(this.navItems, function(e) {
6280             if (e.tabId != except.tabId && e.was_active) {
6281                e.was_active = false;
6282                return false;
6283             }
6284             return true;
6285             
6286         });
6287     },
6288     getWasActive : function ()
6289     {
6290         var r = false;
6291         Roo.each(this.navItems, function(e) {
6292             if (e.was_active) {
6293                r = e;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299         return r;
6300     }
6301     
6302     
6303 });
6304
6305  
6306 Roo.apply(Roo.bootstrap.NavGroup, {
6307     
6308     groups: {},
6309      /**
6310     * register a Navigation Group
6311     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312     */
6313     register : function(navgrp)
6314     {
6315         this.groups[navgrp.navId] = navgrp;
6316         
6317     },
6318     /**
6319     * fetch a Navigation Group based on the navigation ID
6320     * @param {string} the navgroup to add
6321     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6322     */
6323     get: function(navId) {
6324         if (typeof(this.groups[navId]) == 'undefined') {
6325             return false;
6326             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327         }
6328         return this.groups[navId] ;
6329     }
6330     
6331     
6332     
6333 });
6334
6335  /*
6336  * - LGPL
6337  *
6338  * row
6339  * 
6340  */
6341
6342 /**
6343  * @class Roo.bootstrap.NavItem
6344  * @extends Roo.bootstrap.Component
6345  * Bootstrap Navbar.NavItem class
6346  * @cfg {String} href  link to
6347  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6348  * @cfg {Boolean} button_outline show and outlined button
6349  * @cfg {String} html content of button
6350  * @cfg {String} badge text inside badge
6351  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6352  * @cfg {String} glyphicon DEPRICATED - use fa
6353  * @cfg {String} icon DEPRICATED - use fa
6354  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6355  * @cfg {Boolean} active Is item active
6356  * @cfg {Boolean} disabled Is item disabled
6357  * @cfg {String} linkcls  Link Class
6358  * @cfg {Boolean} preventDefault (true | false) default false
6359  * @cfg {String} tabId the tab that this item activates.
6360  * @cfg {String} tagtype (a|span) render as a href or span?
6361  * @cfg {Boolean} animateRef (true|false) link to element default false  
6362   
6363  * @constructor
6364  * Create a new Navbar Item
6365  * @param {Object} config The config object
6366  */
6367 Roo.bootstrap.NavItem = function(config){
6368     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6369     this.addEvents({
6370         // raw events
6371         /**
6372          * @event click
6373          * The raw click event for the entire grid.
6374          * @param {Roo.EventObject} e
6375          */
6376         "click" : true,
6377          /**
6378             * @event changed
6379             * Fires when the active item active state changes
6380             * @param {Roo.bootstrap.NavItem} this
6381             * @param {boolean} state the new state
6382              
6383          */
6384         'changed': true,
6385         /**
6386             * @event scrollto
6387             * Fires when scroll to element
6388             * @param {Roo.bootstrap.NavItem} this
6389             * @param {Object} options
6390             * @param {Roo.EventObject} e
6391              
6392          */
6393         'scrollto': true
6394     });
6395    
6396 };
6397
6398 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6399     
6400     href: false,
6401     html: '',
6402     badge: '',
6403     icon: false,
6404     fa : false,
6405     glyphicon: false,
6406     active: false,
6407     preventDefault : false,
6408     tabId : false,
6409     tagtype : 'a',
6410     tag: 'li',
6411     disabled : false,
6412     animateRef : false,
6413     was_active : false,
6414     button_weight : '',
6415     button_outline : false,
6416     linkcls : '',
6417     navLink: false,
6418     
6419     getAutoCreate : function(){
6420          
6421         var cfg = {
6422             tag: this.tag,
6423             cls: 'nav-item'
6424         };
6425         
6426         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6427         
6428         if (this.active) {
6429             cfg.cls +=  ' active' ;
6430         }
6431         if (this.disabled) {
6432             cfg.cls += ' disabled';
6433         }
6434         
6435         // BS4 only?
6436         if (this.button_weight.length) {
6437             cfg.tag = this.href ? 'a' : 'button';
6438             cfg.html = this.html || '';
6439             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440             if (this.href) {
6441                 cfg.href = this.href;
6442             }
6443             if (this.fa) {
6444                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445             } else {
6446                 cfg.cls += " nav-html";
6447             }
6448             
6449             // menu .. should add dropdown-menu class - so no need for carat..
6450             
6451             if (this.badge !== '') {
6452                  
6453                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6454             }
6455             return cfg;
6456         }
6457         
6458         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6459             cfg.cn = [
6460                 {
6461                     tag: this.tagtype,
6462                     href : this.href || "#",
6463                     html: this.html || '',
6464                     cls : ''
6465                 }
6466             ];
6467             if (this.tagtype == 'a') {
6468                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6469         
6470             }
6471             if (this.icon) {
6472                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6473             } else  if (this.fa) {
6474                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6475             } else if(this.glyphicon) {
6476                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6477             } else {
6478                 cfg.cn[0].cls += " nav-html";
6479             }
6480             
6481             if (this.menu) {
6482                 cfg.cn[0].html += " <span class='caret'></span>";
6483              
6484             }
6485             
6486             if (this.badge !== '') {
6487                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6488             }
6489         }
6490         
6491         
6492         
6493         return cfg;
6494     },
6495     onRender : function(ct, position)
6496     {
6497        // Roo.log("Call onRender: " + this.xtype);
6498         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6499             this.tag = 'div';
6500         }
6501         
6502         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6503         this.navLink = this.el.select('.nav-link',true).first();
6504         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6505         return ret;
6506     },
6507       
6508     
6509     initEvents: function() 
6510     {
6511         if (typeof (this.menu) != 'undefined') {
6512             this.menu.parentType = this.xtype;
6513             this.menu.triggerEl = this.el;
6514             this.menu = this.addxtype(Roo.apply({}, this.menu));
6515         }
6516         
6517         this.el.on('click', this.onClick, this);
6518         
6519         //if(this.tagtype == 'span'){
6520         //    this.el.select('span',true).on('click', this.onClick, this);
6521         //}
6522        
6523         // at this point parent should be available..
6524         this.parent().register(this);
6525     },
6526     
6527     onClick : function(e)
6528     {
6529         if (e.getTarget('.dropdown-menu-item')) {
6530             // did you click on a menu itemm.... - then don't trigger onclick..
6531             return;
6532         }
6533         
6534         if(
6535                 this.preventDefault || 
6536                 this.href == '#' 
6537         ){
6538             Roo.log("NavItem - prevent Default?");
6539             e.preventDefault();
6540         }
6541         
6542         if (this.disabled) {
6543             return;
6544         }
6545         
6546         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6547         if (tg && tg.transition) {
6548             Roo.log("waiting for the transitionend");
6549             return;
6550         }
6551         
6552         
6553         
6554         //Roo.log("fire event clicked");
6555         if(this.fireEvent('click', this, e) === false){
6556             return;
6557         };
6558         
6559         if(this.tagtype == 'span'){
6560             return;
6561         }
6562         
6563         //Roo.log(this.href);
6564         var ael = this.el.select('a',true).first();
6565         //Roo.log(ael);
6566         
6567         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6568             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6569             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6570                 return; // ignore... - it's a 'hash' to another page.
6571             }
6572             Roo.log("NavItem - prevent Default?");
6573             e.preventDefault();
6574             this.scrollToElement(e);
6575         }
6576         
6577         
6578         var p =  this.parent();
6579    
6580         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6581             if (typeof(p.setActiveItem) !== 'undefined') {
6582                 p.setActiveItem(this);
6583             }
6584         }
6585         
6586         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6587         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6588             // remove the collapsed menu expand...
6589             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6590         }
6591     },
6592     
6593     isActive: function () {
6594         return this.active
6595     },
6596     setActive : function(state, fire, is_was_active)
6597     {
6598         if (this.active && !state && this.navId) {
6599             this.was_active = true;
6600             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601             if (nv) {
6602                 nv.clearWasActive(this);
6603             }
6604             
6605         }
6606         this.active = state;
6607         
6608         if (!state ) {
6609             this.el.removeClass('active');
6610             this.navLink ? this.navLink.removeClass('active') : false;
6611         } else if (!this.el.hasClass('active')) {
6612             
6613             this.el.addClass('active');
6614             if (Roo.bootstrap.version == 4 && this.navLink ) {
6615                 this.navLink.addClass('active');
6616             }
6617             
6618         }
6619         if (fire) {
6620             this.fireEvent('changed', this, state);
6621         }
6622         
6623         // show a panel if it's registered and related..
6624         
6625         if (!this.navId || !this.tabId || !state || is_was_active) {
6626             return;
6627         }
6628         
6629         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6630         if (!tg) {
6631             return;
6632         }
6633         var pan = tg.getPanelByName(this.tabId);
6634         if (!pan) {
6635             return;
6636         }
6637         // if we can not flip to new panel - go back to old nav highlight..
6638         if (false == tg.showPanel(pan)) {
6639             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640             if (nv) {
6641                 var onav = nv.getWasActive();
6642                 if (onav) {
6643                     onav.setActive(true, false, true);
6644                 }
6645             }
6646             
6647         }
6648         
6649         
6650         
6651     },
6652      // this should not be here...
6653     setDisabled : function(state)
6654     {
6655         this.disabled = state;
6656         if (!state ) {
6657             this.el.removeClass('disabled');
6658         } else if (!this.el.hasClass('disabled')) {
6659             this.el.addClass('disabled');
6660         }
6661         
6662     },
6663     
6664     /**
6665      * Fetch the element to display the tooltip on.
6666      * @return {Roo.Element} defaults to this.el
6667      */
6668     tooltipEl : function()
6669     {
6670         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6671     },
6672     
6673     scrollToElement : function(e)
6674     {
6675         var c = document.body;
6676         
6677         /*
6678          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679          */
6680         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6681             c = document.documentElement;
6682         }
6683         
6684         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6685         
6686         if(!target){
6687             return;
6688         }
6689
6690         var o = target.calcOffsetsTo(c);
6691         
6692         var options = {
6693             target : target,
6694             value : o[1]
6695         };
6696         
6697         this.fireEvent('scrollto', this, options, e);
6698         
6699         Roo.get(c).scrollTo('top', options.value, true);
6700         
6701         return;
6702     },
6703     /**
6704      * Set the HTML (text content) of the item
6705      * @param {string} html  content for the nav item
6706      */
6707     setHtml : function(html)
6708     {
6709         this.html = html;
6710         this.htmlEl.dom.innerHTML = html;
6711         
6712     } 
6713 });
6714  
6715
6716  /*
6717  * - LGPL
6718  *
6719  * sidebar item
6720  *
6721  *  li
6722  *    <span> icon </span>
6723  *    <span> text </span>
6724  *    <span>badge </span>
6725  */
6726
6727 /**
6728  * @class Roo.bootstrap.NavSidebarItem
6729  * @extends Roo.bootstrap.NavItem
6730  * Bootstrap Navbar.NavSidebarItem class
6731  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6732  * {Boolean} open is the menu open
6733  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6734  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6735  * {String} buttonSize (sm|md|lg)the extra classes for the button
6736  * {Boolean} showArrow show arrow next to the text (default true)
6737  * @constructor
6738  * Create a new Navbar Button
6739  * @param {Object} config The config object
6740  */
6741 Roo.bootstrap.NavSidebarItem = function(config){
6742     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6743     this.addEvents({
6744         // raw events
6745         /**
6746          * @event click
6747          * The raw click event for the entire grid.
6748          * @param {Roo.EventObject} e
6749          */
6750         "click" : true,
6751          /**
6752             * @event changed
6753             * Fires when the active item active state changes
6754             * @param {Roo.bootstrap.NavSidebarItem} this
6755             * @param {boolean} state the new state
6756              
6757          */
6758         'changed': true
6759     });
6760    
6761 };
6762
6763 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6764     
6765     badgeWeight : 'default',
6766     
6767     open: false,
6768     
6769     buttonView : false,
6770     
6771     buttonWeight : 'default',
6772     
6773     buttonSize : 'md',
6774     
6775     showArrow : true,
6776     
6777     getAutoCreate : function(){
6778         
6779         
6780         var a = {
6781                 tag: 'a',
6782                 href : this.href || '#',
6783                 cls: '',
6784                 html : '',
6785                 cn : []
6786         };
6787         
6788         if(this.buttonView){
6789             a = {
6790                 tag: 'button',
6791                 href : this.href || '#',
6792                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6793                 html : this.html,
6794                 cn : []
6795             };
6796         }
6797         
6798         var cfg = {
6799             tag: 'li',
6800             cls: '',
6801             cn: [ a ]
6802         };
6803         
6804         if (this.active) {
6805             cfg.cls += ' active';
6806         }
6807         
6808         if (this.disabled) {
6809             cfg.cls += ' disabled';
6810         }
6811         if (this.open) {
6812             cfg.cls += ' open x-open';
6813         }
6814         // left icon..
6815         if (this.glyphicon || this.icon) {
6816             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6817             a.cn.push({ tag : 'i', cls : c }) ;
6818         }
6819         
6820         if(!this.buttonView){
6821             var span = {
6822                 tag: 'span',
6823                 html : this.html || ''
6824             };
6825
6826             a.cn.push(span);
6827             
6828         }
6829         
6830         if (this.badge !== '') {
6831             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6832         }
6833         
6834         if (this.menu) {
6835             
6836             if(this.showArrow){
6837                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6838             }
6839             
6840             a.cls += ' dropdown-toggle treeview' ;
6841         }
6842         
6843         return cfg;
6844     },
6845     
6846     initEvents : function()
6847     { 
6848         if (typeof (this.menu) != 'undefined') {
6849             this.menu.parentType = this.xtype;
6850             this.menu.triggerEl = this.el;
6851             this.menu = this.addxtype(Roo.apply({}, this.menu));
6852         }
6853         
6854         this.el.on('click', this.onClick, this);
6855         
6856         if(this.badge !== ''){
6857             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6858         }
6859         
6860     },
6861     
6862     onClick : function(e)
6863     {
6864         if(this.disabled){
6865             e.preventDefault();
6866             return;
6867         }
6868         
6869         if(this.preventDefault){
6870             e.preventDefault();
6871         }
6872         
6873         this.fireEvent('click', this, e);
6874     },
6875     
6876     disable : function()
6877     {
6878         this.setDisabled(true);
6879     },
6880     
6881     enable : function()
6882     {
6883         this.setDisabled(false);
6884     },
6885     
6886     setDisabled : function(state)
6887     {
6888         if(this.disabled == state){
6889             return;
6890         }
6891         
6892         this.disabled = state;
6893         
6894         if (state) {
6895             this.el.addClass('disabled');
6896             return;
6897         }
6898         
6899         this.el.removeClass('disabled');
6900         
6901         return;
6902     },
6903     
6904     setActive : function(state)
6905     {
6906         if(this.active == state){
6907             return;
6908         }
6909         
6910         this.active = state;
6911         
6912         if (state) {
6913             this.el.addClass('active');
6914             return;
6915         }
6916         
6917         this.el.removeClass('active');
6918         
6919         return;
6920     },
6921     
6922     isActive: function () 
6923     {
6924         return this.active;
6925     },
6926     
6927     setBadge : function(str)
6928     {
6929         if(!this.badgeEl){
6930             return;
6931         }
6932         
6933         this.badgeEl.dom.innerHTML = str;
6934     }
6935     
6936    
6937      
6938  
6939 });
6940  
6941
6942  /*
6943  * - LGPL
6944  *
6945  *  Breadcrumb Nav
6946  * 
6947  */
6948 Roo.namespace('Roo.bootstrap.breadcrumb');
6949
6950
6951 /**
6952  * @class Roo.bootstrap.breadcrumb.Nav
6953  * @extends Roo.bootstrap.Component
6954  * Bootstrap Breadcrumb Nav Class
6955  *  
6956  * @children Roo.bootstrap.breadcrumb.Item
6957  * 
6958  * @constructor
6959  * Create a new breadcrumb.Nav
6960  * @param {Object} config The config object
6961  */
6962
6963
6964 Roo.bootstrap.breadcrumb.Nav = function(config){
6965     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6966     
6967     
6968 };
6969
6970 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6971     
6972     getAutoCreate : function()
6973     {
6974
6975         var cfg = {
6976             tag: 'nav',
6977             cn : [
6978                 {
6979                     tag : 'ol',
6980                     cls : 'breadcrumb'
6981                 }
6982             ]
6983             
6984         };
6985           
6986         return cfg;
6987     },
6988     
6989     initEvents: function()
6990     {
6991         this.olEl = this.el.select('ol',true).first();    
6992     },
6993     getChildContainer : function()
6994     {
6995         return this.olEl;  
6996     }
6997     
6998 });
6999
7000  /*
7001  * - LGPL
7002  *
7003  *  Breadcrumb Item
7004  * 
7005  */
7006
7007
7008 /**
7009  * @class Roo.bootstrap.breadcrumb.Nav
7010  * @extends Roo.bootstrap.Component
7011  * Bootstrap Breadcrumb Nav Class
7012  *  
7013  * @children Roo.bootstrap.breadcrumb.Component
7014  * @cfg {String} html the content of the link.
7015  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7016  * @cfg {Boolean} active is it active
7017
7018  * 
7019  * @constructor
7020  * Create a new breadcrumb.Nav
7021  * @param {Object} config The config object
7022  */
7023
7024 Roo.bootstrap.breadcrumb.Item = function(config){
7025     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7026     this.addEvents({
7027         // img events
7028         /**
7029          * @event click
7030          * The img click event for the img.
7031          * @param {Roo.EventObject} e
7032          */
7033         "click" : true
7034     });
7035     
7036 };
7037
7038 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7039     
7040     href: false,
7041     html : '',
7042     
7043     getAutoCreate : function()
7044     {
7045
7046         var cfg = {
7047             tag: 'li',
7048             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049         };
7050         if (this.href !== false) {
7051             cfg.cn = [{
7052                 tag : 'a',
7053                 href : this.href,
7054                 html : this.html
7055             }];
7056         } else {
7057             cfg.html = this.html;
7058         }
7059         
7060         return cfg;
7061     },
7062     
7063     initEvents: function()
7064     {
7065         if (this.href) {
7066             this.el.select('a', true).first().on('click',this.onClick, this)
7067         }
7068         
7069     },
7070     onClick : function(e)
7071     {
7072         e.preventDefault();
7073         this.fireEvent('click',this,  e);
7074     }
7075     
7076 });
7077
7078  /*
7079  * - LGPL
7080  *
7081  * row
7082  * 
7083  */
7084
7085 /**
7086  * @class Roo.bootstrap.Row
7087  * @extends Roo.bootstrap.Component
7088  * Bootstrap Row class (contains columns...)
7089  * 
7090  * @constructor
7091  * Create a new Row
7092  * @param {Object} config The config object
7093  */
7094
7095 Roo.bootstrap.Row = function(config){
7096     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7097 };
7098
7099 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7100     
7101     getAutoCreate : function(){
7102        return {
7103             cls: 'row clearfix'
7104        };
7105     }
7106     
7107     
7108 });
7109
7110  
7111
7112  /*
7113  * - LGPL
7114  *
7115  * pagination
7116  * 
7117  */
7118
7119 /**
7120  * @class Roo.bootstrap.Pagination
7121  * @extends Roo.bootstrap.Component
7122  * Bootstrap Pagination class
7123  * @cfg {String} size xs | sm | md | lg
7124  * @cfg {Boolean} inverse false | true
7125  * 
7126  * @constructor
7127  * Create a new Pagination
7128  * @param {Object} config The config object
7129  */
7130
7131 Roo.bootstrap.Pagination = function(config){
7132     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7133 };
7134
7135 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7136     
7137     cls: false,
7138     size: false,
7139     inverse: false,
7140     
7141     getAutoCreate : function(){
7142         var cfg = {
7143             tag: 'ul',
7144                 cls: 'pagination'
7145         };
7146         if (this.inverse) {
7147             cfg.cls += ' inverse';
7148         }
7149         if (this.html) {
7150             cfg.html=this.html;
7151         }
7152         if (this.cls) {
7153             cfg.cls += " " + this.cls;
7154         }
7155         return cfg;
7156     }
7157    
7158 });
7159
7160  
7161
7162  /*
7163  * - LGPL
7164  *
7165  * Pagination item
7166  * 
7167  */
7168
7169
7170 /**
7171  * @class Roo.bootstrap.PaginationItem
7172  * @extends Roo.bootstrap.Component
7173  * Bootstrap PaginationItem class
7174  * @cfg {String} html text
7175  * @cfg {String} href the link
7176  * @cfg {Boolean} preventDefault (true | false) default true
7177  * @cfg {Boolean} active (true | false) default false
7178  * @cfg {Boolean} disabled default false
7179  * 
7180  * 
7181  * @constructor
7182  * Create a new PaginationItem
7183  * @param {Object} config The config object
7184  */
7185
7186
7187 Roo.bootstrap.PaginationItem = function(config){
7188     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7189     this.addEvents({
7190         // raw events
7191         /**
7192          * @event click
7193          * The raw click event for the entire grid.
7194          * @param {Roo.EventObject} e
7195          */
7196         "click" : true
7197     });
7198 };
7199
7200 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7201     
7202     href : false,
7203     html : false,
7204     preventDefault: true,
7205     active : false,
7206     cls : false,
7207     disabled: false,
7208     
7209     getAutoCreate : function(){
7210         var cfg= {
7211             tag: 'li',
7212             cn: [
7213                 {
7214                     tag : 'a',
7215                     href : this.href ? this.href : '#',
7216                     html : this.html ? this.html : ''
7217                 }
7218             ]
7219         };
7220         
7221         if(this.cls){
7222             cfg.cls = this.cls;
7223         }
7224         
7225         if(this.disabled){
7226             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7227         }
7228         
7229         if(this.active){
7230             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7231         }
7232         
7233         return cfg;
7234     },
7235     
7236     initEvents: function() {
7237         
7238         this.el.on('click', this.onClick, this);
7239         
7240     },
7241     onClick : function(e)
7242     {
7243         Roo.log('PaginationItem on click ');
7244         if(this.preventDefault){
7245             e.preventDefault();
7246         }
7247         
7248         if(this.disabled){
7249             return;
7250         }
7251         
7252         this.fireEvent('click', this, e);
7253     }
7254    
7255 });
7256
7257  
7258
7259  /*
7260  * - LGPL
7261  *
7262  * slider
7263  * 
7264  */
7265
7266
7267 /**
7268  * @class Roo.bootstrap.Slider
7269  * @extends Roo.bootstrap.Component
7270  * Bootstrap Slider class
7271  *    
7272  * @constructor
7273  * Create a new Slider
7274  * @param {Object} config The config object
7275  */
7276
7277 Roo.bootstrap.Slider = function(config){
7278     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7279 };
7280
7281 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7282     
7283     getAutoCreate : function(){
7284         
7285         var cfg = {
7286             tag: 'div',
7287             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7288             cn: [
7289                 {
7290                     tag: 'a',
7291                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7292                 }
7293             ]
7294         };
7295         
7296         return cfg;
7297     }
7298    
7299 });
7300
7301  /*
7302  * Based on:
7303  * Ext JS Library 1.1.1
7304  * Copyright(c) 2006-2007, Ext JS, LLC.
7305  *
7306  * Originally Released Under LGPL - original licence link has changed is not relivant.
7307  *
7308  * Fork - LGPL
7309  * <script type="text/javascript">
7310  */
7311  /**
7312  * @extends Roo.dd.DDProxy
7313  * @class Roo.grid.SplitDragZone
7314  * Support for Column Header resizing
7315  * @constructor
7316  * @param {Object} config
7317  */
7318 // private
7319 // This is a support class used internally by the Grid components
7320 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7321     this.grid = grid;
7322     this.view = grid.getView();
7323     this.proxy = this.view.resizeProxy;
7324     Roo.grid.SplitDragZone.superclass.constructor.call(
7325         this,
7326         hd, // ID
7327         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7328         {  // CONFIG
7329             dragElId : Roo.id(this.proxy.dom),
7330             resizeFrame:false
7331         }
7332     );
7333     
7334     this.setHandleElId(Roo.id(hd));
7335     if (hd2 !== false) {
7336         this.setOuterHandleElId(Roo.id(hd2));
7337     }
7338     
7339     this.scroll = false;
7340 };
7341 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7342     fly: Roo.Element.fly,
7343
7344     b4StartDrag : function(x, y){
7345         this.view.headersDisabled = true;
7346         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7347                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7348         );
7349         this.proxy.setHeight(h);
7350         
7351         // for old system colWidth really stored the actual width?
7352         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7353         // which in reality did not work.. - it worked only for fixed sizes
7354         // for resizable we need to use actual sizes.
7355         var w = this.cm.getColumnWidth(this.cellIndex);
7356         if (!this.view.mainWrap) {
7357             // bootstrap.
7358             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7359         }
7360         
7361         
7362         
7363         // this was w-this.grid.minColumnWidth;
7364         // doesnt really make sense? - w = thie curren width or the rendered one?
7365         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7366         this.resetConstraints();
7367         this.setXConstraint(minw, 1000);
7368         this.setYConstraint(0, 0);
7369         this.minX = x - minw;
7370         this.maxX = x + 1000;
7371         this.startPos = x;
7372         if (!this.view.mainWrap) { // this is Bootstrap code..
7373             this.getDragEl().style.display='block';
7374         }
7375         
7376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7377     },
7378
7379
7380     handleMouseDown : function(e){
7381         ev = Roo.EventObject.setEvent(e);
7382         var t = this.fly(ev.getTarget());
7383         if(t.hasClass("x-grid-split")){
7384             this.cellIndex = this.view.getCellIndex(t.dom);
7385             this.split = t.dom;
7386             this.cm = this.grid.colModel;
7387             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7388                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7389             }
7390         }
7391     },
7392
7393     endDrag : function(e){
7394         this.view.headersDisabled = false;
7395         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7396         var diff = endX - this.startPos;
7397         // 
7398         var w = this.cm.getColumnWidth(this.cellIndex);
7399         if (!this.view.mainWrap) {
7400             w = 0;
7401         }
7402         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7403     },
7404
7405     autoOffset : function(){
7406         this.setDelta(0,0);
7407     }
7408 });/*
7409  * Based on:
7410  * Ext JS Library 1.1.1
7411  * Copyright(c) 2006-2007, Ext JS, LLC.
7412  *
7413  * Originally Released Under LGPL - original licence link has changed is not relivant.
7414  *
7415  * Fork - LGPL
7416  * <script type="text/javascript">
7417  */
7418
7419 /**
7420  * @class Roo.grid.AbstractSelectionModel
7421  * @extends Roo.util.Observable
7422  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7423  * implemented by descendant classes.  This class should not be directly instantiated.
7424  * @constructor
7425  */
7426 Roo.grid.AbstractSelectionModel = function(){
7427     this.locked = false;
7428     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7429 };
7430
7431 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7432     /** @ignore Called by the grid automatically. Do not call directly. */
7433     init : function(grid){
7434         this.grid = grid;
7435         this.initEvents();
7436     },
7437
7438     /**
7439      * Locks the selections.
7440      */
7441     lock : function(){
7442         this.locked = true;
7443     },
7444
7445     /**
7446      * Unlocks the selections.
7447      */
7448     unlock : function(){
7449         this.locked = false;
7450     },
7451
7452     /**
7453      * Returns true if the selections are locked.
7454      * @return {Boolean}
7455      */
7456     isLocked : function(){
7457         return this.locked;
7458     }
7459 });/*
7460  * Based on:
7461  * Ext JS Library 1.1.1
7462  * Copyright(c) 2006-2007, Ext JS, LLC.
7463  *
7464  * Originally Released Under LGPL - original licence link has changed is not relivant.
7465  *
7466  * Fork - LGPL
7467  * <script type="text/javascript">
7468  */
7469 /**
7470  * @extends Roo.grid.AbstractSelectionModel
7471  * @class Roo.grid.RowSelectionModel
7472  * The default SelectionModel used by {@link Roo.grid.Grid}.
7473  * It supports multiple selections and keyboard selection/navigation. 
7474  * @constructor
7475  * @param {Object} config
7476  */
7477 Roo.grid.RowSelectionModel = function(config){
7478     Roo.apply(this, config);
7479     this.selections = new Roo.util.MixedCollection(false, function(o){
7480         return o.id;
7481     });
7482
7483     this.last = false;
7484     this.lastActive = false;
7485
7486     this.addEvents({
7487         /**
7488         * @event selectionchange
7489         * Fires when the selection changes
7490         * @param {SelectionModel} this
7491         */
7492        "selectionchange" : true,
7493        /**
7494         * @event afterselectionchange
7495         * Fires after the selection changes (eg. by key press or clicking)
7496         * @param {SelectionModel} this
7497         */
7498        "afterselectionchange" : true,
7499        /**
7500         * @event beforerowselect
7501         * Fires when a row is selected being selected, return false to cancel.
7502         * @param {SelectionModel} this
7503         * @param {Number} rowIndex The selected index
7504         * @param {Boolean} keepExisting False if other selections will be cleared
7505         */
7506        "beforerowselect" : true,
7507        /**
7508         * @event rowselect
7509         * Fires when a row is selected.
7510         * @param {SelectionModel} this
7511         * @param {Number} rowIndex The selected index
7512         * @param {Roo.data.Record} r The record
7513         */
7514        "rowselect" : true,
7515        /**
7516         * @event rowdeselect
7517         * Fires when a row is deselected.
7518         * @param {SelectionModel} this
7519         * @param {Number} rowIndex The selected index
7520         */
7521         "rowdeselect" : true
7522     });
7523     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7524     this.locked = false;
7525 };
7526
7527 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7528     /**
7529      * @cfg {Boolean} singleSelect
7530      * True to allow selection of only one row at a time (defaults to false)
7531      */
7532     singleSelect : false,
7533
7534     // private
7535     initEvents : function(){
7536
7537         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7538             this.grid.on("mousedown", this.handleMouseDown, this);
7539         }else{ // allow click to work like normal
7540             this.grid.on("rowclick", this.handleDragableRowClick, this);
7541         }
7542         // bootstrap does not have a view..
7543         var view = this.grid.view ? this.grid.view : this.grid;
7544         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7545             "up" : function(e){
7546                 if(!e.shiftKey){
7547                     this.selectPrevious(e.shiftKey);
7548                 }else if(this.last !== false && this.lastActive !== false){
7549                     var last = this.last;
7550                     this.selectRange(this.last,  this.lastActive-1);
7551                     view.focusRow(this.lastActive);
7552                     if(last !== false){
7553                         this.last = last;
7554                     }
7555                 }else{
7556                     this.selectFirstRow();
7557                 }
7558                 this.fireEvent("afterselectionchange", this);
7559             },
7560             "down" : function(e){
7561                 if(!e.shiftKey){
7562                     this.selectNext(e.shiftKey);
7563                 }else if(this.last !== false && this.lastActive !== false){
7564                     var last = this.last;
7565                     this.selectRange(this.last,  this.lastActive+1);
7566                     view.focusRow(this.lastActive);
7567                     if(last !== false){
7568                         this.last = last;
7569                     }
7570                 }else{
7571                     this.selectFirstRow();
7572                 }
7573                 this.fireEvent("afterselectionchange", this);
7574             },
7575             scope: this
7576         });
7577
7578          
7579         view.on("refresh", this.onRefresh, this);
7580         view.on("rowupdated", this.onRowUpdated, this);
7581         view.on("rowremoved", this.onRemove, this);
7582     },
7583
7584     // private
7585     onRefresh : function(){
7586         var ds = this.grid.ds, i, v = this.grid.view;
7587         var s = this.selections;
7588         s.each(function(r){
7589             if((i = ds.indexOfId(r.id)) != -1){
7590                 v.onRowSelect(i);
7591                 s.add(ds.getAt(i)); // updating the selection relate data
7592             }else{
7593                 s.remove(r);
7594             }
7595         });
7596     },
7597
7598     // private
7599     onRemove : function(v, index, r){
7600         this.selections.remove(r);
7601     },
7602
7603     // private
7604     onRowUpdated : function(v, index, r){
7605         if(this.isSelected(r)){
7606             v.onRowSelect(index);
7607         }
7608     },
7609
7610     /**
7611      * Select records.
7612      * @param {Array} records The records to select
7613      * @param {Boolean} keepExisting (optional) True to keep existing selections
7614      */
7615     selectRecords : function(records, keepExisting){
7616         if(!keepExisting){
7617             this.clearSelections();
7618         }
7619         var ds = this.grid.ds;
7620         for(var i = 0, len = records.length; i < len; i++){
7621             this.selectRow(ds.indexOf(records[i]), true);
7622         }
7623     },
7624
7625     /**
7626      * Gets the number of selected rows.
7627      * @return {Number}
7628      */
7629     getCount : function(){
7630         return this.selections.length;
7631     },
7632
7633     /**
7634      * Selects the first row in the grid.
7635      */
7636     selectFirstRow : function(){
7637         this.selectRow(0);
7638     },
7639
7640     /**
7641      * Select the last row.
7642      * @param {Boolean} keepExisting (optional) True to keep existing selections
7643      */
7644     selectLastRow : function(keepExisting){
7645         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7646     },
7647
7648     /**
7649      * Selects the row immediately following the last selected row.
7650      * @param {Boolean} keepExisting (optional) True to keep existing selections
7651      */
7652     selectNext : function(keepExisting){
7653         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7654             this.selectRow(this.last+1, keepExisting);
7655             var view = this.grid.view ? this.grid.view : this.grid;
7656             view.focusRow(this.last);
7657         }
7658     },
7659
7660     /**
7661      * Selects the row that precedes the last selected row.
7662      * @param {Boolean} keepExisting (optional) True to keep existing selections
7663      */
7664     selectPrevious : function(keepExisting){
7665         if(this.last){
7666             this.selectRow(this.last-1, keepExisting);
7667             var view = this.grid.view ? this.grid.view : this.grid;
7668             view.focusRow(this.last);
7669         }
7670     },
7671
7672     /**
7673      * Returns the selected records
7674      * @return {Array} Array of selected records
7675      */
7676     getSelections : function(){
7677         return [].concat(this.selections.items);
7678     },
7679
7680     /**
7681      * Returns the first selected record.
7682      * @return {Record}
7683      */
7684     getSelected : function(){
7685         return this.selections.itemAt(0);
7686     },
7687
7688
7689     /**
7690      * Clears all selections.
7691      */
7692     clearSelections : function(fast){
7693         if(this.locked) {
7694             return;
7695         }
7696         if(fast !== true){
7697             var ds = this.grid.ds;
7698             var s = this.selections;
7699             s.each(function(r){
7700                 this.deselectRow(ds.indexOfId(r.id));
7701             }, this);
7702             s.clear();
7703         }else{
7704             this.selections.clear();
7705         }
7706         this.last = false;
7707     },
7708
7709
7710     /**
7711      * Selects all rows.
7712      */
7713     selectAll : function(){
7714         if(this.locked) {
7715             return;
7716         }
7717         this.selections.clear();
7718         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7719             this.selectRow(i, true);
7720         }
7721     },
7722
7723     /**
7724      * Returns True if there is a selection.
7725      * @return {Boolean}
7726      */
7727     hasSelection : function(){
7728         return this.selections.length > 0;
7729     },
7730
7731     /**
7732      * Returns True if the specified row is selected.
7733      * @param {Number/Record} record The record or index of the record to check
7734      * @return {Boolean}
7735      */
7736     isSelected : function(index){
7737         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7738         return (r && this.selections.key(r.id) ? true : false);
7739     },
7740
7741     /**
7742      * Returns True if the specified record id is selected.
7743      * @param {String} id The id of record to check
7744      * @return {Boolean}
7745      */
7746     isIdSelected : function(id){
7747         return (this.selections.key(id) ? true : false);
7748     },
7749
7750     // private
7751     handleMouseDown : function(e, t)
7752     {
7753         var view = this.grid.view ? this.grid.view : this.grid;
7754         var rowIndex;
7755         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7756             return;
7757         };
7758         if(e.shiftKey && this.last !== false){
7759             var last = this.last;
7760             this.selectRange(last, rowIndex, e.ctrlKey);
7761             this.last = last; // reset the last
7762             view.focusRow(rowIndex);
7763         }else{
7764             var isSelected = this.isSelected(rowIndex);
7765             if(e.button !== 0 && isSelected){
7766                 view.focusRow(rowIndex);
7767             }else if(e.ctrlKey && isSelected){
7768                 this.deselectRow(rowIndex);
7769             }else if(!isSelected){
7770                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7771                 view.focusRow(rowIndex);
7772             }
7773         }
7774         this.fireEvent("afterselectionchange", this);
7775     },
7776     // private
7777     handleDragableRowClick :  function(grid, rowIndex, e) 
7778     {
7779         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7780             this.selectRow(rowIndex, false);
7781             var view = this.grid.view ? this.grid.view : this.grid;
7782             view.focusRow(rowIndex);
7783              this.fireEvent("afterselectionchange", this);
7784         }
7785     },
7786     
7787     /**
7788      * Selects multiple rows.
7789      * @param {Array} rows Array of the indexes of the row to select
7790      * @param {Boolean} keepExisting (optional) True to keep existing selections
7791      */
7792     selectRows : function(rows, keepExisting){
7793         if(!keepExisting){
7794             this.clearSelections();
7795         }
7796         for(var i = 0, len = rows.length; i < len; i++){
7797             this.selectRow(rows[i], true);
7798         }
7799     },
7800
7801     /**
7802      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7803      * @param {Number} startRow The index of the first row in the range
7804      * @param {Number} endRow The index of the last row in the range
7805      * @param {Boolean} keepExisting (optional) True to retain existing selections
7806      */
7807     selectRange : function(startRow, endRow, keepExisting){
7808         if(this.locked) {
7809             return;
7810         }
7811         if(!keepExisting){
7812             this.clearSelections();
7813         }
7814         if(startRow <= endRow){
7815             for(var i = startRow; i <= endRow; i++){
7816                 this.selectRow(i, true);
7817             }
7818         }else{
7819             for(var i = startRow; i >= endRow; i--){
7820                 this.selectRow(i, true);
7821             }
7822         }
7823     },
7824
7825     /**
7826      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7827      * @param {Number} startRow The index of the first row in the range
7828      * @param {Number} endRow The index of the last row in the range
7829      */
7830     deselectRange : function(startRow, endRow, preventViewNotify){
7831         if(this.locked) {
7832             return;
7833         }
7834         for(var i = startRow; i <= endRow; i++){
7835             this.deselectRow(i, preventViewNotify);
7836         }
7837     },
7838
7839     /**
7840      * Selects a row.
7841      * @param {Number} row The index of the row to select
7842      * @param {Boolean} keepExisting (optional) True to keep existing selections
7843      */
7844     selectRow : function(index, keepExisting, preventViewNotify){
7845         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7846             return;
7847         }
7848         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7849             if(!keepExisting || this.singleSelect){
7850                 this.clearSelections();
7851             }
7852             var r = this.grid.ds.getAt(index);
7853             this.selections.add(r);
7854             this.last = this.lastActive = index;
7855             if(!preventViewNotify){
7856                 var view = this.grid.view ? this.grid.view : this.grid;
7857                 view.onRowSelect(index);
7858             }
7859             this.fireEvent("rowselect", this, index, r);
7860             this.fireEvent("selectionchange", this);
7861         }
7862     },
7863
7864     /**
7865      * Deselects a row.
7866      * @param {Number} row The index of the row to deselect
7867      */
7868     deselectRow : function(index, preventViewNotify){
7869         if(this.locked) {
7870             return;
7871         }
7872         if(this.last == index){
7873             this.last = false;
7874         }
7875         if(this.lastActive == index){
7876             this.lastActive = false;
7877         }
7878         var r = this.grid.ds.getAt(index);
7879         this.selections.remove(r);
7880         if(!preventViewNotify){
7881             var view = this.grid.view ? this.grid.view : this.grid;
7882             view.onRowDeselect(index);
7883         }
7884         this.fireEvent("rowdeselect", this, index);
7885         this.fireEvent("selectionchange", this);
7886     },
7887
7888     // private
7889     restoreLast : function(){
7890         if(this._last){
7891             this.last = this._last;
7892         }
7893     },
7894
7895     // private
7896     acceptsNav : function(row, col, cm){
7897         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7898     },
7899
7900     // private
7901     onEditorKey : function(field, e){
7902         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7903         if(k == e.TAB){
7904             e.stopEvent();
7905             ed.completeEdit();
7906             if(e.shiftKey){
7907                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7908             }else{
7909                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7910             }
7911         }else if(k == e.ENTER && !e.ctrlKey){
7912             e.stopEvent();
7913             ed.completeEdit();
7914             if(e.shiftKey){
7915                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7916             }else{
7917                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7918             }
7919         }else if(k == e.ESC){
7920             ed.cancelEdit();
7921         }
7922         if(newCell){
7923             g.startEditing(newCell[0], newCell[1]);
7924         }
7925     }
7926 });/*
7927  * Based on:
7928  * Ext JS Library 1.1.1
7929  * Copyright(c) 2006-2007, Ext JS, LLC.
7930  *
7931  * Originally Released Under LGPL - original licence link has changed is not relivant.
7932  *
7933  * Fork - LGPL
7934  * <script type="text/javascript">
7935  */
7936  
7937
7938 /**
7939  * @class Roo.grid.ColumnModel
7940  * @extends Roo.util.Observable
7941  * This is the default implementation of a ColumnModel used by the Grid. It defines
7942  * the columns in the grid.
7943  * <br>Usage:<br>
7944  <pre><code>
7945  var colModel = new Roo.grid.ColumnModel([
7946         {header: "Ticker", width: 60, sortable: true, locked: true},
7947         {header: "Company Name", width: 150, sortable: true},
7948         {header: "Market Cap.", width: 100, sortable: true},
7949         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7950         {header: "Employees", width: 100, sortable: true, resizable: false}
7951  ]);
7952  </code></pre>
7953  * <p>
7954  
7955  * The config options listed for this class are options which may appear in each
7956  * individual column definition.
7957  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7958  * @constructor
7959  * @param {Object} config An Array of column config objects. See this class's
7960  * config objects for details.
7961 */
7962 Roo.grid.ColumnModel = function(config){
7963         /**
7964      * The config passed into the constructor
7965      */
7966     this.config = []; //config;
7967     this.lookup = {};
7968
7969     // if no id, create one
7970     // if the column does not have a dataIndex mapping,
7971     // map it to the order it is in the config
7972     for(var i = 0, len = config.length; i < len; i++){
7973         this.addColumn(config[i]);
7974         
7975     }
7976
7977     /**
7978      * The width of columns which have no width specified (defaults to 100)
7979      * @type Number
7980      */
7981     this.defaultWidth = 100;
7982
7983     /**
7984      * Default sortable of columns which have no sortable specified (defaults to false)
7985      * @type Boolean
7986      */
7987     this.defaultSortable = false;
7988
7989     this.addEvents({
7990         /**
7991              * @event widthchange
7992              * Fires when the width of a column changes.
7993              * @param {ColumnModel} this
7994              * @param {Number} columnIndex The column index
7995              * @param {Number} newWidth The new width
7996              */
7997             "widthchange": true,
7998         /**
7999              * @event headerchange
8000              * Fires when the text of a header changes.
8001              * @param {ColumnModel} this
8002              * @param {Number} columnIndex The column index
8003              * @param {Number} newText The new header text
8004              */
8005             "headerchange": true,
8006         /**
8007              * @event hiddenchange
8008              * Fires when a column is hidden or "unhidden".
8009              * @param {ColumnModel} this
8010              * @param {Number} columnIndex The column index
8011              * @param {Boolean} hidden true if hidden, false otherwise
8012              */
8013             "hiddenchange": true,
8014             /**
8015          * @event columnmoved
8016          * Fires when a column is moved.
8017          * @param {ColumnModel} this
8018          * @param {Number} oldIndex
8019          * @param {Number} newIndex
8020          */
8021         "columnmoved" : true,
8022         /**
8023          * @event columlockchange
8024          * Fires when a column's locked state is changed
8025          * @param {ColumnModel} this
8026          * @param {Number} colIndex
8027          * @param {Boolean} locked true if locked
8028          */
8029         "columnlockchange" : true
8030     });
8031     Roo.grid.ColumnModel.superclass.constructor.call(this);
8032 };
8033 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8034     /**
8035      * @cfg {String} header The header text to display in the Grid view.
8036      */
8037         /**
8038      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8039      */
8040         /**
8041      * @cfg {String} smHeader Header at Bootsrap Small width
8042      */
8043         /**
8044      * @cfg {String} mdHeader Header at Bootsrap Medium width
8045      */
8046         /**
8047      * @cfg {String} lgHeader Header at Bootsrap Large width
8048      */
8049         /**
8050      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8051      */
8052     /**
8053      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8054      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8055      * specified, the column's index is used as an index into the Record's data Array.
8056      */
8057     /**
8058      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8059      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8060      */
8061     /**
8062      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8063      * Defaults to the value of the {@link #defaultSortable} property.
8064      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8065      */
8066     /**
8067      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8068      */
8069     /**
8070      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8071      */
8072     /**
8073      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8074      */
8075     /**
8076      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8077      */
8078     /**
8079      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8080      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8081      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8082      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8083      */
8084        /**
8085      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8086      */
8087     /**
8088      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8089      */
8090     /**
8091      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8092      */
8093     /**
8094      * @cfg {String} cursor (Optional)
8095      */
8096     /**
8097      * @cfg {String} tooltip (Optional)
8098      */
8099     /**
8100      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8101      */
8102     /**
8103      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8104      */
8105     /**
8106      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8107      */
8108     /**
8109      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8110      */
8111         /**
8112      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8113      */
8114     /**
8115      * Returns the id of the column at the specified index.
8116      * @param {Number} index The column index
8117      * @return {String} the id
8118      */
8119     getColumnId : function(index){
8120         return this.config[index].id;
8121     },
8122
8123     /**
8124      * Returns the column for a specified id.
8125      * @param {String} id The column id
8126      * @return {Object} the column
8127      */
8128     getColumnById : function(id){
8129         return this.lookup[id];
8130     },
8131
8132     
8133     /**
8134      * Returns the column Object for a specified dataIndex.
8135      * @param {String} dataIndex The column dataIndex
8136      * @return {Object|Boolean} the column or false if not found
8137      */
8138     getColumnByDataIndex: function(dataIndex){
8139         var index = this.findColumnIndex(dataIndex);
8140         return index > -1 ? this.config[index] : false;
8141     },
8142     
8143     /**
8144      * Returns the index for a specified column id.
8145      * @param {String} id The column id
8146      * @return {Number} the index, or -1 if not found
8147      */
8148     getIndexById : function(id){
8149         for(var i = 0, len = this.config.length; i < len; i++){
8150             if(this.config[i].id == id){
8151                 return i;
8152             }
8153         }
8154         return -1;
8155     },
8156     
8157     /**
8158      * Returns the index for a specified column dataIndex.
8159      * @param {String} dataIndex The column dataIndex
8160      * @return {Number} the index, or -1 if not found
8161      */
8162     
8163     findColumnIndex : function(dataIndex){
8164         for(var i = 0, len = this.config.length; i < len; i++){
8165             if(this.config[i].dataIndex == dataIndex){
8166                 return i;
8167             }
8168         }
8169         return -1;
8170     },
8171     
8172     
8173     moveColumn : function(oldIndex, newIndex){
8174         var c = this.config[oldIndex];
8175         this.config.splice(oldIndex, 1);
8176         this.config.splice(newIndex, 0, c);
8177         this.dataMap = null;
8178         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8179     },
8180
8181     isLocked : function(colIndex){
8182         return this.config[colIndex].locked === true;
8183     },
8184
8185     setLocked : function(colIndex, value, suppressEvent){
8186         if(this.isLocked(colIndex) == value){
8187             return;
8188         }
8189         this.config[colIndex].locked = value;
8190         if(!suppressEvent){
8191             this.fireEvent("columnlockchange", this, colIndex, value);
8192         }
8193     },
8194
8195     getTotalLockedWidth : function(){
8196         var totalWidth = 0;
8197         for(var i = 0; i < this.config.length; i++){
8198             if(this.isLocked(i) && !this.isHidden(i)){
8199                 this.totalWidth += this.getColumnWidth(i);
8200             }
8201         }
8202         return totalWidth;
8203     },
8204
8205     getLockedCount : function(){
8206         for(var i = 0, len = this.config.length; i < len; i++){
8207             if(!this.isLocked(i)){
8208                 return i;
8209             }
8210         }
8211         
8212         return this.config.length;
8213     },
8214
8215     /**
8216      * Returns the number of columns.
8217      * @return {Number}
8218      */
8219     getColumnCount : function(visibleOnly){
8220         if(visibleOnly === true){
8221             var c = 0;
8222             for(var i = 0, len = this.config.length; i < len; i++){
8223                 if(!this.isHidden(i)){
8224                     c++;
8225                 }
8226             }
8227             return c;
8228         }
8229         return this.config.length;
8230     },
8231
8232     /**
8233      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8234      * @param {Function} fn
8235      * @param {Object} scope (optional)
8236      * @return {Array} result
8237      */
8238     getColumnsBy : function(fn, scope){
8239         var r = [];
8240         for(var i = 0, len = this.config.length; i < len; i++){
8241             var c = this.config[i];
8242             if(fn.call(scope||this, c, i) === true){
8243                 r[r.length] = c;
8244             }
8245         }
8246         return r;
8247     },
8248
8249     /**
8250      * Returns true if the specified column is sortable.
8251      * @param {Number} col The column index
8252      * @return {Boolean}
8253      */
8254     isSortable : function(col){
8255         if(typeof this.config[col].sortable == "undefined"){
8256             return this.defaultSortable;
8257         }
8258         return this.config[col].sortable;
8259     },
8260
8261     /**
8262      * Returns the rendering (formatting) function defined for the column.
8263      * @param {Number} col The column index.
8264      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8265      */
8266     getRenderer : function(col){
8267         if(!this.config[col].renderer){
8268             return Roo.grid.ColumnModel.defaultRenderer;
8269         }
8270         return this.config[col].renderer;
8271     },
8272
8273     /**
8274      * Sets the rendering (formatting) function for a column.
8275      * @param {Number} col The column index
8276      * @param {Function} fn The function to use to process the cell's raw data
8277      * to return HTML markup for the grid view. The render function is called with
8278      * the following parameters:<ul>
8279      * <li>Data value.</li>
8280      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8281      * <li>css A CSS style string to apply to the table cell.</li>
8282      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8283      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8284      * <li>Row index</li>
8285      * <li>Column index</li>
8286      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8287      */
8288     setRenderer : function(col, fn){
8289         this.config[col].renderer = fn;
8290     },
8291
8292     /**
8293      * Returns the width for the specified column.
8294      * @param {Number} col The column index
8295      * @param (optional) {String} gridSize bootstrap width size.
8296      * @return {Number}
8297      */
8298     getColumnWidth : function(col, gridSize)
8299         {
8300                 var cfg = this.config[col];
8301                 
8302                 if (typeof(gridSize) == 'undefined') {
8303                         return cfg.width * 1 || this.defaultWidth;
8304                 }
8305                 if (gridSize === false) { // if we set it..
8306                         return cfg.width || false;
8307                 }
8308                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8309                 
8310                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8311                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8312                                 continue;
8313                         }
8314                         return cfg[ sizes[i] ];
8315                 }
8316                 return 1;
8317                 
8318     },
8319
8320     /**
8321      * Sets the width for a column.
8322      * @param {Number} col The column index
8323      * @param {Number} width The new width
8324      */
8325     setColumnWidth : function(col, width, suppressEvent){
8326         this.config[col].width = width;
8327         this.totalWidth = null;
8328         if(!suppressEvent){
8329              this.fireEvent("widthchange", this, col, width);
8330         }
8331     },
8332
8333     /**
8334      * Returns the total width of all columns.
8335      * @param {Boolean} includeHidden True to include hidden column widths
8336      * @return {Number}
8337      */
8338     getTotalWidth : function(includeHidden){
8339         if(!this.totalWidth){
8340             this.totalWidth = 0;
8341             for(var i = 0, len = this.config.length; i < len; i++){
8342                 if(includeHidden || !this.isHidden(i)){
8343                     this.totalWidth += this.getColumnWidth(i);
8344                 }
8345             }
8346         }
8347         return this.totalWidth;
8348     },
8349
8350     /**
8351      * Returns the header for the specified column.
8352      * @param {Number} col The column index
8353      * @return {String}
8354      */
8355     getColumnHeader : function(col){
8356         return this.config[col].header;
8357     },
8358
8359     /**
8360      * Sets the header for a column.
8361      * @param {Number} col The column index
8362      * @param {String} header The new header
8363      */
8364     setColumnHeader : function(col, header){
8365         this.config[col].header = header;
8366         this.fireEvent("headerchange", this, col, header);
8367     },
8368
8369     /**
8370      * Returns the tooltip for the specified column.
8371      * @param {Number} col The column index
8372      * @return {String}
8373      */
8374     getColumnTooltip : function(col){
8375             return this.config[col].tooltip;
8376     },
8377     /**
8378      * Sets the tooltip for a column.
8379      * @param {Number} col The column index
8380      * @param {String} tooltip The new tooltip
8381      */
8382     setColumnTooltip : function(col, tooltip){
8383             this.config[col].tooltip = tooltip;
8384     },
8385
8386     /**
8387      * Returns the dataIndex for the specified column.
8388      * @param {Number} col The column index
8389      * @return {Number}
8390      */
8391     getDataIndex : function(col){
8392         return this.config[col].dataIndex;
8393     },
8394
8395     /**
8396      * Sets the dataIndex for a column.
8397      * @param {Number} col The column index
8398      * @param {Number} dataIndex The new dataIndex
8399      */
8400     setDataIndex : function(col, dataIndex){
8401         this.config[col].dataIndex = dataIndex;
8402     },
8403
8404     
8405     
8406     /**
8407      * Returns true if the cell is editable.
8408      * @param {Number} colIndex The column index
8409      * @param {Number} rowIndex The row index - this is nto actually used..?
8410      * @return {Boolean}
8411      */
8412     isCellEditable : function(colIndex, rowIndex){
8413         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8414     },
8415
8416     /**
8417      * Returns the editor defined for the cell/column.
8418      * return false or null to disable editing.
8419      * @param {Number} colIndex The column index
8420      * @param {Number} rowIndex The row index
8421      * @return {Object}
8422      */
8423     getCellEditor : function(colIndex, rowIndex){
8424         return this.config[colIndex].editor;
8425     },
8426
8427     /**
8428      * Sets if a column is editable.
8429      * @param {Number} col The column index
8430      * @param {Boolean} editable True if the column is editable
8431      */
8432     setEditable : function(col, editable){
8433         this.config[col].editable = editable;
8434     },
8435
8436
8437     /**
8438      * Returns true if the column is hidden.
8439      * @param {Number} colIndex The column index
8440      * @return {Boolean}
8441      */
8442     isHidden : function(colIndex){
8443         return this.config[colIndex].hidden;
8444     },
8445
8446
8447     /**
8448      * Returns true if the column width cannot be changed
8449      */
8450     isFixed : function(colIndex){
8451         return this.config[colIndex].fixed;
8452     },
8453
8454     /**
8455      * Returns true if the column can be resized
8456      * @return {Boolean}
8457      */
8458     isResizable : function(colIndex){
8459         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8460     },
8461     /**
8462      * Sets if a column is hidden.
8463      * @param {Number} colIndex The column index
8464      * @param {Boolean} hidden True if the column is hidden
8465      */
8466     setHidden : function(colIndex, hidden){
8467         this.config[colIndex].hidden = hidden;
8468         this.totalWidth = null;
8469         this.fireEvent("hiddenchange", this, colIndex, hidden);
8470     },
8471
8472     /**
8473      * Sets the editor for a column.
8474      * @param {Number} col The column index
8475      * @param {Object} editor The editor object
8476      */
8477     setEditor : function(col, editor){
8478         this.config[col].editor = editor;
8479     },
8480     /**
8481      * Add a column (experimental...) - defaults to adding to the end..
8482      * @param {Object} config 
8483     */
8484     addColumn : function(c)
8485     {
8486     
8487         var i = this.config.length;
8488         this.config[i] = c;
8489         
8490         if(typeof c.dataIndex == "undefined"){
8491             c.dataIndex = i;
8492         }
8493         if(typeof c.renderer == "string"){
8494             c.renderer = Roo.util.Format[c.renderer];
8495         }
8496         if(typeof c.id == "undefined"){
8497             c.id = Roo.id();
8498         }
8499         if(c.editor && c.editor.xtype){
8500             c.editor  = Roo.factory(c.editor, Roo.grid);
8501         }
8502         if(c.editor && c.editor.isFormField){
8503             c.editor = new Roo.grid.GridEditor(c.editor);
8504         }
8505         this.lookup[c.id] = c;
8506     }
8507     
8508 });
8509
8510 Roo.grid.ColumnModel.defaultRenderer = function(value)
8511 {
8512     if(typeof value == "object") {
8513         return value;
8514     }
8515         if(typeof value == "string" && value.length < 1){
8516             return "&#160;";
8517         }
8518     
8519         return String.format("{0}", value);
8520 };
8521
8522 // Alias for backwards compatibility
8523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8524 /*
8525  * Based on:
8526  * Ext JS Library 1.1.1
8527  * Copyright(c) 2006-2007, Ext JS, LLC.
8528  *
8529  * Originally Released Under LGPL - original licence link has changed is not relivant.
8530  *
8531  * Fork - LGPL
8532  * <script type="text/javascript">
8533  */
8534  
8535 /**
8536  * @class Roo.LoadMask
8537  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8538  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8539  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8540  * element's UpdateManager load indicator and will be destroyed after the initial load.
8541  * @constructor
8542  * Create a new LoadMask
8543  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8544  * @param {Object} config The config object
8545  */
8546 Roo.LoadMask = function(el, config){
8547     this.el = Roo.get(el);
8548     Roo.apply(this, config);
8549     if(this.store){
8550         this.store.on('beforeload', this.onBeforeLoad, this);
8551         this.store.on('load', this.onLoad, this);
8552         this.store.on('loadexception', this.onLoadException, this);
8553         this.removeMask = false;
8554     }else{
8555         var um = this.el.getUpdateManager();
8556         um.showLoadIndicator = false; // disable the default indicator
8557         um.on('beforeupdate', this.onBeforeLoad, this);
8558         um.on('update', this.onLoad, this);
8559         um.on('failure', this.onLoad, this);
8560         this.removeMask = true;
8561     }
8562 };
8563
8564 Roo.LoadMask.prototype = {
8565     /**
8566      * @cfg {Boolean} removeMask
8567      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8568      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8569      */
8570     removeMask : false,
8571     /**
8572      * @cfg {String} msg
8573      * The text to display in a centered loading message box (defaults to 'Loading...')
8574      */
8575     msg : 'Loading...',
8576     /**
8577      * @cfg {String} msgCls
8578      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8579      */
8580     msgCls : 'x-mask-loading',
8581
8582     /**
8583      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8584      * @type Boolean
8585      */
8586     disabled: false,
8587
8588     /**
8589      * Disables the mask to prevent it from being displayed
8590      */
8591     disable : function(){
8592        this.disabled = true;
8593     },
8594
8595     /**
8596      * Enables the mask so that it can be displayed
8597      */
8598     enable : function(){
8599         this.disabled = false;
8600     },
8601     
8602     onLoadException : function()
8603     {
8604         Roo.log(arguments);
8605         
8606         if (typeof(arguments[3]) != 'undefined') {
8607             Roo.MessageBox.alert("Error loading",arguments[3]);
8608         } 
8609         /*
8610         try {
8611             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8612                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8613             }   
8614         } catch(e) {
8615             
8616         }
8617         */
8618     
8619         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8620     },
8621     // private
8622     onLoad : function()
8623     {
8624         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8625     },
8626
8627     // private
8628     onBeforeLoad : function(){
8629         if(!this.disabled){
8630             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8631         }
8632     },
8633
8634     // private
8635     destroy : function(){
8636         if(this.store){
8637             this.store.un('beforeload', this.onBeforeLoad, this);
8638             this.store.un('load', this.onLoad, this);
8639             this.store.un('loadexception', this.onLoadException, this);
8640         }else{
8641             var um = this.el.getUpdateManager();
8642             um.un('beforeupdate', this.onBeforeLoad, this);
8643             um.un('update', this.onLoad, this);
8644             um.un('failure', this.onLoad, this);
8645         }
8646     }
8647 };/**
8648  * @class Roo.bootstrap.Table
8649  * @licence LGBL
8650  * @extends Roo.bootstrap.Component
8651  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8652  * Similar to Roo.grid.Grid
8653  * <pre><code>
8654  var table = Roo.factory({
8655     xtype : 'Table',
8656     xns : Roo.bootstrap,
8657     autoSizeColumns: true,
8658     
8659     
8660     store : {
8661         xtype : 'Store',
8662         xns : Roo.data,
8663         remoteSort : true,
8664         sortInfo : { direction : 'ASC', field: 'name' },
8665         proxy : {
8666            xtype : 'HttpProxy',
8667            xns : Roo.data,
8668            method : 'GET',
8669            url : 'https://example.com/some.data.url.json'
8670         },
8671         reader : {
8672            xtype : 'JsonReader',
8673            xns : Roo.data,
8674            fields : [ 'id', 'name', whatever' ],
8675            id : 'id',
8676            root : 'data'
8677         }
8678     },
8679     cm : [
8680         {
8681             xtype : 'ColumnModel',
8682             xns : Roo.grid,
8683             align : 'center',
8684             cursor : 'pointer',
8685             dataIndex : 'is_in_group',
8686             header : "Name",
8687             sortable : true,
8688             renderer : function(v, x , r) {  
8689             
8690                 return String.format("{0}", v)
8691             }
8692             width : 3
8693         } // more columns..
8694     ],
8695     selModel : {
8696         xtype : 'RowSelectionModel',
8697         xns : Roo.bootstrap.Table
8698         // you can add listeners to catch selection change here....
8699     }
8700      
8701
8702  });
8703  // set any options
8704  grid.render(Roo.get("some-div"));
8705 </code></pre>
8706
8707 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8708
8709
8710
8711  *
8712  * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8713  * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8714  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8715  * 
8716  * @cfg {String} cls table class
8717  *
8718  * 
8719  * @cfg {boolean} striped Should the rows be alternative striped
8720  * @cfg {boolean} bordered Add borders to the table
8721  * @cfg {boolean} hover Add hover highlighting
8722  * @cfg {boolean} condensed Format condensed
8723  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
8724  *                also adds table-responsive (see bootstrap docs for details)
8725  * @cfg {Boolean} loadMask (true|false) default false
8726  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8727  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8728  * @cfg {Boolean} rowSelection (true|false) default false
8729  * @cfg {Boolean} cellSelection (true|false) default false
8730  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8731  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8732  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8733  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8734  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8735  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8736  * 
8737  * @constructor
8738  * Create a new Table
8739  * @param {Object} config The config object
8740  */
8741
8742 Roo.bootstrap.Table = function(config)
8743 {
8744     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8745      
8746     // BC...
8747     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8748     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8749     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8750     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8751     
8752     this.view = this; // compat with grid.
8753     
8754     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8755     if (this.sm) {
8756         this.sm.grid = this;
8757         this.selModel = Roo.factory(this.sm, Roo.grid);
8758         this.sm = this.selModel;
8759         this.sm.xmodule = this.xmodule || false;
8760     }
8761     
8762     if (this.cm && typeof(this.cm.config) == 'undefined') {
8763         this.colModel = new Roo.grid.ColumnModel(this.cm);
8764         this.cm = this.colModel;
8765         this.cm.xmodule = this.xmodule || false;
8766     }
8767     if (this.store) {
8768         this.store= Roo.factory(this.store, Roo.data);
8769         this.ds = this.store;
8770         this.ds.xmodule = this.xmodule || false;
8771          
8772     }
8773     if (this.footer && this.store) {
8774         this.footer.dataSource = this.ds;
8775         this.footer = Roo.factory(this.footer);
8776     }
8777     
8778     /** @private */
8779     this.addEvents({
8780         /**
8781          * @event cellclick
8782          * Fires when a cell is clicked
8783          * @param {Roo.bootstrap.Table} this
8784          * @param {Roo.Element} el
8785          * @param {Number} rowIndex
8786          * @param {Number} columnIndex
8787          * @param {Roo.EventObject} e
8788          */
8789         "cellclick" : true,
8790         /**
8791          * @event celldblclick
8792          * Fires when a cell is double clicked
8793          * @param {Roo.bootstrap.Table} this
8794          * @param {Roo.Element} el
8795          * @param {Number} rowIndex
8796          * @param {Number} columnIndex
8797          * @param {Roo.EventObject} e
8798          */
8799         "celldblclick" : true,
8800         /**
8801          * @event rowclick
8802          * Fires when a row is clicked
8803          * @param {Roo.bootstrap.Table} this
8804          * @param {Roo.Element} el
8805          * @param {Number} rowIndex
8806          * @param {Roo.EventObject} e
8807          */
8808         "rowclick" : true,
8809         /**
8810          * @event rowdblclick
8811          * Fires when a row is double clicked
8812          * @param {Roo.bootstrap.Table} this
8813          * @param {Roo.Element} el
8814          * @param {Number} rowIndex
8815          * @param {Roo.EventObject} e
8816          */
8817         "rowdblclick" : true,
8818         /**
8819          * @event mouseover
8820          * Fires when a mouseover occur
8821          * @param {Roo.bootstrap.Table} this
8822          * @param {Roo.Element} el
8823          * @param {Number} rowIndex
8824          * @param {Number} columnIndex
8825          * @param {Roo.EventObject} e
8826          */
8827         "mouseover" : true,
8828         /**
8829          * @event mouseout
8830          * Fires when a mouseout occur
8831          * @param {Roo.bootstrap.Table} this
8832          * @param {Roo.Element} el
8833          * @param {Number} rowIndex
8834          * @param {Number} columnIndex
8835          * @param {Roo.EventObject} e
8836          */
8837         "mouseout" : true,
8838         /**
8839          * @event rowclass
8840          * Fires when a row is rendered, so you can change add a style to it.
8841          * @param {Roo.bootstrap.Table} this
8842          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8843          */
8844         'rowclass' : true,
8845           /**
8846          * @event rowsrendered
8847          * Fires when all the  rows have been rendered
8848          * @param {Roo.bootstrap.Table} this
8849          */
8850         'rowsrendered' : true,
8851         /**
8852          * @event contextmenu
8853          * The raw contextmenu event for the entire grid.
8854          * @param {Roo.EventObject} e
8855          */
8856         "contextmenu" : true,
8857         /**
8858          * @event rowcontextmenu
8859          * Fires when a row is right clicked
8860          * @param {Roo.bootstrap.Table} this
8861          * @param {Number} rowIndex
8862          * @param {Roo.EventObject} e
8863          */
8864         "rowcontextmenu" : true,
8865         /**
8866          * @event cellcontextmenu
8867          * Fires when a cell is right clicked
8868          * @param {Roo.bootstrap.Table} this
8869          * @param {Number} rowIndex
8870          * @param {Number} cellIndex
8871          * @param {Roo.EventObject} e
8872          */
8873          "cellcontextmenu" : true,
8874          /**
8875          * @event headercontextmenu
8876          * Fires when a header is right clicked
8877          * @param {Roo.bootstrap.Table} this
8878          * @param {Number} columnIndex
8879          * @param {Roo.EventObject} e
8880          */
8881         "headercontextmenu" : true,
8882         /**
8883          * @event mousedown
8884          * The raw mousedown event for the entire grid.
8885          * @param {Roo.EventObject} e
8886          */
8887         "mousedown" : true
8888         
8889     });
8890 };
8891
8892 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8893     
8894     cls: false,
8895     
8896     striped : false,
8897     scrollBody : false,
8898     bordered: false,
8899     hover:  false,
8900     condensed : false,
8901     responsive : false,
8902     sm : false,
8903     cm : false,
8904     store : false,
8905     loadMask : false,
8906     footerShow : true,
8907     headerShow : true,
8908     enableColumnResize: true,
8909   
8910     rowSelection : false,
8911     cellSelection : false,
8912     layout : false,
8913
8914     minColumnWidth : 50,
8915     
8916     // Roo.Element - the tbody
8917     bodyEl: false,  // <tbody> Roo.Element - thead element    
8918     headEl: false,  // <thead> Roo.Element - thead element
8919     resizeProxy : false, // proxy element for dragging?
8920
8921
8922     
8923     container: false, // used by gridpanel...
8924     
8925     lazyLoad : false,
8926     
8927     CSS : Roo.util.CSS,
8928     
8929     auto_hide_footer : false,
8930     
8931     view: false, // actually points to this..
8932     
8933     getAutoCreate : function()
8934     {
8935         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8936         
8937         cfg = {
8938             tag: 'table',
8939             cls : 'table', 
8940             cn : []
8941         };
8942         // this get's auto added by panel.Grid
8943         if (this.scrollBody) {
8944             cfg.cls += ' table-body-fixed';
8945         }    
8946         if (this.striped) {
8947             cfg.cls += ' table-striped';
8948         }
8949         
8950         if (this.hover) {
8951             cfg.cls += ' table-hover';
8952         }
8953         if (this.bordered) {
8954             cfg.cls += ' table-bordered';
8955         }
8956         if (this.condensed) {
8957             cfg.cls += ' table-condensed';
8958         }
8959         
8960         if (this.responsive) {
8961             cfg.cls += ' table-responsive';
8962         }
8963         
8964         if (this.cls) {
8965             cfg.cls+=  ' ' +this.cls;
8966         }
8967         
8968         
8969         
8970         if (this.layout) {
8971             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8972         }
8973         
8974         if(this.store || this.cm){
8975             if(this.headerShow){
8976                 cfg.cn.push(this.renderHeader());
8977             }
8978             
8979             cfg.cn.push(this.renderBody());
8980             
8981             if(this.footerShow){
8982                 cfg.cn.push(this.renderFooter());
8983             }
8984             // where does this come from?
8985             //cfg.cls+=  ' TableGrid';
8986         }
8987         
8988         return { cn : [ cfg ] };
8989     },
8990     
8991     initEvents : function()
8992     {   
8993         if(!this.store || !this.cm){
8994             return;
8995         }
8996         if (this.selModel) {
8997             this.selModel.initEvents();
8998         }
8999         
9000         
9001         //Roo.log('initEvents with ds!!!!');
9002         
9003         this.bodyEl = this.el.select('tbody', true).first();
9004         this.headEl = this.el.select('thead', true).first();
9005         this.mainFoot = this.el.select('tfoot', true).first();
9006         
9007         
9008         
9009         
9010         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9011             e.on('click', this.sort, this);
9012         }, this);
9013         
9014         
9015         // why is this done????? = it breaks dialogs??
9016         //this.parent().el.setStyle('position', 'relative');
9017         
9018         
9019         if (this.footer) {
9020             this.footer.parentId = this.id;
9021             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9022             
9023             if(this.lazyLoad){
9024                 this.el.select('tfoot tr td').first().addClass('hide');
9025             }
9026         } 
9027         
9028         if(this.loadMask) {
9029             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9030         }
9031         
9032         this.store.on('load', this.onLoad, this);
9033         this.store.on('beforeload', this.onBeforeLoad, this);
9034         this.store.on('update', this.onUpdate, this);
9035         this.store.on('add', this.onAdd, this);
9036         this.store.on("clear", this.clear, this);
9037         
9038         this.el.on("contextmenu", this.onContextMenu, this);
9039         
9040         
9041         this.cm.on("headerchange", this.onHeaderChange, this);
9042         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9043
9044  //?? does bodyEl get replaced on render?
9045         this.bodyEl.on("click", this.onClick, this);
9046         this.bodyEl.on("dblclick", this.onDblClick, this);        
9047         this.bodyEl.on('scroll', this.onBodyScroll, this);
9048
9049         // guessing mainbody will work - this relays usually caught by selmodel at present.
9050         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9051   
9052   
9053         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9054         
9055   
9056         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9057             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9058         }
9059         
9060         this.initCSS();
9061     },
9062     // Compatibility with grid - we implement all the view features at present.
9063     getView : function()
9064     {
9065         return this;
9066     },
9067     
9068     initCSS : function()
9069     {
9070         
9071         
9072         var cm = this.cm, styles = [];
9073         this.CSS.removeStyleSheet(this.id + '-cssrules');
9074         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9075         // we can honour xs/sm/md/xl  as widths...
9076         // we first have to decide what widht we are currently at...
9077         var sz = Roo.getGridSize();
9078         
9079         var total = 0;
9080         var last = -1;
9081         var cols = []; // visable cols.
9082         var total_abs = 0;
9083         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9084             var w = cm.getColumnWidth(i, false);
9085             if(cm.isHidden(i)){
9086                 cols.push( { rel : false, abs : 0 });
9087                 continue;
9088             }
9089             if (w !== false) {
9090                 cols.push( { rel : false, abs : w });
9091                 total_abs += w;
9092                 last = i; // not really..
9093                 continue;
9094             }
9095             var w = cm.getColumnWidth(i, sz);
9096             if (w > 0) {
9097                 last = i
9098             }
9099             total += w;
9100             cols.push( { rel : w, abs : false });
9101         }
9102         
9103         var avail = this.bodyEl.dom.clientWidth - total_abs;
9104         
9105         var unitWidth = Math.floor(avail / total);
9106         var rem = avail - (unitWidth * total);
9107         
9108         var hidden, width, pos = 0 , splithide , left;
9109         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9110             
9111             hidden = 'display:none;';
9112             left = '';
9113             width  = 'width:0px;';
9114             splithide = '';
9115             if(!cm.isHidden(i)){
9116                 hidden = '';
9117                 
9118                 
9119                 // we can honour xs/sm/md/xl ?
9120                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9121                 if (w===0) {
9122                     hidden = 'display:none;';
9123                 }
9124                 // width should return a small number...
9125                 if (i == last) {
9126                     w+=rem; // add the remaining with..
9127                 }
9128                 pos += w;
9129                 left = "left:" + (pos -4) + "px;";
9130                 width = "width:" + w+ "px;";
9131                 
9132             }
9133             if (this.responsive) {
9134                 width = '';
9135                 left = '';
9136                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9137                 splithide = 'display: none;';
9138             }
9139             
9140             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9141             if (this.headEl) {
9142                 if (i == last) {
9143                     splithide = 'display:none;';
9144                 }
9145                 
9146                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9147                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9148                 );
9149             }
9150             
9151         }
9152         //Roo.log(styles.join(''));
9153         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9154         
9155     },
9156     
9157     
9158     
9159     onContextMenu : function(e, t)
9160     {
9161         this.processEvent("contextmenu", e);
9162     },
9163     
9164     processEvent : function(name, e)
9165     {
9166         if (name != 'touchstart' ) {
9167             this.fireEvent(name, e);    
9168         }
9169         
9170         var t = e.getTarget();
9171         
9172         var cell = Roo.get(t);
9173         
9174         if(!cell){
9175             return;
9176         }
9177         
9178         if(cell.findParent('tfoot', false, true)){
9179             return;
9180         }
9181         
9182         if(cell.findParent('thead', false, true)){
9183             
9184             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9185                 cell = Roo.get(t).findParent('th', false, true);
9186                 if (!cell) {
9187                     Roo.log("failed to find th in thead?");
9188                     Roo.log(e.getTarget());
9189                     return;
9190                 }
9191             }
9192             
9193             var cellIndex = cell.dom.cellIndex;
9194             
9195             var ename = name == 'touchstart' ? 'click' : name;
9196             this.fireEvent("header" + ename, this, cellIndex, e);
9197             
9198             return;
9199         }
9200         
9201         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9202             cell = Roo.get(t).findParent('td', false, true);
9203             if (!cell) {
9204                 Roo.log("failed to find th in tbody?");
9205                 Roo.log(e.getTarget());
9206                 return;
9207             }
9208         }
9209         
9210         var row = cell.findParent('tr', false, true);
9211         var cellIndex = cell.dom.cellIndex;
9212         var rowIndex = row.dom.rowIndex - 1;
9213         
9214         if(row !== false){
9215             
9216             this.fireEvent("row" + name, this, rowIndex, e);
9217             
9218             if(cell !== false){
9219             
9220                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9221             }
9222         }
9223         
9224     },
9225     
9226     onMouseover : function(e, el)
9227     {
9228         var cell = Roo.get(el);
9229         
9230         if(!cell){
9231             return;
9232         }
9233         
9234         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9235             cell = cell.findParent('td', false, true);
9236         }
9237         
9238         var row = cell.findParent('tr', false, true);
9239         var cellIndex = cell.dom.cellIndex;
9240         var rowIndex = row.dom.rowIndex - 1; // start from 0
9241         
9242         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9243         
9244     },
9245     
9246     onMouseout : function(e, el)
9247     {
9248         var cell = Roo.get(el);
9249         
9250         if(!cell){
9251             return;
9252         }
9253         
9254         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9255             cell = cell.findParent('td', false, true);
9256         }
9257         
9258         var row = cell.findParent('tr', false, true);
9259         var cellIndex = cell.dom.cellIndex;
9260         var rowIndex = row.dom.rowIndex - 1; // start from 0
9261         
9262         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9263         
9264     },
9265     
9266     onClick : function(e, el)
9267     {
9268         var cell = Roo.get(el);
9269         
9270         if(!cell || (!this.cellSelection && !this.rowSelection)){
9271             return;
9272         }
9273         
9274         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9275             cell = cell.findParent('td', false, true);
9276         }
9277         
9278         if(!cell || typeof(cell) == 'undefined'){
9279             return;
9280         }
9281         
9282         var row = cell.findParent('tr', false, true);
9283         
9284         if(!row || typeof(row) == 'undefined'){
9285             return;
9286         }
9287         
9288         var cellIndex = cell.dom.cellIndex;
9289         var rowIndex = this.getRowIndex(row);
9290         
9291         // why??? - should these not be based on SelectionModel?
9292         //if(this.cellSelection){
9293             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9294         //}
9295         
9296         //if(this.rowSelection){
9297             this.fireEvent('rowclick', this, row, rowIndex, e);
9298         //}
9299          
9300     },
9301         
9302     onDblClick : function(e,el)
9303     {
9304         var cell = Roo.get(el);
9305         
9306         if(!cell || (!this.cellSelection && !this.rowSelection)){
9307             return;
9308         }
9309         
9310         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9311             cell = cell.findParent('td', false, true);
9312         }
9313         
9314         if(!cell || typeof(cell) == 'undefined'){
9315             return;
9316         }
9317         
9318         var row = cell.findParent('tr', false, true);
9319         
9320         if(!row || typeof(row) == 'undefined'){
9321             return;
9322         }
9323         
9324         var cellIndex = cell.dom.cellIndex;
9325         var rowIndex = this.getRowIndex(row);
9326         
9327         if(this.cellSelection){
9328             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9329         }
9330         
9331         if(this.rowSelection){
9332             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9333         }
9334     },
9335     findRowIndex : function(el)
9336     {
9337         var cell = Roo.get(el);
9338         if(!cell) {
9339             return false;
9340         }
9341         var row = cell.findParent('tr', false, true);
9342         
9343         if(!row || typeof(row) == 'undefined'){
9344             return false;
9345         }
9346         return this.getRowIndex(row);
9347     },
9348     sort : function(e,el)
9349     {
9350         var col = Roo.get(el);
9351         
9352         if(!col.hasClass('sortable')){
9353             return;
9354         }
9355         
9356         var sort = col.attr('sort');
9357         var dir = 'ASC';
9358         
9359         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9360             dir = 'DESC';
9361         }
9362         
9363         this.store.sortInfo = {field : sort, direction : dir};
9364         
9365         if (this.footer) {
9366             Roo.log("calling footer first");
9367             this.footer.onClick('first');
9368         } else {
9369         
9370             this.store.load({ params : { start : 0 } });
9371         }
9372     },
9373     
9374     renderHeader : function()
9375     {
9376         var header = {
9377             tag: 'thead',
9378             cn : []
9379         };
9380         
9381         var cm = this.cm;
9382         this.totalWidth = 0;
9383         
9384         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9385             
9386             var config = cm.config[i];
9387             
9388             var c = {
9389                 tag: 'th',
9390                 cls : 'x-hcol-' + i,
9391                 style : '',
9392                 
9393                 html: cm.getColumnHeader(i)
9394             };
9395             
9396             var tooltip = cm.getColumnTooltip(i);
9397             if (tooltip) {
9398                 c.tooltip = tooltip;
9399             }
9400             
9401             
9402             var hh = '';
9403             
9404             if(typeof(config.sortable) != 'undefined' && config.sortable){
9405                 c.cls += ' sortable';
9406                 c.html = '<i class="fa"></i>' + c.html;
9407             }
9408             
9409             // could use BS4 hidden-..-down 
9410             
9411             if(typeof(config.lgHeader) != 'undefined'){
9412                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9413             }
9414             
9415             if(typeof(config.mdHeader) != 'undefined'){
9416                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9417             }
9418             
9419             if(typeof(config.smHeader) != 'undefined'){
9420                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9421             }
9422             
9423             if(typeof(config.xsHeader) != 'undefined'){
9424                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9425             }
9426             
9427             if(hh.length){
9428                 c.html = hh;
9429             }
9430             
9431             if(typeof(config.tooltip) != 'undefined'){
9432                 c.tooltip = config.tooltip;
9433             }
9434             
9435             if(typeof(config.colspan) != 'undefined'){
9436                 c.colspan = config.colspan;
9437             }
9438             
9439             // hidden is handled by CSS now
9440             
9441             if(typeof(config.dataIndex) != 'undefined'){
9442                 c.sort = config.dataIndex;
9443             }
9444             
9445            
9446             
9447             if(typeof(config.align) != 'undefined' && config.align.length){
9448                 c.style += ' text-align:' + config.align + ';';
9449             }
9450             
9451             /* width is done in CSS
9452              *if(typeof(config.width) != 'undefined'){
9453                 c.style += ' width:' + config.width + 'px;';
9454                 this.totalWidth += config.width;
9455             } else {
9456                 this.totalWidth += 100; // assume minimum of 100 per column?
9457             }
9458             */
9459             
9460             if(typeof(config.cls) != 'undefined'){
9461                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9462             }
9463             // this is the bit that doesnt reall work at all...
9464             
9465             if (this.responsive) {
9466                  
9467             
9468                 ['xs','sm','md','lg'].map(function(size){
9469                     
9470                     if(typeof(config[size]) == 'undefined'){
9471                         return;
9472                     }
9473                      
9474                     if (!config[size]) { // 0 = hidden
9475                         // BS 4 '0' is treated as hide that column and below.
9476                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9477                         return;
9478                     }
9479                     
9480                     c.cls += ' col-' + size + '-' + config[size] + (
9481                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9482                     );
9483                     
9484                     
9485                 });
9486             }
9487             // at the end?
9488             
9489             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9490             
9491             
9492             
9493             
9494             header.cn.push(c)
9495         }
9496         
9497         return header;
9498     },
9499     
9500     renderBody : function()
9501     {
9502         var body = {
9503             tag: 'tbody',
9504             cn : [
9505                 {
9506                     tag: 'tr',
9507                     cn : [
9508                         {
9509                             tag : 'td',
9510                             colspan :  this.cm.getColumnCount()
9511                         }
9512                     ]
9513                 }
9514             ]
9515         };
9516         
9517         return body;
9518     },
9519     
9520     renderFooter : function()
9521     {
9522         var footer = {
9523             tag: 'tfoot',
9524             cn : [
9525                 {
9526                     tag: 'tr',
9527                     cn : [
9528                         {
9529                             tag : 'td',
9530                             colspan :  this.cm.getColumnCount()
9531                         }
9532                     ]
9533                 }
9534             ]
9535         };
9536         
9537         return footer;
9538     },
9539     
9540     
9541     
9542     onLoad : function()
9543     {
9544 //        Roo.log('ds onload');
9545         this.clear();
9546         
9547         var _this = this;
9548         var cm = this.cm;
9549         var ds = this.store;
9550         
9551         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9552             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9553             if (_this.store.sortInfo) {
9554                     
9555                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9556                     e.select('i', true).addClass(['fa-arrow-up']);
9557                 }
9558                 
9559                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9560                     e.select('i', true).addClass(['fa-arrow-down']);
9561                 }
9562             }
9563         });
9564         
9565         var tbody =  this.bodyEl;
9566               
9567         if(ds.getCount() > 0){
9568             ds.data.each(function(d,rowIndex){
9569                 var row =  this.renderRow(cm, ds, rowIndex);
9570                 
9571                 tbody.createChild(row);
9572                 
9573                 var _this = this;
9574                 
9575                 if(row.cellObjects.length){
9576                     Roo.each(row.cellObjects, function(r){
9577                         _this.renderCellObject(r);
9578                     })
9579                 }
9580                 
9581             }, this);
9582         }
9583         
9584         var tfoot = this.el.select('tfoot', true).first();
9585         
9586         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9587             
9588             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9589             
9590             var total = this.ds.getTotalCount();
9591             
9592             if(this.footer.pageSize < total){
9593                 this.mainFoot.show();
9594             }
9595         }
9596         
9597         Roo.each(this.el.select('tbody td', true).elements, function(e){
9598             e.on('mouseover', _this.onMouseover, _this);
9599         });
9600         
9601         Roo.each(this.el.select('tbody td', true).elements, function(e){
9602             e.on('mouseout', _this.onMouseout, _this);
9603         });
9604         this.fireEvent('rowsrendered', this);
9605         
9606         this.autoSize();
9607         
9608         this.initCSS(); /// resize cols
9609
9610         
9611     },
9612     
9613     
9614     onUpdate : function(ds,record)
9615     {
9616         this.refreshRow(record);
9617         this.autoSize();
9618     },
9619     
9620     onRemove : function(ds, record, index, isUpdate){
9621         if(isUpdate !== true){
9622             this.fireEvent("beforerowremoved", this, index, record);
9623         }
9624         var bt = this.bodyEl.dom;
9625         
9626         var rows = this.el.select('tbody > tr', true).elements;
9627         
9628         if(typeof(rows[index]) != 'undefined'){
9629             bt.removeChild(rows[index].dom);
9630         }
9631         
9632 //        if(bt.rows[index]){
9633 //            bt.removeChild(bt.rows[index]);
9634 //        }
9635         
9636         if(isUpdate !== true){
9637             //this.stripeRows(index);
9638             //this.syncRowHeights(index, index);
9639             //this.layout();
9640             this.fireEvent("rowremoved", this, index, record);
9641         }
9642     },
9643     
9644     onAdd : function(ds, records, rowIndex)
9645     {
9646         //Roo.log('on Add called');
9647         // - note this does not handle multiple adding very well..
9648         var bt = this.bodyEl.dom;
9649         for (var i =0 ; i < records.length;i++) {
9650             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9651             //Roo.log(records[i]);
9652             //Roo.log(this.store.getAt(rowIndex+i));
9653             this.insertRow(this.store, rowIndex + i, false);
9654             return;
9655         }
9656         
9657     },
9658     
9659     
9660     refreshRow : function(record){
9661         var ds = this.store, index;
9662         if(typeof record == 'number'){
9663             index = record;
9664             record = ds.getAt(index);
9665         }else{
9666             index = ds.indexOf(record);
9667             if (index < 0) {
9668                 return; // should not happen - but seems to 
9669             }
9670         }
9671         this.insertRow(ds, index, true);
9672         this.autoSize();
9673         this.onRemove(ds, record, index+1, true);
9674         this.autoSize();
9675         //this.syncRowHeights(index, index);
9676         //this.layout();
9677         this.fireEvent("rowupdated", this, index, record);
9678     },
9679     // private - called by RowSelection
9680     onRowSelect : function(rowIndex){
9681         var row = this.getRowDom(rowIndex);
9682         row.addClass(['bg-info','info']);
9683     },
9684     // private - called by RowSelection
9685     onRowDeselect : function(rowIndex)
9686     {
9687         if (rowIndex < 0) {
9688             return;
9689         }
9690         var row = this.getRowDom(rowIndex);
9691         row.removeClass(['bg-info','info']);
9692     },
9693       /**
9694      * Focuses the specified row.
9695      * @param {Number} row The row index
9696      */
9697     focusRow : function(row)
9698     {
9699         //Roo.log('GridView.focusRow');
9700         var x = this.bodyEl.dom.scrollLeft;
9701         this.focusCell(row, 0, false);
9702         this.bodyEl.dom.scrollLeft = x;
9703
9704     },
9705      /**
9706      * Focuses the specified cell.
9707      * @param {Number} row The row index
9708      * @param {Number} col The column index
9709      * @param {Boolean} hscroll false to disable horizontal scrolling
9710      */
9711     focusCell : function(row, col, hscroll)
9712     {
9713         //Roo.log('GridView.focusCell');
9714         var el = this.ensureVisible(row, col, hscroll);
9715         // not sure what focusEL achives = it's a <a> pos relative 
9716         //this.focusEl.alignTo(el, "tl-tl");
9717         //if(Roo.isGecko){
9718         //    this.focusEl.focus();
9719         //}else{
9720         //    this.focusEl.focus.defer(1, this.focusEl);
9721         //}
9722     },
9723     
9724      /**
9725      * Scrolls the specified cell into view
9726      * @param {Number} row The row index
9727      * @param {Number} col The column index
9728      * @param {Boolean} hscroll false to disable horizontal scrolling
9729      */
9730     ensureVisible : function(row, col, hscroll)
9731     {
9732         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9733         //return null; //disable for testing.
9734         if(typeof row != "number"){
9735             row = row.rowIndex;
9736         }
9737         if(row < 0 && row >= this.ds.getCount()){
9738             return  null;
9739         }
9740         col = (col !== undefined ? col : 0);
9741         var cm = this.cm;
9742         while(cm.isHidden(col)){
9743             col++;
9744         }
9745
9746         var el = this.getCellDom(row, col);
9747         if(!el){
9748             return null;
9749         }
9750         var c = this.bodyEl.dom;
9751
9752         var ctop = parseInt(el.offsetTop, 10);
9753         var cleft = parseInt(el.offsetLeft, 10);
9754         var cbot = ctop + el.offsetHeight;
9755         var cright = cleft + el.offsetWidth;
9756
9757         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9758         var ch = 0; //?? header is not withing the area?
9759         var stop = parseInt(c.scrollTop, 10);
9760         var sleft = parseInt(c.scrollLeft, 10);
9761         var sbot = stop + ch;
9762         var sright = sleft + c.clientWidth;
9763         /*
9764         Roo.log('GridView.ensureVisible:' +
9765                 ' ctop:' + ctop +
9766                 ' c.clientHeight:' + c.clientHeight +
9767                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9768                 ' stop:' + stop +
9769                 ' cbot:' + cbot +
9770                 ' sbot:' + sbot +
9771                 ' ch:' + ch  
9772                 );
9773         */
9774         if(ctop < stop){
9775             c.scrollTop = ctop;
9776             //Roo.log("set scrolltop to ctop DISABLE?");
9777         }else if(cbot > sbot){
9778             //Roo.log("set scrolltop to cbot-ch");
9779             c.scrollTop = cbot-ch;
9780         }
9781
9782         if(hscroll !== false){
9783             if(cleft < sleft){
9784                 c.scrollLeft = cleft;
9785             }else if(cright > sright){
9786                 c.scrollLeft = cright-c.clientWidth;
9787             }
9788         }
9789
9790         return el;
9791     },
9792     
9793     
9794     insertRow : function(dm, rowIndex, isUpdate){
9795         
9796         if(!isUpdate){
9797             this.fireEvent("beforerowsinserted", this, rowIndex);
9798         }
9799             //var s = this.getScrollState();
9800         var row = this.renderRow(this.cm, this.store, rowIndex);
9801         // insert before rowIndex..
9802         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9803         
9804         var _this = this;
9805                 
9806         if(row.cellObjects.length){
9807             Roo.each(row.cellObjects, function(r){
9808                 _this.renderCellObject(r);
9809             })
9810         }
9811             
9812         if(!isUpdate){
9813             this.fireEvent("rowsinserted", this, rowIndex);
9814             //this.syncRowHeights(firstRow, lastRow);
9815             //this.stripeRows(firstRow);
9816             //this.layout();
9817         }
9818         
9819     },
9820     
9821     
9822     getRowDom : function(rowIndex)
9823     {
9824         var rows = this.el.select('tbody > tr', true).elements;
9825         
9826         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9827         
9828     },
9829     getCellDom : function(rowIndex, colIndex)
9830     {
9831         var row = this.getRowDom(rowIndex);
9832         if (row === false) {
9833             return false;
9834         }
9835         var cols = row.select('td', true).elements;
9836         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9837         
9838     },
9839     
9840     // returns the object tree for a tr..
9841   
9842     
9843     renderRow : function(cm, ds, rowIndex) 
9844     {
9845         var d = ds.getAt(rowIndex);
9846         
9847         var row = {
9848             tag : 'tr',
9849             cls : 'x-row-' + rowIndex,
9850             cn : []
9851         };
9852             
9853         var cellObjects = [];
9854         
9855         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9856             var config = cm.config[i];
9857             
9858             var renderer = cm.getRenderer(i);
9859             var value = '';
9860             var id = false;
9861             
9862             if(typeof(renderer) !== 'undefined'){
9863                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9864             }
9865             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9866             // and are rendered into the cells after the row is rendered - using the id for the element.
9867             
9868             if(typeof(value) === 'object'){
9869                 id = Roo.id();
9870                 cellObjects.push({
9871                     container : id,
9872                     cfg : value 
9873                 })
9874             }
9875             
9876             var rowcfg = {
9877                 record: d,
9878                 rowIndex : rowIndex,
9879                 colIndex : i,
9880                 rowClass : ''
9881             };
9882
9883             this.fireEvent('rowclass', this, rowcfg);
9884             
9885             var td = {
9886                 tag: 'td',
9887                 // this might end up displaying HTML?
9888                 // this is too messy... - better to only do it on columsn you know are going to be too long
9889                 //tooltip : (typeof(value) === 'object') ? '' : value,
9890                 cls : rowcfg.rowClass + ' x-col-' + i,
9891                 style: '',
9892                 html: (typeof(value) === 'object') ? '' : value
9893             };
9894             
9895             if (id) {
9896                 td.id = id;
9897             }
9898             
9899             if(typeof(config.colspan) != 'undefined'){
9900                 td.colspan = config.colspan;
9901             }
9902             
9903             
9904             
9905             if(typeof(config.align) != 'undefined' && config.align.length){
9906                 td.style += ' text-align:' + config.align + ';';
9907             }
9908             if(typeof(config.valign) != 'undefined' && config.valign.length){
9909                 td.style += ' vertical-align:' + config.valign + ';';
9910             }
9911             /*
9912             if(typeof(config.width) != 'undefined'){
9913                 td.style += ' width:' +  config.width + 'px;';
9914             }
9915             */
9916             
9917             if(typeof(config.cursor) != 'undefined'){
9918                 td.style += ' cursor:' +  config.cursor + ';';
9919             }
9920             
9921             if(typeof(config.cls) != 'undefined'){
9922                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9923             }
9924             if (this.responsive) {
9925                 ['xs','sm','md','lg'].map(function(size){
9926                     
9927                     if(typeof(config[size]) == 'undefined'){
9928                         return;
9929                     }
9930                     
9931                     
9932                       
9933                     if (!config[size]) { // 0 = hidden
9934                         // BS 4 '0' is treated as hide that column and below.
9935                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9936                         return;
9937                     }
9938                     
9939                     td.cls += ' col-' + size + '-' + config[size] + (
9940                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9941                     );
9942                      
9943     
9944                 });
9945             }
9946             row.cn.push(td);
9947            
9948         }
9949         
9950         row.cellObjects = cellObjects;
9951         
9952         return row;
9953           
9954     },
9955     
9956     
9957     
9958     onBeforeLoad : function()
9959     {
9960         
9961     },
9962      /**
9963      * Remove all rows
9964      */
9965     clear : function()
9966     {
9967         this.el.select('tbody', true).first().dom.innerHTML = '';
9968     },
9969     /**
9970      * Show or hide a row.
9971      * @param {Number} rowIndex to show or hide
9972      * @param {Boolean} state hide
9973      */
9974     setRowVisibility : function(rowIndex, state)
9975     {
9976         var bt = this.bodyEl.dom;
9977         
9978         var rows = this.el.select('tbody > tr', true).elements;
9979         
9980         if(typeof(rows[rowIndex]) == 'undefined'){
9981             return;
9982         }
9983         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9984         
9985     },
9986     
9987     
9988     getSelectionModel : function(){
9989         if(!this.selModel){
9990             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9991         }
9992         return this.selModel;
9993     },
9994     /*
9995      * Render the Roo.bootstrap object from renderder
9996      */
9997     renderCellObject : function(r)
9998     {
9999         var _this = this;
10000         
10001         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10002         
10003         var t = r.cfg.render(r.container);
10004         
10005         if(r.cfg.cn){
10006             Roo.each(r.cfg.cn, function(c){
10007                 var child = {
10008                     container: t.getChildContainer(),
10009                     cfg: c
10010                 };
10011                 _this.renderCellObject(child);
10012             })
10013         }
10014     },
10015     /**
10016      * get the Row Index from a dom element.
10017      * @param {Roo.Element} row The row to look for
10018      * @returns {Number} the row
10019      */
10020     getRowIndex : function(row)
10021     {
10022         var rowIndex = -1;
10023         
10024         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10025             if(el != row){
10026                 return;
10027             }
10028             
10029             rowIndex = index;
10030         });
10031         
10032         return rowIndex;
10033     },
10034     /**
10035      * get the header TH element for columnIndex
10036      * @param {Number} columnIndex
10037      * @returns {Roo.Element}
10038      */
10039     getHeaderIndex: function(colIndex)
10040     {
10041         var cols = this.headEl.select('th', true).elements;
10042         return cols[colIndex]; 
10043     },
10044     /**
10045      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10046      * @param {domElement} cell to look for
10047      * @returns {Number} the column
10048      */
10049     getCellIndex : function(cell)
10050     {
10051         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10052         if(id){
10053             return parseInt(id[1], 10);
10054         }
10055         return 0;
10056     },
10057      /**
10058      * Returns the grid's underlying element = used by panel.Grid
10059      * @return {Element} The element
10060      */
10061     getGridEl : function(){
10062         return this.el;
10063     },
10064      /**
10065      * Forces a resize - used by panel.Grid
10066      * @return {Element} The element
10067      */
10068     autoSize : function()
10069     {
10070         //var ctr = Roo.get(this.container.dom.parentElement);
10071         var ctr = Roo.get(this.el.dom);
10072         
10073         var thd = this.getGridEl().select('thead',true).first();
10074         var tbd = this.getGridEl().select('tbody', true).first();
10075         var tfd = this.getGridEl().select('tfoot', true).first();
10076         
10077         var cw = ctr.getWidth();
10078         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10079         
10080         if (tbd) {
10081             
10082             tbd.setWidth(ctr.getWidth());
10083             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10084             // this needs fixing for various usage - currently only hydra job advers I think..
10085             //tdb.setHeight(
10086             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10087             //); 
10088             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10089             cw -= barsize;
10090         }
10091         cw = Math.max(cw, this.totalWidth);
10092         this.getGridEl().select('tbody tr',true).setWidth(cw);
10093         this.initCSS();
10094         
10095         // resize 'expandable coloumn?
10096         
10097         return; // we doe not have a view in this design..
10098         
10099     },
10100     onBodyScroll: function()
10101     {
10102         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10103         if(this.headEl){
10104             this.headEl.setStyle({
10105                 'position' : 'relative',
10106                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10107             });
10108         }
10109         
10110         if(this.lazyLoad){
10111             
10112             var scrollHeight = this.bodyEl.dom.scrollHeight;
10113             
10114             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10115             
10116             var height = this.bodyEl.getHeight();
10117             
10118             if(scrollHeight - height == scrollTop) {
10119                 
10120                 var total = this.ds.getTotalCount();
10121                 
10122                 if(this.footer.cursor + this.footer.pageSize < total){
10123                     
10124                     this.footer.ds.load({
10125                         params : {
10126                             start : this.footer.cursor + this.footer.pageSize,
10127                             limit : this.footer.pageSize
10128                         },
10129                         add : true
10130                     });
10131                 }
10132             }
10133             
10134         }
10135     },
10136     onColumnSplitterMoved : function(i, diff)
10137     {
10138         this.userResized = true;
10139         
10140         var cm = this.colModel;
10141         
10142         var w = this.getHeaderIndex(i).getWidth() + diff;
10143         
10144         
10145         cm.setColumnWidth(i, w, true);
10146         this.initCSS();
10147         //var cid = cm.getColumnId(i); << not used in this version?
10148        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10149         
10150         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10151         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10152         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10153 */
10154         //this.updateSplitters();
10155         //this.layout(); << ??
10156         this.fireEvent("columnresize", i, w);
10157     },
10158     onHeaderChange : function()
10159     {
10160         var header = this.renderHeader();
10161         var table = this.el.select('table', true).first();
10162         
10163         this.headEl.remove();
10164         this.headEl = table.createChild(header, this.bodyEl, false);
10165         
10166         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10167             e.on('click', this.sort, this);
10168         }, this);
10169         
10170         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10171             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10172         }
10173         
10174     },
10175     
10176     onHiddenChange : function(colModel, colIndex, hidden)
10177     {
10178         /*
10179         this.cm.setHidden()
10180         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10181         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10182         
10183         this.CSS.updateRule(thSelector, "display", "");
10184         this.CSS.updateRule(tdSelector, "display", "");
10185         
10186         if(hidden){
10187             this.CSS.updateRule(thSelector, "display", "none");
10188             this.CSS.updateRule(tdSelector, "display", "none");
10189         }
10190         */
10191         // onload calls initCSS()
10192         this.onHeaderChange();
10193         this.onLoad();
10194     },
10195     
10196     setColumnWidth: function(col_index, width)
10197     {
10198         // width = "md-2 xs-2..."
10199         if(!this.colModel.config[col_index]) {
10200             return;
10201         }
10202         
10203         var w = width.split(" ");
10204         
10205         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10206         
10207         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10208         
10209         
10210         for(var j = 0; j < w.length; j++) {
10211             
10212             if(!w[j]) {
10213                 continue;
10214             }
10215             
10216             var size_cls = w[j].split("-");
10217             
10218             if(!Number.isInteger(size_cls[1] * 1)) {
10219                 continue;
10220             }
10221             
10222             if(!this.colModel.config[col_index][size_cls[0]]) {
10223                 continue;
10224             }
10225             
10226             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10227                 continue;
10228             }
10229             
10230             h_row[0].classList.replace(
10231                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10232                 "col-"+size_cls[0]+"-"+size_cls[1]
10233             );
10234             
10235             for(var i = 0; i < rows.length; i++) {
10236                 
10237                 var size_cls = w[j].split("-");
10238                 
10239                 if(!Number.isInteger(size_cls[1] * 1)) {
10240                     continue;
10241                 }
10242                 
10243                 if(!this.colModel.config[col_index][size_cls[0]]) {
10244                     continue;
10245                 }
10246                 
10247                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10248                     continue;
10249                 }
10250                 
10251                 rows[i].classList.replace(
10252                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10253                     "col-"+size_cls[0]+"-"+size_cls[1]
10254                 );
10255             }
10256             
10257             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10258         }
10259     }
10260 });
10261
10262 // currently only used to find the split on drag.. 
10263 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10264
10265 /**
10266  * @depricated
10267 */
10268 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10269 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10270 /*
10271  * - LGPL
10272  *
10273  * table cell
10274  * 
10275  */
10276
10277 /**
10278  * @class Roo.bootstrap.TableCell
10279  * @extends Roo.bootstrap.Component
10280  * Bootstrap TableCell class
10281  * @cfg {String} html cell contain text
10282  * @cfg {String} cls cell class
10283  * @cfg {String} tag cell tag (td|th) default td
10284  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10285  * @cfg {String} align Aligns the content in a cell
10286  * @cfg {String} axis Categorizes cells
10287  * @cfg {String} bgcolor Specifies the background color of a cell
10288  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10289  * @cfg {Number} colspan Specifies the number of columns a cell should span
10290  * @cfg {String} headers Specifies one or more header cells a cell is related to
10291  * @cfg {Number} height Sets the height of a cell
10292  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10293  * @cfg {Number} rowspan Sets the number of rows a cell should span
10294  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10295  * @cfg {String} valign Vertical aligns the content in a cell
10296  * @cfg {Number} width Specifies the width of a cell
10297  * 
10298  * @constructor
10299  * Create a new TableCell
10300  * @param {Object} config The config object
10301  */
10302
10303 Roo.bootstrap.TableCell = function(config){
10304     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10305 };
10306
10307 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10308     
10309     html: false,
10310     cls: false,
10311     tag: false,
10312     abbr: false,
10313     align: false,
10314     axis: false,
10315     bgcolor: false,
10316     charoff: false,
10317     colspan: false,
10318     headers: false,
10319     height: false,
10320     nowrap: false,
10321     rowspan: false,
10322     scope: false,
10323     valign: false,
10324     width: false,
10325     
10326     
10327     getAutoCreate : function(){
10328         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10329         
10330         cfg = {
10331             tag: 'td'
10332         };
10333         
10334         if(this.tag){
10335             cfg.tag = this.tag;
10336         }
10337         
10338         if (this.html) {
10339             cfg.html=this.html
10340         }
10341         if (this.cls) {
10342             cfg.cls=this.cls
10343         }
10344         if (this.abbr) {
10345             cfg.abbr=this.abbr
10346         }
10347         if (this.align) {
10348             cfg.align=this.align
10349         }
10350         if (this.axis) {
10351             cfg.axis=this.axis
10352         }
10353         if (this.bgcolor) {
10354             cfg.bgcolor=this.bgcolor
10355         }
10356         if (this.charoff) {
10357             cfg.charoff=this.charoff
10358         }
10359         if (this.colspan) {
10360             cfg.colspan=this.colspan
10361         }
10362         if (this.headers) {
10363             cfg.headers=this.headers
10364         }
10365         if (this.height) {
10366             cfg.height=this.height
10367         }
10368         if (this.nowrap) {
10369             cfg.nowrap=this.nowrap
10370         }
10371         if (this.rowspan) {
10372             cfg.rowspan=this.rowspan
10373         }
10374         if (this.scope) {
10375             cfg.scope=this.scope
10376         }
10377         if (this.valign) {
10378             cfg.valign=this.valign
10379         }
10380         if (this.width) {
10381             cfg.width=this.width
10382         }
10383         
10384         
10385         return cfg;
10386     }
10387    
10388 });
10389
10390  
10391
10392  /*
10393  * - LGPL
10394  *
10395  * table row
10396  * 
10397  */
10398
10399 /**
10400  * @class Roo.bootstrap.TableRow
10401  * @extends Roo.bootstrap.Component
10402  * Bootstrap TableRow class
10403  * @cfg {String} cls row class
10404  * @cfg {String} align Aligns the content in a table row
10405  * @cfg {String} bgcolor Specifies a background color for a table row
10406  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10407  * @cfg {String} valign Vertical aligns the content in a table row
10408  * 
10409  * @constructor
10410  * Create a new TableRow
10411  * @param {Object} config The config object
10412  */
10413
10414 Roo.bootstrap.TableRow = function(config){
10415     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10416 };
10417
10418 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10419     
10420     cls: false,
10421     align: false,
10422     bgcolor: false,
10423     charoff: false,
10424     valign: false,
10425     
10426     getAutoCreate : function(){
10427         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10428         
10429         cfg = {
10430             tag: 'tr'
10431         };
10432             
10433         if(this.cls){
10434             cfg.cls = this.cls;
10435         }
10436         if(this.align){
10437             cfg.align = this.align;
10438         }
10439         if(this.bgcolor){
10440             cfg.bgcolor = this.bgcolor;
10441         }
10442         if(this.charoff){
10443             cfg.charoff = this.charoff;
10444         }
10445         if(this.valign){
10446             cfg.valign = this.valign;
10447         }
10448         
10449         return cfg;
10450     }
10451    
10452 });
10453
10454  
10455
10456  /*
10457  * - LGPL
10458  *
10459  * table body
10460  * 
10461  */
10462
10463 /**
10464  * @class Roo.bootstrap.TableBody
10465  * @extends Roo.bootstrap.Component
10466  * Bootstrap TableBody class
10467  * @cfg {String} cls element class
10468  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10469  * @cfg {String} align Aligns the content inside the element
10470  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10471  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10472  * 
10473  * @constructor
10474  * Create a new TableBody
10475  * @param {Object} config The config object
10476  */
10477
10478 Roo.bootstrap.TableBody = function(config){
10479     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10480 };
10481
10482 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10483     
10484     cls: false,
10485     tag: false,
10486     align: false,
10487     charoff: false,
10488     valign: false,
10489     
10490     getAutoCreate : function(){
10491         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10492         
10493         cfg = {
10494             tag: 'tbody'
10495         };
10496             
10497         if (this.cls) {
10498             cfg.cls=this.cls
10499         }
10500         if(this.tag){
10501             cfg.tag = this.tag;
10502         }
10503         
10504         if(this.align){
10505             cfg.align = this.align;
10506         }
10507         if(this.charoff){
10508             cfg.charoff = this.charoff;
10509         }
10510         if(this.valign){
10511             cfg.valign = this.valign;
10512         }
10513         
10514         return cfg;
10515     }
10516     
10517     
10518 //    initEvents : function()
10519 //    {
10520 //        
10521 //        if(!this.store){
10522 //            return;
10523 //        }
10524 //        
10525 //        this.store = Roo.factory(this.store, Roo.data);
10526 //        this.store.on('load', this.onLoad, this);
10527 //        
10528 //        this.store.load();
10529 //        
10530 //    },
10531 //    
10532 //    onLoad: function () 
10533 //    {   
10534 //        this.fireEvent('load', this);
10535 //    }
10536 //    
10537 //   
10538 });
10539
10540  
10541
10542  /*
10543  * Based on:
10544  * Ext JS Library 1.1.1
10545  * Copyright(c) 2006-2007, Ext JS, LLC.
10546  *
10547  * Originally Released Under LGPL - original licence link has changed is not relivant.
10548  *
10549  * Fork - LGPL
10550  * <script type="text/javascript">
10551  */
10552
10553 // as we use this in bootstrap.
10554 Roo.namespace('Roo.form');
10555  /**
10556  * @class Roo.form.Action
10557  * Internal Class used to handle form actions
10558  * @constructor
10559  * @param {Roo.form.BasicForm} el The form element or its id
10560  * @param {Object} config Configuration options
10561  */
10562
10563  
10564  
10565 // define the action interface
10566 Roo.form.Action = function(form, options){
10567     this.form = form;
10568     this.options = options || {};
10569 };
10570 /**
10571  * Client Validation Failed
10572  * @const 
10573  */
10574 Roo.form.Action.CLIENT_INVALID = 'client';
10575 /**
10576  * Server Validation Failed
10577  * @const 
10578  */
10579 Roo.form.Action.SERVER_INVALID = 'server';
10580  /**
10581  * Connect to Server Failed
10582  * @const 
10583  */
10584 Roo.form.Action.CONNECT_FAILURE = 'connect';
10585 /**
10586  * Reading Data from Server Failed
10587  * @const 
10588  */
10589 Roo.form.Action.LOAD_FAILURE = 'load';
10590
10591 Roo.form.Action.prototype = {
10592     type : 'default',
10593     failureType : undefined,
10594     response : undefined,
10595     result : undefined,
10596
10597     // interface method
10598     run : function(options){
10599
10600     },
10601
10602     // interface method
10603     success : function(response){
10604
10605     },
10606
10607     // interface method
10608     handleResponse : function(response){
10609
10610     },
10611
10612     // default connection failure
10613     failure : function(response){
10614         
10615         this.response = response;
10616         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10617         this.form.afterAction(this, false);
10618     },
10619
10620     processResponse : function(response){
10621         this.response = response;
10622         if(!response.responseText){
10623             return true;
10624         }
10625         this.result = this.handleResponse(response);
10626         return this.result;
10627     },
10628
10629     // utility functions used internally
10630     getUrl : function(appendParams){
10631         var url = this.options.url || this.form.url || this.form.el.dom.action;
10632         if(appendParams){
10633             var p = this.getParams();
10634             if(p){
10635                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10636             }
10637         }
10638         return url;
10639     },
10640
10641     getMethod : function(){
10642         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10643     },
10644
10645     getParams : function(){
10646         var bp = this.form.baseParams;
10647         var p = this.options.params;
10648         if(p){
10649             if(typeof p == "object"){
10650                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10651             }else if(typeof p == 'string' && bp){
10652                 p += '&' + Roo.urlEncode(bp);
10653             }
10654         }else if(bp){
10655             p = Roo.urlEncode(bp);
10656         }
10657         return p;
10658     },
10659
10660     createCallback : function(){
10661         return {
10662             success: this.success,
10663             failure: this.failure,
10664             scope: this,
10665             timeout: (this.form.timeout*1000),
10666             upload: this.form.fileUpload ? this.success : undefined
10667         };
10668     }
10669 };
10670
10671 Roo.form.Action.Submit = function(form, options){
10672     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10673 };
10674
10675 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10676     type : 'submit',
10677
10678     haveProgress : false,
10679     uploadComplete : false,
10680     
10681     // uploadProgress indicator.
10682     uploadProgress : function()
10683     {
10684         if (!this.form.progressUrl) {
10685             return;
10686         }
10687         
10688         if (!this.haveProgress) {
10689             Roo.MessageBox.progress("Uploading", "Uploading");
10690         }
10691         if (this.uploadComplete) {
10692            Roo.MessageBox.hide();
10693            return;
10694         }
10695         
10696         this.haveProgress = true;
10697    
10698         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10699         
10700         var c = new Roo.data.Connection();
10701         c.request({
10702             url : this.form.progressUrl,
10703             params: {
10704                 id : uid
10705             },
10706             method: 'GET',
10707             success : function(req){
10708                //console.log(data);
10709                 var rdata = false;
10710                 var edata;
10711                 try  {
10712                    rdata = Roo.decode(req.responseText)
10713                 } catch (e) {
10714                     Roo.log("Invalid data from server..");
10715                     Roo.log(edata);
10716                     return;
10717                 }
10718                 if (!rdata || !rdata.success) {
10719                     Roo.log(rdata);
10720                     Roo.MessageBox.alert(Roo.encode(rdata));
10721                     return;
10722                 }
10723                 var data = rdata.data;
10724                 
10725                 if (this.uploadComplete) {
10726                    Roo.MessageBox.hide();
10727                    return;
10728                 }
10729                    
10730                 if (data){
10731                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10732                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10733                     );
10734                 }
10735                 this.uploadProgress.defer(2000,this);
10736             },
10737        
10738             failure: function(data) {
10739                 Roo.log('progress url failed ');
10740                 Roo.log(data);
10741             },
10742             scope : this
10743         });
10744            
10745     },
10746     
10747     
10748     run : function()
10749     {
10750         // run get Values on the form, so it syncs any secondary forms.
10751         this.form.getValues();
10752         
10753         var o = this.options;
10754         var method = this.getMethod();
10755         var isPost = method == 'POST';
10756         if(o.clientValidation === false || this.form.isValid()){
10757             
10758             if (this.form.progressUrl) {
10759                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10760                     (new Date() * 1) + '' + Math.random());
10761                     
10762             } 
10763             
10764             
10765             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10766                 form:this.form.el.dom,
10767                 url:this.getUrl(!isPost),
10768                 method: method,
10769                 params:isPost ? this.getParams() : null,
10770                 isUpload: this.form.fileUpload,
10771                 formData : this.form.formData
10772             }));
10773             
10774             this.uploadProgress();
10775
10776         }else if (o.clientValidation !== false){ // client validation failed
10777             this.failureType = Roo.form.Action.CLIENT_INVALID;
10778             this.form.afterAction(this, false);
10779         }
10780     },
10781
10782     success : function(response)
10783     {
10784         this.uploadComplete= true;
10785         if (this.haveProgress) {
10786             Roo.MessageBox.hide();
10787         }
10788         
10789         
10790         var result = this.processResponse(response);
10791         if(result === true || result.success){
10792             this.form.afterAction(this, true);
10793             return;
10794         }
10795         if(result.errors){
10796             this.form.markInvalid(result.errors);
10797             this.failureType = Roo.form.Action.SERVER_INVALID;
10798         }
10799         this.form.afterAction(this, false);
10800     },
10801     failure : function(response)
10802     {
10803         this.uploadComplete= true;
10804         if (this.haveProgress) {
10805             Roo.MessageBox.hide();
10806         }
10807         
10808         this.response = response;
10809         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10810         this.form.afterAction(this, false);
10811     },
10812     
10813     handleResponse : function(response){
10814         if(this.form.errorReader){
10815             var rs = this.form.errorReader.read(response);
10816             var errors = [];
10817             if(rs.records){
10818                 for(var i = 0, len = rs.records.length; i < len; i++) {
10819                     var r = rs.records[i];
10820                     errors[i] = r.data;
10821                 }
10822             }
10823             if(errors.length < 1){
10824                 errors = null;
10825             }
10826             return {
10827                 success : rs.success,
10828                 errors : errors
10829             };
10830         }
10831         var ret = false;
10832         try {
10833             ret = Roo.decode(response.responseText);
10834         } catch (e) {
10835             ret = {
10836                 success: false,
10837                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10838                 errors : []
10839             };
10840         }
10841         return ret;
10842         
10843     }
10844 });
10845
10846
10847 Roo.form.Action.Load = function(form, options){
10848     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10849     this.reader = this.form.reader;
10850 };
10851
10852 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10853     type : 'load',
10854
10855     run : function(){
10856         
10857         Roo.Ajax.request(Roo.apply(
10858                 this.createCallback(), {
10859                     method:this.getMethod(),
10860                     url:this.getUrl(false),
10861                     params:this.getParams()
10862         }));
10863     },
10864
10865     success : function(response){
10866         
10867         var result = this.processResponse(response);
10868         if(result === true || !result.success || !result.data){
10869             this.failureType = Roo.form.Action.LOAD_FAILURE;
10870             this.form.afterAction(this, false);
10871             return;
10872         }
10873         this.form.clearInvalid();
10874         this.form.setValues(result.data);
10875         this.form.afterAction(this, true);
10876     },
10877
10878     handleResponse : function(response){
10879         if(this.form.reader){
10880             var rs = this.form.reader.read(response);
10881             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10882             return {
10883                 success : rs.success,
10884                 data : data
10885             };
10886         }
10887         return Roo.decode(response.responseText);
10888     }
10889 });
10890
10891 Roo.form.Action.ACTION_TYPES = {
10892     'load' : Roo.form.Action.Load,
10893     'submit' : Roo.form.Action.Submit
10894 };/*
10895  * - LGPL
10896  *
10897  * form
10898  *
10899  */
10900
10901 /**
10902  * @class Roo.bootstrap.Form
10903  * @extends Roo.bootstrap.Component
10904  * Bootstrap Form class
10905  * @cfg {String} method  GET | POST (default POST)
10906  * @cfg {String} labelAlign top | left (default top)
10907  * @cfg {String} align left  | right - for navbars
10908  * @cfg {Boolean} loadMask load mask when submit (default true)
10909
10910  *
10911  * @constructor
10912  * Create a new Form
10913  * @param {Object} config The config object
10914  */
10915
10916
10917 Roo.bootstrap.Form = function(config){
10918     
10919     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10920     
10921     Roo.bootstrap.Form.popover.apply();
10922     
10923     this.addEvents({
10924         /**
10925          * @event clientvalidation
10926          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10927          * @param {Form} this
10928          * @param {Boolean} valid true if the form has passed client-side validation
10929          */
10930         clientvalidation: true,
10931         /**
10932          * @event beforeaction
10933          * Fires before any action is performed. Return false to cancel the action.
10934          * @param {Form} this
10935          * @param {Action} action The action to be performed
10936          */
10937         beforeaction: true,
10938         /**
10939          * @event actionfailed
10940          * Fires when an action fails.
10941          * @param {Form} this
10942          * @param {Action} action The action that failed
10943          */
10944         actionfailed : true,
10945         /**
10946          * @event actioncomplete
10947          * Fires when an action is completed.
10948          * @param {Form} this
10949          * @param {Action} action The action that completed
10950          */
10951         actioncomplete : true
10952     });
10953 };
10954
10955 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10956
10957      /**
10958      * @cfg {String} method
10959      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10960      */
10961     method : 'POST',
10962     /**
10963      * @cfg {String} url
10964      * The URL to use for form actions if one isn't supplied in the action options.
10965      */
10966     /**
10967      * @cfg {Boolean} fileUpload
10968      * Set to true if this form is a file upload.
10969      */
10970
10971     /**
10972      * @cfg {Object} baseParams
10973      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10974      */
10975
10976     /**
10977      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10978      */
10979     timeout: 30,
10980     /**
10981      * @cfg {Sting} align (left|right) for navbar forms
10982      */
10983     align : 'left',
10984
10985     // private
10986     activeAction : null,
10987
10988     /**
10989      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10990      * element by passing it or its id or mask the form itself by passing in true.
10991      * @type Mixed
10992      */
10993     waitMsgTarget : false,
10994
10995     loadMask : true,
10996     
10997     /**
10998      * @cfg {Boolean} errorMask (true|false) default false
10999      */
11000     errorMask : false,
11001     
11002     /**
11003      * @cfg {Number} maskOffset Default 100
11004      */
11005     maskOffset : 100,
11006     
11007     /**
11008      * @cfg {Boolean} maskBody
11009      */
11010     maskBody : false,
11011
11012     getAutoCreate : function(){
11013
11014         var cfg = {
11015             tag: 'form',
11016             method : this.method || 'POST',
11017             id : this.id || Roo.id(),
11018             cls : ''
11019         };
11020         if (this.parent().xtype.match(/^Nav/)) {
11021             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11022
11023         }
11024
11025         if (this.labelAlign == 'left' ) {
11026             cfg.cls += ' form-horizontal';
11027         }
11028
11029
11030         return cfg;
11031     },
11032     initEvents : function()
11033     {
11034         this.el.on('submit', this.onSubmit, this);
11035         // this was added as random key presses on the form where triggering form submit.
11036         this.el.on('keypress', function(e) {
11037             if (e.getCharCode() != 13) {
11038                 return true;
11039             }
11040             // we might need to allow it for textareas.. and some other items.
11041             // check e.getTarget().
11042
11043             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11044                 return true;
11045             }
11046
11047             Roo.log("keypress blocked");
11048
11049             e.preventDefault();
11050             return false;
11051         });
11052         
11053     },
11054     // private
11055     onSubmit : function(e){
11056         e.stopEvent();
11057     },
11058
11059      /**
11060      * Returns true if client-side validation on the form is successful.
11061      * @return Boolean
11062      */
11063     isValid : function(){
11064         var items = this.getItems();
11065         var valid = true;
11066         var target = false;
11067         
11068         items.each(function(f){
11069             
11070             if(f.validate()){
11071                 return;
11072             }
11073             
11074             Roo.log('invalid field: ' + f.name);
11075             
11076             valid = false;
11077
11078             if(!target && f.el.isVisible(true)){
11079                 target = f;
11080             }
11081            
11082         });
11083         
11084         if(this.errorMask && !valid){
11085             Roo.bootstrap.Form.popover.mask(this, target);
11086         }
11087         
11088         return valid;
11089     },
11090     
11091     /**
11092      * Returns true if any fields in this form have changed since their original load.
11093      * @return Boolean
11094      */
11095     isDirty : function(){
11096         var dirty = false;
11097         var items = this.getItems();
11098         items.each(function(f){
11099            if(f.isDirty()){
11100                dirty = true;
11101                return false;
11102            }
11103            return true;
11104         });
11105         return dirty;
11106     },
11107      /**
11108      * Performs a predefined action (submit or load) or custom actions you define on this form.
11109      * @param {String} actionName The name of the action type
11110      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11111      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11112      * accept other config options):
11113      * <pre>
11114 Property          Type             Description
11115 ----------------  ---------------  ----------------------------------------------------------------------------------
11116 url               String           The url for the action (defaults to the form's url)
11117 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11118 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11119 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11120                                    validate the form on the client (defaults to false)
11121      * </pre>
11122      * @return {BasicForm} this
11123      */
11124     doAction : function(action, options){
11125         if(typeof action == 'string'){
11126             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11127         }
11128         if(this.fireEvent('beforeaction', this, action) !== false){
11129             this.beforeAction(action);
11130             action.run.defer(100, action);
11131         }
11132         return this;
11133     },
11134
11135     // private
11136     beforeAction : function(action){
11137         var o = action.options;
11138         
11139         if(this.loadMask){
11140             
11141             if(this.maskBody){
11142                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11143             } else {
11144                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11145             }
11146         }
11147         // not really supported yet.. ??
11148
11149         //if(this.waitMsgTarget === true){
11150         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11151         //}else if(this.waitMsgTarget){
11152         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11153         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11154         //}else {
11155         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11156        // }
11157
11158     },
11159
11160     // private
11161     afterAction : function(action, success){
11162         this.activeAction = null;
11163         var o = action.options;
11164
11165         if(this.loadMask){
11166             
11167             if(this.maskBody){
11168                 Roo.get(document.body).unmask();
11169             } else {
11170                 this.el.unmask();
11171             }
11172         }
11173         
11174         //if(this.waitMsgTarget === true){
11175 //            this.el.unmask();
11176         //}else if(this.waitMsgTarget){
11177         //    this.waitMsgTarget.unmask();
11178         //}else{
11179         //    Roo.MessageBox.updateProgress(1);
11180         //    Roo.MessageBox.hide();
11181        // }
11182         //
11183         if(success){
11184             if(o.reset){
11185                 this.reset();
11186             }
11187             Roo.callback(o.success, o.scope, [this, action]);
11188             this.fireEvent('actioncomplete', this, action);
11189
11190         }else{
11191
11192             // failure condition..
11193             // we have a scenario where updates need confirming.
11194             // eg. if a locking scenario exists..
11195             // we look for { errors : { needs_confirm : true }} in the response.
11196             if (
11197                 (typeof(action.result) != 'undefined')  &&
11198                 (typeof(action.result.errors) != 'undefined')  &&
11199                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11200            ){
11201                 var _t = this;
11202                 Roo.log("not supported yet");
11203                  /*
11204
11205                 Roo.MessageBox.confirm(
11206                     "Change requires confirmation",
11207                     action.result.errorMsg,
11208                     function(r) {
11209                         if (r != 'yes') {
11210                             return;
11211                         }
11212                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11213                     }
11214
11215                 );
11216                 */
11217
11218
11219                 return;
11220             }
11221
11222             Roo.callback(o.failure, o.scope, [this, action]);
11223             // show an error message if no failed handler is set..
11224             if (!this.hasListener('actionfailed')) {
11225                 Roo.log("need to add dialog support");
11226                 /*
11227                 Roo.MessageBox.alert("Error",
11228                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11229                         action.result.errorMsg :
11230                         "Saving Failed, please check your entries or try again"
11231                 );
11232                 */
11233             }
11234
11235             this.fireEvent('actionfailed', this, action);
11236         }
11237
11238     },
11239     /**
11240      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11241      * @param {String} id The value to search for
11242      * @return Field
11243      */
11244     findField : function(id){
11245         var items = this.getItems();
11246         var field = items.get(id);
11247         if(!field){
11248              items.each(function(f){
11249                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11250                     field = f;
11251                     return false;
11252                 }
11253                 return true;
11254             });
11255         }
11256         return field || null;
11257     },
11258      /**
11259      * Mark fields in this form invalid in bulk.
11260      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11261      * @return {BasicForm} this
11262      */
11263     markInvalid : function(errors){
11264         if(errors instanceof Array){
11265             for(var i = 0, len = errors.length; i < len; i++){
11266                 var fieldError = errors[i];
11267                 var f = this.findField(fieldError.id);
11268                 if(f){
11269                     f.markInvalid(fieldError.msg);
11270                 }
11271             }
11272         }else{
11273             var field, id;
11274             for(id in errors){
11275                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11276                     field.markInvalid(errors[id]);
11277                 }
11278             }
11279         }
11280         //Roo.each(this.childForms || [], function (f) {
11281         //    f.markInvalid(errors);
11282         //});
11283
11284         return this;
11285     },
11286
11287     /**
11288      * Set values for fields in this form in bulk.
11289      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11290      * @return {BasicForm} this
11291      */
11292     setValues : function(values){
11293         if(values instanceof Array){ // array of objects
11294             for(var i = 0, len = values.length; i < len; i++){
11295                 var v = values[i];
11296                 var f = this.findField(v.id);
11297                 if(f){
11298                     f.setValue(v.value);
11299                     if(this.trackResetOnLoad){
11300                         f.originalValue = f.getValue();
11301                     }
11302                 }
11303             }
11304         }else{ // object hash
11305             var field, id;
11306             for(id in values){
11307                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11308
11309                     if (field.setFromData &&
11310                         field.valueField &&
11311                         field.displayField &&
11312                         // combos' with local stores can
11313                         // be queried via setValue()
11314                         // to set their value..
11315                         (field.store && !field.store.isLocal)
11316                         ) {
11317                         // it's a combo
11318                         var sd = { };
11319                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11320                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11321                         field.setFromData(sd);
11322
11323                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11324                         
11325                         field.setFromData(values);
11326                         
11327                     } else {
11328                         field.setValue(values[id]);
11329                     }
11330
11331
11332                     if(this.trackResetOnLoad){
11333                         field.originalValue = field.getValue();
11334                     }
11335                 }
11336             }
11337         }
11338
11339         //Roo.each(this.childForms || [], function (f) {
11340         //    f.setValues(values);
11341         //});
11342
11343         return this;
11344     },
11345
11346     /**
11347      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11348      * they are returned as an array.
11349      * @param {Boolean} asString
11350      * @return {Object}
11351      */
11352     getValues : function(asString){
11353         //if (this.childForms) {
11354             // copy values from the child forms
11355         //    Roo.each(this.childForms, function (f) {
11356         //        this.setValues(f.getValues());
11357         //    }, this);
11358         //}
11359
11360
11361
11362         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11363         if(asString === true){
11364             return fs;
11365         }
11366         return Roo.urlDecode(fs);
11367     },
11368
11369     /**
11370      * Returns the fields in this form as an object with key/value pairs.
11371      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11372      * @return {Object}
11373      */
11374     getFieldValues : function(with_hidden)
11375     {
11376         var items = this.getItems();
11377         var ret = {};
11378         items.each(function(f){
11379             
11380             if (!f.getName()) {
11381                 return;
11382             }
11383             
11384             var v = f.getValue();
11385             
11386             if (f.inputType =='radio') {
11387                 if (typeof(ret[f.getName()]) == 'undefined') {
11388                     ret[f.getName()] = ''; // empty..
11389                 }
11390
11391                 if (!f.el.dom.checked) {
11392                     return;
11393
11394                 }
11395                 v = f.el.dom.value;
11396
11397             }
11398             
11399             if(f.xtype == 'MoneyField'){
11400                 ret[f.currencyName] = f.getCurrency();
11401             }
11402
11403             // not sure if this supported any more..
11404             if ((typeof(v) == 'object') && f.getRawValue) {
11405                 v = f.getRawValue() ; // dates..
11406             }
11407             // combo boxes where name != hiddenName...
11408             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11409                 ret[f.name] = f.getRawValue();
11410             }
11411             ret[f.getName()] = v;
11412         });
11413
11414         return ret;
11415     },
11416
11417     /**
11418      * Clears all invalid messages in this form.
11419      * @return {BasicForm} this
11420      */
11421     clearInvalid : function(){
11422         var items = this.getItems();
11423
11424         items.each(function(f){
11425            f.clearInvalid();
11426         });
11427
11428         return this;
11429     },
11430
11431     /**
11432      * Resets this form.
11433      * @return {BasicForm} this
11434      */
11435     reset : function(){
11436         var items = this.getItems();
11437         items.each(function(f){
11438             f.reset();
11439         });
11440
11441         Roo.each(this.childForms || [], function (f) {
11442             f.reset();
11443         });
11444
11445
11446         return this;
11447     },
11448     
11449     getItems : function()
11450     {
11451         var r=new Roo.util.MixedCollection(false, function(o){
11452             return o.id || (o.id = Roo.id());
11453         });
11454         var iter = function(el) {
11455             if (el.inputEl) {
11456                 r.add(el);
11457             }
11458             if (!el.items) {
11459                 return;
11460             }
11461             Roo.each(el.items,function(e) {
11462                 iter(e);
11463             });
11464         };
11465
11466         iter(this);
11467         return r;
11468     },
11469     
11470     hideFields : function(items)
11471     {
11472         Roo.each(items, function(i){
11473             
11474             var f = this.findField(i);
11475             
11476             if(!f){
11477                 return;
11478             }
11479             
11480             f.hide();
11481             
11482         }, this);
11483     },
11484     
11485     showFields : function(items)
11486     {
11487         Roo.each(items, function(i){
11488             
11489             var f = this.findField(i);
11490             
11491             if(!f){
11492                 return;
11493             }
11494             
11495             f.show();
11496             
11497         }, this);
11498     }
11499
11500 });
11501
11502 Roo.apply(Roo.bootstrap.Form, {
11503     
11504     popover : {
11505         
11506         padding : 5,
11507         
11508         isApplied : false,
11509         
11510         isMasked : false,
11511         
11512         form : false,
11513         
11514         target : false,
11515         
11516         toolTip : false,
11517         
11518         intervalID : false,
11519         
11520         maskEl : false,
11521         
11522         apply : function()
11523         {
11524             if(this.isApplied){
11525                 return;
11526             }
11527             
11528             this.maskEl = {
11529                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11530                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11531                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11532                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11533             };
11534             
11535             this.maskEl.top.enableDisplayMode("block");
11536             this.maskEl.left.enableDisplayMode("block");
11537             this.maskEl.bottom.enableDisplayMode("block");
11538             this.maskEl.right.enableDisplayMode("block");
11539             
11540             this.toolTip = new Roo.bootstrap.Tooltip({
11541                 cls : 'roo-form-error-popover',
11542                 alignment : {
11543                     'left' : ['r-l', [-2,0], 'right'],
11544                     'right' : ['l-r', [2,0], 'left'],
11545                     'bottom' : ['tl-bl', [0,2], 'top'],
11546                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11547                 }
11548             });
11549             
11550             this.toolTip.render(Roo.get(document.body));
11551
11552             this.toolTip.el.enableDisplayMode("block");
11553             
11554             Roo.get(document.body).on('click', function(){
11555                 this.unmask();
11556             }, this);
11557             
11558             Roo.get(document.body).on('touchstart', function(){
11559                 this.unmask();
11560             }, this);
11561             
11562             this.isApplied = true
11563         },
11564         
11565         mask : function(form, target)
11566         {
11567             this.form = form;
11568             
11569             this.target = target;
11570             
11571             if(!this.form.errorMask || !target.el){
11572                 return;
11573             }
11574             
11575             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11576             
11577             Roo.log(scrollable);
11578             
11579             var ot = this.target.el.calcOffsetsTo(scrollable);
11580             
11581             var scrollTo = ot[1] - this.form.maskOffset;
11582             
11583             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11584             
11585             scrollable.scrollTo('top', scrollTo);
11586             
11587             var box = this.target.el.getBox();
11588             Roo.log(box);
11589             var zIndex = Roo.bootstrap.Modal.zIndex++;
11590
11591             
11592             this.maskEl.top.setStyle('position', 'absolute');
11593             this.maskEl.top.setStyle('z-index', zIndex);
11594             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11595             this.maskEl.top.setLeft(0);
11596             this.maskEl.top.setTop(0);
11597             this.maskEl.top.show();
11598             
11599             this.maskEl.left.setStyle('position', 'absolute');
11600             this.maskEl.left.setStyle('z-index', zIndex);
11601             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11602             this.maskEl.left.setLeft(0);
11603             this.maskEl.left.setTop(box.y - this.padding);
11604             this.maskEl.left.show();
11605
11606             this.maskEl.bottom.setStyle('position', 'absolute');
11607             this.maskEl.bottom.setStyle('z-index', zIndex);
11608             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11609             this.maskEl.bottom.setLeft(0);
11610             this.maskEl.bottom.setTop(box.bottom + this.padding);
11611             this.maskEl.bottom.show();
11612
11613             this.maskEl.right.setStyle('position', 'absolute');
11614             this.maskEl.right.setStyle('z-index', zIndex);
11615             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11616             this.maskEl.right.setLeft(box.right + this.padding);
11617             this.maskEl.right.setTop(box.y - this.padding);
11618             this.maskEl.right.show();
11619
11620             this.toolTip.bindEl = this.target.el;
11621
11622             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11623
11624             var tip = this.target.blankText;
11625
11626             if(this.target.getValue() !== '' ) {
11627                 
11628                 if (this.target.invalidText.length) {
11629                     tip = this.target.invalidText;
11630                 } else if (this.target.regexText.length){
11631                     tip = this.target.regexText;
11632                 }
11633             }
11634
11635             this.toolTip.show(tip);
11636
11637             this.intervalID = window.setInterval(function() {
11638                 Roo.bootstrap.Form.popover.unmask();
11639             }, 10000);
11640
11641             window.onwheel = function(){ return false;};
11642             
11643             (function(){ this.isMasked = true; }).defer(500, this);
11644             
11645         },
11646         
11647         unmask : function()
11648         {
11649             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11650                 return;
11651             }
11652             
11653             this.maskEl.top.setStyle('position', 'absolute');
11654             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11655             this.maskEl.top.hide();
11656
11657             this.maskEl.left.setStyle('position', 'absolute');
11658             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11659             this.maskEl.left.hide();
11660
11661             this.maskEl.bottom.setStyle('position', 'absolute');
11662             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11663             this.maskEl.bottom.hide();
11664
11665             this.maskEl.right.setStyle('position', 'absolute');
11666             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11667             this.maskEl.right.hide();
11668             
11669             this.toolTip.hide();
11670             
11671             this.toolTip.el.hide();
11672             
11673             window.onwheel = function(){ return true;};
11674             
11675             if(this.intervalID){
11676                 window.clearInterval(this.intervalID);
11677                 this.intervalID = false;
11678             }
11679             
11680             this.isMasked = false;
11681             
11682         }
11683         
11684     }
11685     
11686 });
11687
11688 /*
11689  * Based on:
11690  * Ext JS Library 1.1.1
11691  * Copyright(c) 2006-2007, Ext JS, LLC.
11692  *
11693  * Originally Released Under LGPL - original licence link has changed is not relivant.
11694  *
11695  * Fork - LGPL
11696  * <script type="text/javascript">
11697  */
11698 /**
11699  * @class Roo.form.VTypes
11700  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11701  * @singleton
11702  */
11703 Roo.form.VTypes = function(){
11704     // closure these in so they are only created once.
11705     var alpha = /^[a-zA-Z_]+$/;
11706     var alphanum = /^[a-zA-Z0-9_]+$/;
11707     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11708     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11709
11710     // All these messages and functions are configurable
11711     return {
11712         /**
11713          * The function used to validate email addresses
11714          * @param {String} value The email address
11715          */
11716         'email' : function(v){
11717             return email.test(v);
11718         },
11719         /**
11720          * The error text to display when the email validation function returns false
11721          * @type String
11722          */
11723         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11724         /**
11725          * The keystroke filter mask to be applied on email input
11726          * @type RegExp
11727          */
11728         'emailMask' : /[a-z0-9_\.\-@]/i,
11729
11730         /**
11731          * The function used to validate URLs
11732          * @param {String} value The URL
11733          */
11734         'url' : function(v){
11735             return url.test(v);
11736         },
11737         /**
11738          * The error text to display when the url validation function returns false
11739          * @type String
11740          */
11741         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11742         
11743         /**
11744          * The function used to validate alpha values
11745          * @param {String} value The value
11746          */
11747         'alpha' : function(v){
11748             return alpha.test(v);
11749         },
11750         /**
11751          * The error text to display when the alpha validation function returns false
11752          * @type String
11753          */
11754         'alphaText' : 'This field should only contain letters and _',
11755         /**
11756          * The keystroke filter mask to be applied on alpha input
11757          * @type RegExp
11758          */
11759         'alphaMask' : /[a-z_]/i,
11760
11761         /**
11762          * The function used to validate alphanumeric values
11763          * @param {String} value The value
11764          */
11765         'alphanum' : function(v){
11766             return alphanum.test(v);
11767         },
11768         /**
11769          * The error text to display when the alphanumeric validation function returns false
11770          * @type String
11771          */
11772         'alphanumText' : 'This field should only contain letters, numbers and _',
11773         /**
11774          * The keystroke filter mask to be applied on alphanumeric input
11775          * @type RegExp
11776          */
11777         'alphanumMask' : /[a-z0-9_]/i
11778     };
11779 }();/*
11780  * - LGPL
11781  *
11782  * Input
11783  * 
11784  */
11785
11786 /**
11787  * @class Roo.bootstrap.Input
11788  * @extends Roo.bootstrap.Component
11789  * Bootstrap Input class
11790  * @cfg {Boolean} disabled is it disabled
11791  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11792  * @cfg {String} name name of the input
11793  * @cfg {string} fieldLabel - the label associated
11794  * @cfg {string} placeholder - placeholder to put in text.
11795  * @cfg {string}  before - input group add on before
11796  * @cfg {string} after - input group add on after
11797  * @cfg {string} size - (lg|sm) or leave empty..
11798  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11799  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11800  * @cfg {Number} md colspan out of 12 for computer-sized screens
11801  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11802  * @cfg {string} value default value of the input
11803  * @cfg {Number} labelWidth set the width of label 
11804  * @cfg {Number} labellg set the width of label (1-12)
11805  * @cfg {Number} labelmd set the width of label (1-12)
11806  * @cfg {Number} labelsm set the width of label (1-12)
11807  * @cfg {Number} labelxs set the width of label (1-12)
11808  * @cfg {String} labelAlign (top|left)
11809  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11810  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11811  * @cfg {String} indicatorpos (left|right) default left
11812  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11813  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11814  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11815
11816  * @cfg {String} align (left|center|right) Default left
11817  * @cfg {Boolean} forceFeedback (true|false) Default false
11818  * 
11819  * @constructor
11820  * Create a new Input
11821  * @param {Object} config The config object
11822  */
11823
11824 Roo.bootstrap.Input = function(config){
11825     
11826     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11827     
11828     this.addEvents({
11829         /**
11830          * @event focus
11831          * Fires when this field receives input focus.
11832          * @param {Roo.form.Field} this
11833          */
11834         focus : true,
11835         /**
11836          * @event blur
11837          * Fires when this field loses input focus.
11838          * @param {Roo.form.Field} this
11839          */
11840         blur : true,
11841         /**
11842          * @event specialkey
11843          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11844          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11845          * @param {Roo.form.Field} this
11846          * @param {Roo.EventObject} e The event object
11847          */
11848         specialkey : true,
11849         /**
11850          * @event change
11851          * Fires just before the field blurs if the field value has changed.
11852          * @param {Roo.form.Field} this
11853          * @param {Mixed} newValue The new value
11854          * @param {Mixed} oldValue The original value
11855          */
11856         change : true,
11857         /**
11858          * @event invalid
11859          * Fires after the field has been marked as invalid.
11860          * @param {Roo.form.Field} this
11861          * @param {String} msg The validation message
11862          */
11863         invalid : true,
11864         /**
11865          * @event valid
11866          * Fires after the field has been validated with no errors.
11867          * @param {Roo.form.Field} this
11868          */
11869         valid : true,
11870          /**
11871          * @event keyup
11872          * Fires after the key up
11873          * @param {Roo.form.Field} this
11874          * @param {Roo.EventObject}  e The event Object
11875          */
11876         keyup : true,
11877         /**
11878          * @event paste
11879          * Fires after the user pastes into input
11880          * @param {Roo.form.Field} this
11881          * @param {Roo.EventObject}  e The event Object
11882          */
11883         paste : true
11884     });
11885 };
11886
11887 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11888      /**
11889      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11890       automatic validation (defaults to "keyup").
11891      */
11892     validationEvent : "keyup",
11893      /**
11894      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11895      */
11896     validateOnBlur : true,
11897     /**
11898      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11899      */
11900     validationDelay : 250,
11901      /**
11902      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11903      */
11904     focusClass : "x-form-focus",  // not needed???
11905     
11906        
11907     /**
11908      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11909      */
11910     invalidClass : "has-warning",
11911     
11912     /**
11913      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11914      */
11915     validClass : "has-success",
11916     
11917     /**
11918      * @cfg {Boolean} hasFeedback (true|false) default true
11919      */
11920     hasFeedback : true,
11921     
11922     /**
11923      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11924      */
11925     invalidFeedbackClass : "glyphicon-warning-sign",
11926     
11927     /**
11928      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11929      */
11930     validFeedbackClass : "glyphicon-ok",
11931     
11932     /**
11933      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11934      */
11935     selectOnFocus : false,
11936     
11937      /**
11938      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11939      */
11940     maskRe : null,
11941        /**
11942      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11943      */
11944     vtype : null,
11945     
11946       /**
11947      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11948      */
11949     disableKeyFilter : false,
11950     
11951        /**
11952      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11953      */
11954     disabled : false,
11955      /**
11956      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11957      */
11958     allowBlank : true,
11959     /**
11960      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11961      */
11962     blankText : "Please complete this mandatory field",
11963     
11964      /**
11965      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11966      */
11967     minLength : 0,
11968     /**
11969      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11970      */
11971     maxLength : Number.MAX_VALUE,
11972     /**
11973      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11974      */
11975     minLengthText : "The minimum length for this field is {0}",
11976     /**
11977      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11978      */
11979     maxLengthText : "The maximum length for this field is {0}",
11980   
11981     
11982     /**
11983      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11984      * If available, this function will be called only after the basic validators all return true, and will be passed the
11985      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11986      */
11987     validator : null,
11988     /**
11989      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11990      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11991      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11992      */
11993     regex : null,
11994     /**
11995      * @cfg {String} regexText -- Depricated - use Invalid Text
11996      */
11997     regexText : "",
11998     
11999     /**
12000      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12001      */
12002     invalidText : "",
12003     
12004     
12005     
12006     autocomplete: false,
12007     
12008     
12009     fieldLabel : '',
12010     inputType : 'text',
12011     
12012     name : false,
12013     placeholder: false,
12014     before : false,
12015     after : false,
12016     size : false,
12017     hasFocus : false,
12018     preventMark: false,
12019     isFormField : true,
12020     value : '',
12021     labelWidth : 2,
12022     labelAlign : false,
12023     readOnly : false,
12024     align : false,
12025     formatedValue : false,
12026     forceFeedback : false,
12027     
12028     indicatorpos : 'left',
12029     
12030     labellg : 0,
12031     labelmd : 0,
12032     labelsm : 0,
12033     labelxs : 0,
12034     
12035     capture : '',
12036     accept : '',
12037     
12038     parentLabelAlign : function()
12039     {
12040         var parent = this;
12041         while (parent.parent()) {
12042             parent = parent.parent();
12043             if (typeof(parent.labelAlign) !='undefined') {
12044                 return parent.labelAlign;
12045             }
12046         }
12047         return 'left';
12048         
12049     },
12050     
12051     getAutoCreate : function()
12052     {
12053         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12054         
12055         var id = Roo.id();
12056         
12057         var cfg = {};
12058         
12059         if(this.inputType != 'hidden'){
12060             cfg.cls = 'form-group' //input-group
12061         }
12062         
12063         var input =  {
12064             tag: 'input',
12065             id : id,
12066             type : this.inputType,
12067             value : this.value,
12068             cls : 'form-control',
12069             placeholder : this.placeholder || '',
12070             autocomplete : this.autocomplete || 'new-password'
12071         };
12072         if (this.inputType == 'file') {
12073             input.style = 'overflow:hidden'; // why not in CSS?
12074         }
12075         
12076         if(this.capture.length){
12077             input.capture = this.capture;
12078         }
12079         
12080         if(this.accept.length){
12081             input.accept = this.accept + "/*";
12082         }
12083         
12084         if(this.align){
12085             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12086         }
12087         
12088         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12089             input.maxLength = this.maxLength;
12090         }
12091         
12092         if (this.disabled) {
12093             input.disabled=true;
12094         }
12095         
12096         if (this.readOnly) {
12097             input.readonly=true;
12098         }
12099         
12100         if (this.name) {
12101             input.name = this.name;
12102         }
12103         
12104         if (this.size) {
12105             input.cls += ' input-' + this.size;
12106         }
12107         
12108         var settings=this;
12109         ['xs','sm','md','lg'].map(function(size){
12110             if (settings[size]) {
12111                 cfg.cls += ' col-' + size + '-' + settings[size];
12112             }
12113         });
12114         
12115         var inputblock = input;
12116         
12117         var feedback = {
12118             tag: 'span',
12119             cls: 'glyphicon form-control-feedback'
12120         };
12121             
12122         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12123             
12124             inputblock = {
12125                 cls : 'has-feedback',
12126                 cn :  [
12127                     input,
12128                     feedback
12129                 ] 
12130             };  
12131         }
12132         
12133         if (this.before || this.after) {
12134             
12135             inputblock = {
12136                 cls : 'input-group',
12137                 cn :  [] 
12138             };
12139             
12140             if (this.before && typeof(this.before) == 'string') {
12141                 
12142                 inputblock.cn.push({
12143                     tag :'span',
12144                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12145                     html : this.before
12146                 });
12147             }
12148             if (this.before && typeof(this.before) == 'object') {
12149                 this.before = Roo.factory(this.before);
12150                 
12151                 inputblock.cn.push({
12152                     tag :'span',
12153                     cls : 'roo-input-before input-group-prepend   input-group-' +
12154                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12155                 });
12156             }
12157             
12158             inputblock.cn.push(input);
12159             
12160             if (this.after && typeof(this.after) == 'string') {
12161                 inputblock.cn.push({
12162                     tag :'span',
12163                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12164                     html : this.after
12165                 });
12166             }
12167             if (this.after && typeof(this.after) == 'object') {
12168                 this.after = Roo.factory(this.after);
12169                 
12170                 inputblock.cn.push({
12171                     tag :'span',
12172                     cls : 'roo-input-after input-group-append  input-group-' +
12173                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12174                 });
12175             }
12176             
12177             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12178                 inputblock.cls += ' has-feedback';
12179                 inputblock.cn.push(feedback);
12180             }
12181         };
12182         var indicator = {
12183             tag : 'i',
12184             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12185             tooltip : 'This field is required'
12186         };
12187         if (this.allowBlank ) {
12188             indicator.style = this.allowBlank ? ' display:none' : '';
12189         }
12190         if (align ==='left' && this.fieldLabel.length) {
12191             
12192             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12193             
12194             cfg.cn = [
12195                 indicator,
12196                 {
12197                     tag: 'label',
12198                     'for' :  id,
12199                     cls : 'control-label col-form-label',
12200                     html : this.fieldLabel
12201
12202                 },
12203                 {
12204                     cls : "", 
12205                     cn: [
12206                         inputblock
12207                     ]
12208                 }
12209             ];
12210             
12211             var labelCfg = cfg.cn[1];
12212             var contentCfg = cfg.cn[2];
12213             
12214             if(this.indicatorpos == 'right'){
12215                 cfg.cn = [
12216                     {
12217                         tag: 'label',
12218                         'for' :  id,
12219                         cls : 'control-label col-form-label',
12220                         cn : [
12221                             {
12222                                 tag : 'span',
12223                                 html : this.fieldLabel
12224                             },
12225                             indicator
12226                         ]
12227                     },
12228                     {
12229                         cls : "",
12230                         cn: [
12231                             inputblock
12232                         ]
12233                     }
12234
12235                 ];
12236                 
12237                 labelCfg = cfg.cn[0];
12238                 contentCfg = cfg.cn[1];
12239             
12240             }
12241             
12242             if(this.labelWidth > 12){
12243                 labelCfg.style = "width: " + this.labelWidth + 'px';
12244             }
12245             
12246             if(this.labelWidth < 13 && this.labelmd == 0){
12247                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12248             }
12249             
12250             if(this.labellg > 0){
12251                 labelCfg.cls += ' col-lg-' + this.labellg;
12252                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12253             }
12254             
12255             if(this.labelmd > 0){
12256                 labelCfg.cls += ' col-md-' + this.labelmd;
12257                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12258             }
12259             
12260             if(this.labelsm > 0){
12261                 labelCfg.cls += ' col-sm-' + this.labelsm;
12262                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12263             }
12264             
12265             if(this.labelxs > 0){
12266                 labelCfg.cls += ' col-xs-' + this.labelxs;
12267                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12268             }
12269             
12270             
12271         } else if ( this.fieldLabel.length) {
12272                 
12273             
12274             
12275             cfg.cn = [
12276                 {
12277                     tag : 'i',
12278                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12279                     tooltip : 'This field is required',
12280                     style : this.allowBlank ? ' display:none' : '' 
12281                 },
12282                 {
12283                     tag: 'label',
12284                    //cls : 'input-group-addon',
12285                     html : this.fieldLabel
12286
12287                 },
12288
12289                inputblock
12290
12291            ];
12292            
12293            if(this.indicatorpos == 'right'){
12294        
12295                 cfg.cn = [
12296                     {
12297                         tag: 'label',
12298                        //cls : 'input-group-addon',
12299                         html : this.fieldLabel
12300
12301                     },
12302                     {
12303                         tag : 'i',
12304                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12305                         tooltip : 'This field is required',
12306                         style : this.allowBlank ? ' display:none' : '' 
12307                     },
12308
12309                    inputblock
12310
12311                ];
12312
12313             }
12314
12315         } else {
12316             
12317             cfg.cn = [
12318
12319                     inputblock
12320
12321             ];
12322                 
12323                 
12324         };
12325         
12326         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12327            cfg.cls += ' navbar-form';
12328         }
12329         
12330         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12331             // on BS4 we do this only if not form 
12332             cfg.cls += ' navbar-form';
12333             cfg.tag = 'li';
12334         }
12335         
12336         return cfg;
12337         
12338     },
12339     /**
12340      * return the real input element.
12341      */
12342     inputEl: function ()
12343     {
12344         return this.el.select('input.form-control',true).first();
12345     },
12346     
12347     tooltipEl : function()
12348     {
12349         return this.inputEl();
12350     },
12351     
12352     indicatorEl : function()
12353     {
12354         if (Roo.bootstrap.version == 4) {
12355             return false; // not enabled in v4 yet.
12356         }
12357         
12358         var indicator = this.el.select('i.roo-required-indicator',true).first();
12359         
12360         if(!indicator){
12361             return false;
12362         }
12363         
12364         return indicator;
12365         
12366     },
12367     
12368     setDisabled : function(v)
12369     {
12370         var i  = this.inputEl().dom;
12371         if (!v) {
12372             i.removeAttribute('disabled');
12373             return;
12374             
12375         }
12376         i.setAttribute('disabled','true');
12377     },
12378     initEvents : function()
12379     {
12380           
12381         this.inputEl().on("keydown" , this.fireKey,  this);
12382         this.inputEl().on("focus", this.onFocus,  this);
12383         this.inputEl().on("blur", this.onBlur,  this);
12384         
12385         this.inputEl().relayEvent('keyup', this);
12386         this.inputEl().relayEvent('paste', this);
12387         
12388         this.indicator = this.indicatorEl();
12389         
12390         if(this.indicator){
12391             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12392         }
12393  
12394         // reference to original value for reset
12395         this.originalValue = this.getValue();
12396         //Roo.form.TextField.superclass.initEvents.call(this);
12397         if(this.validationEvent == 'keyup'){
12398             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12399             this.inputEl().on('keyup', this.filterValidation, this);
12400         }
12401         else if(this.validationEvent !== false){
12402             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12403         }
12404         
12405         if(this.selectOnFocus){
12406             this.on("focus", this.preFocus, this);
12407             
12408         }
12409         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12410             this.inputEl().on("keypress", this.filterKeys, this);
12411         } else {
12412             this.inputEl().relayEvent('keypress', this);
12413         }
12414        /* if(this.grow){
12415             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12416             this.el.on("click", this.autoSize,  this);
12417         }
12418         */
12419         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12420             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12421         }
12422         
12423         if (typeof(this.before) == 'object') {
12424             this.before.render(this.el.select('.roo-input-before',true).first());
12425         }
12426         if (typeof(this.after) == 'object') {
12427             this.after.render(this.el.select('.roo-input-after',true).first());
12428         }
12429         
12430         this.inputEl().on('change', this.onChange, this);
12431         
12432     },
12433     filterValidation : function(e){
12434         if(!e.isNavKeyPress()){
12435             this.validationTask.delay(this.validationDelay);
12436         }
12437     },
12438      /**
12439      * Validates the field value
12440      * @return {Boolean} True if the value is valid, else false
12441      */
12442     validate : function(){
12443         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12444         if(this.disabled || this.validateValue(this.getRawValue())){
12445             this.markValid();
12446             return true;
12447         }
12448         
12449         this.markInvalid();
12450         return false;
12451     },
12452     
12453     
12454     /**
12455      * Validates a value according to the field's validation rules and marks the field as invalid
12456      * if the validation fails
12457      * @param {Mixed} value The value to validate
12458      * @return {Boolean} True if the value is valid, else false
12459      */
12460     validateValue : function(value)
12461     {
12462         if(this.getVisibilityEl().hasClass('hidden')){
12463             return true;
12464         }
12465         
12466         if(value.length < 1)  { // if it's blank
12467             if(this.allowBlank){
12468                 return true;
12469             }
12470             return false;
12471         }
12472         
12473         if(value.length < this.minLength){
12474             return false;
12475         }
12476         if(value.length > this.maxLength){
12477             return false;
12478         }
12479         if(this.vtype){
12480             var vt = Roo.form.VTypes;
12481             if(!vt[this.vtype](value, this)){
12482                 return false;
12483             }
12484         }
12485         if(typeof this.validator == "function"){
12486             var msg = this.validator(value);
12487             if(msg !== true){
12488                 return false;
12489             }
12490             if (typeof(msg) == 'string') {
12491                 this.invalidText = msg;
12492             }
12493         }
12494         
12495         if(this.regex && !this.regex.test(value)){
12496             return false;
12497         }
12498         
12499         return true;
12500     },
12501     
12502      // private
12503     fireKey : function(e){
12504         //Roo.log('field ' + e.getKey());
12505         if(e.isNavKeyPress()){
12506             this.fireEvent("specialkey", this, e);
12507         }
12508     },
12509     focus : function (selectText){
12510         if(this.rendered){
12511             this.inputEl().focus();
12512             if(selectText === true){
12513                 this.inputEl().dom.select();
12514             }
12515         }
12516         return this;
12517     } ,
12518     
12519     onFocus : function(){
12520         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12521            // this.el.addClass(this.focusClass);
12522         }
12523         if(!this.hasFocus){
12524             this.hasFocus = true;
12525             this.startValue = this.getValue();
12526             this.fireEvent("focus", this);
12527         }
12528     },
12529     
12530     beforeBlur : Roo.emptyFn,
12531
12532     
12533     // private
12534     onBlur : function(){
12535         this.beforeBlur();
12536         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12537             //this.el.removeClass(this.focusClass);
12538         }
12539         this.hasFocus = false;
12540         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12541             this.validate();
12542         }
12543         var v = this.getValue();
12544         if(String(v) !== String(this.startValue)){
12545             this.fireEvent('change', this, v, this.startValue);
12546         }
12547         this.fireEvent("blur", this);
12548     },
12549     
12550     onChange : function(e)
12551     {
12552         var v = this.getValue();
12553         if(String(v) !== String(this.startValue)){
12554             this.fireEvent('change', this, v, this.startValue);
12555         }
12556         
12557     },
12558     
12559     /**
12560      * Resets the current field value to the originally loaded value and clears any validation messages
12561      */
12562     reset : function(){
12563         this.setValue(this.originalValue);
12564         this.validate();
12565     },
12566      /**
12567      * Returns the name of the field
12568      * @return {Mixed} name The name field
12569      */
12570     getName: function(){
12571         return this.name;
12572     },
12573      /**
12574      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12575      * @return {Mixed} value The field value
12576      */
12577     getValue : function(){
12578         
12579         var v = this.inputEl().getValue();
12580         
12581         return v;
12582     },
12583     /**
12584      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12585      * @return {Mixed} value The field value
12586      */
12587     getRawValue : function(){
12588         var v = this.inputEl().getValue();
12589         
12590         return v;
12591     },
12592     
12593     /**
12594      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12595      * @param {Mixed} value The value to set
12596      */
12597     setRawValue : function(v){
12598         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12599     },
12600     
12601     selectText : function(start, end){
12602         var v = this.getRawValue();
12603         if(v.length > 0){
12604             start = start === undefined ? 0 : start;
12605             end = end === undefined ? v.length : end;
12606             var d = this.inputEl().dom;
12607             if(d.setSelectionRange){
12608                 d.setSelectionRange(start, end);
12609             }else if(d.createTextRange){
12610                 var range = d.createTextRange();
12611                 range.moveStart("character", start);
12612                 range.moveEnd("character", v.length-end);
12613                 range.select();
12614             }
12615         }
12616     },
12617     
12618     /**
12619      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12620      * @param {Mixed} value The value to set
12621      */
12622     setValue : function(v){
12623         this.value = v;
12624         if(this.rendered){
12625             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12626             this.validate();
12627         }
12628     },
12629     
12630     /*
12631     processValue : function(value){
12632         if(this.stripCharsRe){
12633             var newValue = value.replace(this.stripCharsRe, '');
12634             if(newValue !== value){
12635                 this.setRawValue(newValue);
12636                 return newValue;
12637             }
12638         }
12639         return value;
12640     },
12641   */
12642     preFocus : function(){
12643         
12644         if(this.selectOnFocus){
12645             this.inputEl().dom.select();
12646         }
12647     },
12648     filterKeys : function(e){
12649         var k = e.getKey();
12650         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12651             return;
12652         }
12653         var c = e.getCharCode(), cc = String.fromCharCode(c);
12654         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12655             return;
12656         }
12657         if(!this.maskRe.test(cc)){
12658             e.stopEvent();
12659         }
12660     },
12661      /**
12662      * Clear any invalid styles/messages for this field
12663      */
12664     clearInvalid : function(){
12665         
12666         if(!this.el || this.preventMark){ // not rendered
12667             return;
12668         }
12669         
12670         
12671         this.el.removeClass([this.invalidClass, 'is-invalid']);
12672         
12673         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12674             
12675             var feedback = this.el.select('.form-control-feedback', true).first();
12676             
12677             if(feedback){
12678                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12679             }
12680             
12681         }
12682         
12683         if(this.indicator){
12684             this.indicator.removeClass('visible');
12685             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12686         }
12687         
12688         this.fireEvent('valid', this);
12689     },
12690     
12691      /**
12692      * Mark this field as valid
12693      */
12694     markValid : function()
12695     {
12696         if(!this.el  || this.preventMark){ // not rendered...
12697             return;
12698         }
12699         
12700         this.el.removeClass([this.invalidClass, this.validClass]);
12701         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12702
12703         var feedback = this.el.select('.form-control-feedback', true).first();
12704             
12705         if(feedback){
12706             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12707         }
12708         
12709         if(this.indicator){
12710             this.indicator.removeClass('visible');
12711             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12712         }
12713         
12714         if(this.disabled){
12715             return;
12716         }
12717         
12718            
12719         if(this.allowBlank && !this.getRawValue().length){
12720             return;
12721         }
12722         if (Roo.bootstrap.version == 3) {
12723             this.el.addClass(this.validClass);
12724         } else {
12725             this.inputEl().addClass('is-valid');
12726         }
12727
12728         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12729             
12730             var feedback = this.el.select('.form-control-feedback', true).first();
12731             
12732             if(feedback){
12733                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12734                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12735             }
12736             
12737         }
12738         
12739         this.fireEvent('valid', this);
12740     },
12741     
12742      /**
12743      * Mark this field as invalid
12744      * @param {String} msg The validation message
12745      */
12746     markInvalid : function(msg)
12747     {
12748         if(!this.el  || this.preventMark){ // not rendered
12749             return;
12750         }
12751         
12752         this.el.removeClass([this.invalidClass, this.validClass]);
12753         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12754         
12755         var feedback = this.el.select('.form-control-feedback', true).first();
12756             
12757         if(feedback){
12758             this.el.select('.form-control-feedback', true).first().removeClass(
12759                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12760         }
12761
12762         if(this.disabled){
12763             return;
12764         }
12765         
12766         if(this.allowBlank && !this.getRawValue().length){
12767             return;
12768         }
12769         
12770         if(this.indicator){
12771             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12772             this.indicator.addClass('visible');
12773         }
12774         if (Roo.bootstrap.version == 3) {
12775             this.el.addClass(this.invalidClass);
12776         } else {
12777             this.inputEl().addClass('is-invalid');
12778         }
12779         
12780         
12781         
12782         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12783             
12784             var feedback = this.el.select('.form-control-feedback', true).first();
12785             
12786             if(feedback){
12787                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12788                 
12789                 if(this.getValue().length || this.forceFeedback){
12790                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12791                 }
12792                 
12793             }
12794             
12795         }
12796         
12797         this.fireEvent('invalid', this, msg);
12798     },
12799     // private
12800     SafariOnKeyDown : function(event)
12801     {
12802         // this is a workaround for a password hang bug on chrome/ webkit.
12803         if (this.inputEl().dom.type != 'password') {
12804             return;
12805         }
12806         
12807         var isSelectAll = false;
12808         
12809         if(this.inputEl().dom.selectionEnd > 0){
12810             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12811         }
12812         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12813             event.preventDefault();
12814             this.setValue('');
12815             return;
12816         }
12817         
12818         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12819             
12820             event.preventDefault();
12821             // this is very hacky as keydown always get's upper case.
12822             //
12823             var cc = String.fromCharCode(event.getCharCode());
12824             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12825             
12826         }
12827     },
12828     adjustWidth : function(tag, w){
12829         tag = tag.toLowerCase();
12830         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12831             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12832                 if(tag == 'input'){
12833                     return w + 2;
12834                 }
12835                 if(tag == 'textarea'){
12836                     return w-2;
12837                 }
12838             }else if(Roo.isOpera){
12839                 if(tag == 'input'){
12840                     return w + 2;
12841                 }
12842                 if(tag == 'textarea'){
12843                     return w-2;
12844                 }
12845             }
12846         }
12847         return w;
12848     },
12849     
12850     setFieldLabel : function(v)
12851     {
12852         if(!this.rendered){
12853             return;
12854         }
12855         
12856         if(this.indicatorEl()){
12857             var ar = this.el.select('label > span',true);
12858             
12859             if (ar.elements.length) {
12860                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12861                 this.fieldLabel = v;
12862                 return;
12863             }
12864             
12865             var br = this.el.select('label',true);
12866             
12867             if(br.elements.length) {
12868                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12869                 this.fieldLabel = v;
12870                 return;
12871             }
12872             
12873             Roo.log('Cannot Found any of label > span || label in input');
12874             return;
12875         }
12876         
12877         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12878         this.fieldLabel = v;
12879         
12880         
12881     }
12882 });
12883
12884  
12885 /*
12886  * - LGPL
12887  *
12888  * Input
12889  * 
12890  */
12891
12892 /**
12893  * @class Roo.bootstrap.TextArea
12894  * @extends Roo.bootstrap.Input
12895  * Bootstrap TextArea class
12896  * @cfg {Number} cols Specifies the visible width of a text area
12897  * @cfg {Number} rows Specifies the visible number of lines in a text area
12898  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12899  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12900  * @cfg {string} html text
12901  * 
12902  * @constructor
12903  * Create a new TextArea
12904  * @param {Object} config The config object
12905  */
12906
12907 Roo.bootstrap.TextArea = function(config){
12908     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12909    
12910 };
12911
12912 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12913      
12914     cols : false,
12915     rows : 5,
12916     readOnly : false,
12917     warp : 'soft',
12918     resize : false,
12919     value: false,
12920     html: false,
12921     
12922     getAutoCreate : function(){
12923         
12924         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12925         
12926         var id = Roo.id();
12927         
12928         var cfg = {};
12929         
12930         if(this.inputType != 'hidden'){
12931             cfg.cls = 'form-group' //input-group
12932         }
12933         
12934         var input =  {
12935             tag: 'textarea',
12936             id : id,
12937             warp : this.warp,
12938             rows : this.rows,
12939             value : this.value || '',
12940             html: this.html || '',
12941             cls : 'form-control',
12942             placeholder : this.placeholder || '' 
12943             
12944         };
12945         
12946         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12947             input.maxLength = this.maxLength;
12948         }
12949         
12950         if(this.resize){
12951             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12952         }
12953         
12954         if(this.cols){
12955             input.cols = this.cols;
12956         }
12957         
12958         if (this.readOnly) {
12959             input.readonly = true;
12960         }
12961         
12962         if (this.name) {
12963             input.name = this.name;
12964         }
12965         
12966         if (this.size) {
12967             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12968         }
12969         
12970         var settings=this;
12971         ['xs','sm','md','lg'].map(function(size){
12972             if (settings[size]) {
12973                 cfg.cls += ' col-' + size + '-' + settings[size];
12974             }
12975         });
12976         
12977         var inputblock = input;
12978         
12979         if(this.hasFeedback && !this.allowBlank){
12980             
12981             var feedback = {
12982                 tag: 'span',
12983                 cls: 'glyphicon form-control-feedback'
12984             };
12985
12986             inputblock = {
12987                 cls : 'has-feedback',
12988                 cn :  [
12989                     input,
12990                     feedback
12991                 ] 
12992             };  
12993         }
12994         
12995         
12996         if (this.before || this.after) {
12997             
12998             inputblock = {
12999                 cls : 'input-group',
13000                 cn :  [] 
13001             };
13002             if (this.before) {
13003                 inputblock.cn.push({
13004                     tag :'span',
13005                     cls : 'input-group-addon',
13006                     html : this.before
13007                 });
13008             }
13009             
13010             inputblock.cn.push(input);
13011             
13012             if(this.hasFeedback && !this.allowBlank){
13013                 inputblock.cls += ' has-feedback';
13014                 inputblock.cn.push(feedback);
13015             }
13016             
13017             if (this.after) {
13018                 inputblock.cn.push({
13019                     tag :'span',
13020                     cls : 'input-group-addon',
13021                     html : this.after
13022                 });
13023             }
13024             
13025         }
13026         
13027         if (align ==='left' && this.fieldLabel.length) {
13028             cfg.cn = [
13029                 {
13030                     tag: 'label',
13031                     'for' :  id,
13032                     cls : 'control-label',
13033                     html : this.fieldLabel
13034                 },
13035                 {
13036                     cls : "",
13037                     cn: [
13038                         inputblock
13039                     ]
13040                 }
13041
13042             ];
13043             
13044             if(this.labelWidth > 12){
13045                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13046             }
13047
13048             if(this.labelWidth < 13 && this.labelmd == 0){
13049                 this.labelmd = this.labelWidth;
13050             }
13051
13052             if(this.labellg > 0){
13053                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13054                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13055             }
13056
13057             if(this.labelmd > 0){
13058                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13059                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13060             }
13061
13062             if(this.labelsm > 0){
13063                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13064                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13065             }
13066
13067             if(this.labelxs > 0){
13068                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13069                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13070             }
13071             
13072         } else if ( this.fieldLabel.length) {
13073             cfg.cn = [
13074
13075                {
13076                    tag: 'label',
13077                    //cls : 'input-group-addon',
13078                    html : this.fieldLabel
13079
13080                },
13081
13082                inputblock
13083
13084            ];
13085
13086         } else {
13087
13088             cfg.cn = [
13089
13090                 inputblock
13091
13092             ];
13093                 
13094         }
13095         
13096         if (this.disabled) {
13097             input.disabled=true;
13098         }
13099         
13100         return cfg;
13101         
13102     },
13103     /**
13104      * return the real textarea element.
13105      */
13106     inputEl: function ()
13107     {
13108         return this.el.select('textarea.form-control',true).first();
13109     },
13110     
13111     /**
13112      * Clear any invalid styles/messages for this field
13113      */
13114     clearInvalid : function()
13115     {
13116         
13117         if(!this.el || this.preventMark){ // not rendered
13118             return;
13119         }
13120         
13121         var label = this.el.select('label', true).first();
13122         var icon = this.el.select('i.fa-star', true).first();
13123         
13124         if(label && icon){
13125             icon.remove();
13126         }
13127         this.el.removeClass( this.validClass);
13128         this.inputEl().removeClass('is-invalid');
13129          
13130         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13131             
13132             var feedback = this.el.select('.form-control-feedback', true).first();
13133             
13134             if(feedback){
13135                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13136             }
13137             
13138         }
13139         
13140         this.fireEvent('valid', this);
13141     },
13142     
13143      /**
13144      * Mark this field as valid
13145      */
13146     markValid : function()
13147     {
13148         if(!this.el  || this.preventMark){ // not rendered
13149             return;
13150         }
13151         
13152         this.el.removeClass([this.invalidClass, this.validClass]);
13153         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13154         
13155         var feedback = this.el.select('.form-control-feedback', true).first();
13156             
13157         if(feedback){
13158             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13159         }
13160
13161         if(this.disabled || this.allowBlank){
13162             return;
13163         }
13164         
13165         var label = this.el.select('label', true).first();
13166         var icon = this.el.select('i.fa-star', true).first();
13167         
13168         if(label && icon){
13169             icon.remove();
13170         }
13171         if (Roo.bootstrap.version == 3) {
13172             this.el.addClass(this.validClass);
13173         } else {
13174             this.inputEl().addClass('is-valid');
13175         }
13176         
13177         
13178         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13179             
13180             var feedback = this.el.select('.form-control-feedback', true).first();
13181             
13182             if(feedback){
13183                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13184                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13185             }
13186             
13187         }
13188         
13189         this.fireEvent('valid', this);
13190     },
13191     
13192      /**
13193      * Mark this field as invalid
13194      * @param {String} msg The validation message
13195      */
13196     markInvalid : function(msg)
13197     {
13198         if(!this.el  || this.preventMark){ // not rendered
13199             return;
13200         }
13201         
13202         this.el.removeClass([this.invalidClass, this.validClass]);
13203         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13204         
13205         var feedback = this.el.select('.form-control-feedback', true).first();
13206             
13207         if(feedback){
13208             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13209         }
13210
13211         if(this.disabled || this.allowBlank){
13212             return;
13213         }
13214         
13215         var label = this.el.select('label', true).first();
13216         var icon = this.el.select('i.fa-star', true).first();
13217         
13218         if(!this.getValue().length && label && !icon){
13219             this.el.createChild({
13220                 tag : 'i',
13221                 cls : 'text-danger fa fa-lg fa-star',
13222                 tooltip : 'This field is required',
13223                 style : 'margin-right:5px;'
13224             }, label, true);
13225         }
13226         
13227         if (Roo.bootstrap.version == 3) {
13228             this.el.addClass(this.invalidClass);
13229         } else {
13230             this.inputEl().addClass('is-invalid');
13231         }
13232         
13233         // fixme ... this may be depricated need to test..
13234         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13235             
13236             var feedback = this.el.select('.form-control-feedback', true).first();
13237             
13238             if(feedback){
13239                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13240                 
13241                 if(this.getValue().length || this.forceFeedback){
13242                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13243                 }
13244                 
13245             }
13246             
13247         }
13248         
13249         this.fireEvent('invalid', this, msg);
13250     }
13251 });
13252
13253  
13254 /*
13255  * - LGPL
13256  *
13257  * trigger field - base class for combo..
13258  * 
13259  */
13260  
13261 /**
13262  * @class Roo.bootstrap.TriggerField
13263  * @extends Roo.bootstrap.Input
13264  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13265  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13266  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13267  * for which you can provide a custom implementation.  For example:
13268  * <pre><code>
13269 var trigger = new Roo.bootstrap.TriggerField();
13270 trigger.onTriggerClick = myTriggerFn;
13271 trigger.applyTo('my-field');
13272 </code></pre>
13273  *
13274  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13275  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13276  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13277  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13278  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13279
13280  * @constructor
13281  * Create a new TriggerField.
13282  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13283  * to the base TextField)
13284  */
13285 Roo.bootstrap.TriggerField = function(config){
13286     this.mimicing = false;
13287     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13288 };
13289
13290 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13291     /**
13292      * @cfg {String} triggerClass A CSS class to apply to the trigger
13293      */
13294      /**
13295      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13296      */
13297     hideTrigger:false,
13298
13299     /**
13300      * @cfg {Boolean} removable (true|false) special filter default false
13301      */
13302     removable : false,
13303     
13304     /** @cfg {Boolean} grow @hide */
13305     /** @cfg {Number} growMin @hide */
13306     /** @cfg {Number} growMax @hide */
13307
13308     /**
13309      * @hide 
13310      * @method
13311      */
13312     autoSize: Roo.emptyFn,
13313     // private
13314     monitorTab : true,
13315     // private
13316     deferHeight : true,
13317
13318     
13319     actionMode : 'wrap',
13320     
13321     caret : false,
13322     
13323     
13324     getAutoCreate : function(){
13325        
13326         var align = this.labelAlign || this.parentLabelAlign();
13327         
13328         var id = Roo.id();
13329         
13330         var cfg = {
13331             cls: 'form-group' //input-group
13332         };
13333         
13334         
13335         var input =  {
13336             tag: 'input',
13337             id : id,
13338             type : this.inputType,
13339             cls : 'form-control',
13340             autocomplete: 'new-password',
13341             placeholder : this.placeholder || '' 
13342             
13343         };
13344         if (this.name) {
13345             input.name = this.name;
13346         }
13347         if (this.size) {
13348             input.cls += ' input-' + this.size;
13349         }
13350         
13351         if (this.disabled) {
13352             input.disabled=true;
13353         }
13354         
13355         var inputblock = input;
13356         
13357         if(this.hasFeedback && !this.allowBlank){
13358             
13359             var feedback = {
13360                 tag: 'span',
13361                 cls: 'glyphicon form-control-feedback'
13362             };
13363             
13364             if(this.removable && !this.editable  ){
13365                 inputblock = {
13366                     cls : 'has-feedback',
13367                     cn :  [
13368                         inputblock,
13369                         {
13370                             tag: 'button',
13371                             html : 'x',
13372                             cls : 'roo-combo-removable-btn close'
13373                         },
13374                         feedback
13375                     ] 
13376                 };
13377             } else {
13378                 inputblock = {
13379                     cls : 'has-feedback',
13380                     cn :  [
13381                         inputblock,
13382                         feedback
13383                     ] 
13384                 };
13385             }
13386
13387         } else {
13388             if(this.removable && !this.editable ){
13389                 inputblock = {
13390                     cls : 'roo-removable',
13391                     cn :  [
13392                         inputblock,
13393                         {
13394                             tag: 'button',
13395                             html : 'x',
13396                             cls : 'roo-combo-removable-btn close'
13397                         }
13398                     ] 
13399                 };
13400             }
13401         }
13402         
13403         if (this.before || this.after) {
13404             
13405             inputblock = {
13406                 cls : 'input-group',
13407                 cn :  [] 
13408             };
13409             if (this.before) {
13410                 inputblock.cn.push({
13411                     tag :'span',
13412                     cls : 'input-group-addon input-group-prepend input-group-text',
13413                     html : this.before
13414                 });
13415             }
13416             
13417             inputblock.cn.push(input);
13418             
13419             if(this.hasFeedback && !this.allowBlank){
13420                 inputblock.cls += ' has-feedback';
13421                 inputblock.cn.push(feedback);
13422             }
13423             
13424             if (this.after) {
13425                 inputblock.cn.push({
13426                     tag :'span',
13427                     cls : 'input-group-addon input-group-append input-group-text',
13428                     html : this.after
13429                 });
13430             }
13431             
13432         };
13433         
13434       
13435         
13436         var ibwrap = inputblock;
13437         
13438         if(this.multiple){
13439             ibwrap = {
13440                 tag: 'ul',
13441                 cls: 'roo-select2-choices',
13442                 cn:[
13443                     {
13444                         tag: 'li',
13445                         cls: 'roo-select2-search-field',
13446                         cn: [
13447
13448                             inputblock
13449                         ]
13450                     }
13451                 ]
13452             };
13453                 
13454         }
13455         
13456         var combobox = {
13457             cls: 'roo-select2-container input-group',
13458             cn: [
13459                  {
13460                     tag: 'input',
13461                     type : 'hidden',
13462                     cls: 'form-hidden-field'
13463                 },
13464                 ibwrap
13465             ]
13466         };
13467         
13468         if(!this.multiple && this.showToggleBtn){
13469             
13470             var caret = {
13471                         tag: 'span',
13472                         cls: 'caret'
13473              };
13474             if (this.caret != false) {
13475                 caret = {
13476                      tag: 'i',
13477                      cls: 'fa fa-' + this.caret
13478                 };
13479                 
13480             }
13481             
13482             combobox.cn.push({
13483                 tag :'span',
13484                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13485                 cn : [
13486                     Roo.bootstrap.version == 3 ? caret : '',
13487                     {
13488                         tag: 'span',
13489                         cls: 'combobox-clear',
13490                         cn  : [
13491                             {
13492                                 tag : 'i',
13493                                 cls: 'icon-remove'
13494                             }
13495                         ]
13496                     }
13497                 ]
13498
13499             })
13500         }
13501         
13502         if(this.multiple){
13503             combobox.cls += ' roo-select2-container-multi';
13504         }
13505          var indicator = {
13506             tag : 'i',
13507             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13508             tooltip : 'This field is required'
13509         };
13510         if (Roo.bootstrap.version == 4) {
13511             indicator = {
13512                 tag : 'i',
13513                 style : 'display:none'
13514             };
13515         }
13516         
13517         
13518         if (align ==='left' && this.fieldLabel.length) {
13519             
13520             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13521
13522             cfg.cn = [
13523                 indicator,
13524                 {
13525                     tag: 'label',
13526                     'for' :  id,
13527                     cls : 'control-label',
13528                     html : this.fieldLabel
13529
13530                 },
13531                 {
13532                     cls : "", 
13533                     cn: [
13534                         combobox
13535                     ]
13536                 }
13537
13538             ];
13539             
13540             var labelCfg = cfg.cn[1];
13541             var contentCfg = cfg.cn[2];
13542             
13543             if(this.indicatorpos == 'right'){
13544                 cfg.cn = [
13545                     {
13546                         tag: 'label',
13547                         'for' :  id,
13548                         cls : 'control-label',
13549                         cn : [
13550                             {
13551                                 tag : 'span',
13552                                 html : this.fieldLabel
13553                             },
13554                             indicator
13555                         ]
13556                     },
13557                     {
13558                         cls : "", 
13559                         cn: [
13560                             combobox
13561                         ]
13562                     }
13563
13564                 ];
13565                 
13566                 labelCfg = cfg.cn[0];
13567                 contentCfg = cfg.cn[1];
13568             }
13569             
13570             if(this.labelWidth > 12){
13571                 labelCfg.style = "width: " + this.labelWidth + 'px';
13572             }
13573             
13574             if(this.labelWidth < 13 && this.labelmd == 0){
13575                 this.labelmd = this.labelWidth;
13576             }
13577             
13578             if(this.labellg > 0){
13579                 labelCfg.cls += ' col-lg-' + this.labellg;
13580                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13581             }
13582             
13583             if(this.labelmd > 0){
13584                 labelCfg.cls += ' col-md-' + this.labelmd;
13585                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13586             }
13587             
13588             if(this.labelsm > 0){
13589                 labelCfg.cls += ' col-sm-' + this.labelsm;
13590                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13591             }
13592             
13593             if(this.labelxs > 0){
13594                 labelCfg.cls += ' col-xs-' + this.labelxs;
13595                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13596             }
13597             
13598         } else if ( this.fieldLabel.length) {
13599 //                Roo.log(" label");
13600             cfg.cn = [
13601                 indicator,
13602                {
13603                    tag: 'label',
13604                    //cls : 'input-group-addon',
13605                    html : this.fieldLabel
13606
13607                },
13608
13609                combobox
13610
13611             ];
13612             
13613             if(this.indicatorpos == 'right'){
13614                 
13615                 cfg.cn = [
13616                     {
13617                        tag: 'label',
13618                        cn : [
13619                            {
13620                                tag : 'span',
13621                                html : this.fieldLabel
13622                            },
13623                            indicator
13624                        ]
13625
13626                     },
13627                     combobox
13628
13629                 ];
13630
13631             }
13632
13633         } else {
13634             
13635 //                Roo.log(" no label && no align");
13636                 cfg = combobox
13637                      
13638                 
13639         }
13640         
13641         var settings=this;
13642         ['xs','sm','md','lg'].map(function(size){
13643             if (settings[size]) {
13644                 cfg.cls += ' col-' + size + '-' + settings[size];
13645             }
13646         });
13647         
13648         return cfg;
13649         
13650     },
13651     
13652     
13653     
13654     // private
13655     onResize : function(w, h){
13656 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13657 //        if(typeof w == 'number'){
13658 //            var x = w - this.trigger.getWidth();
13659 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13660 //            this.trigger.setStyle('left', x+'px');
13661 //        }
13662     },
13663
13664     // private
13665     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13666
13667     // private
13668     getResizeEl : function(){
13669         return this.inputEl();
13670     },
13671
13672     // private
13673     getPositionEl : function(){
13674         return this.inputEl();
13675     },
13676
13677     // private
13678     alignErrorIcon : function(){
13679         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13680     },
13681
13682     // private
13683     initEvents : function(){
13684         
13685         this.createList();
13686         
13687         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13688         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13689         if(!this.multiple && this.showToggleBtn){
13690             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13691             if(this.hideTrigger){
13692                 this.trigger.setDisplayed(false);
13693             }
13694             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13695         }
13696         
13697         if(this.multiple){
13698             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13699         }
13700         
13701         if(this.removable && !this.editable && !this.tickable){
13702             var close = this.closeTriggerEl();
13703             
13704             if(close){
13705                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13706                 close.on('click', this.removeBtnClick, this, close);
13707             }
13708         }
13709         
13710         //this.trigger.addClassOnOver('x-form-trigger-over');
13711         //this.trigger.addClassOnClick('x-form-trigger-click');
13712         
13713         //if(!this.width){
13714         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13715         //}
13716     },
13717     
13718     closeTriggerEl : function()
13719     {
13720         var close = this.el.select('.roo-combo-removable-btn', true).first();
13721         return close ? close : false;
13722     },
13723     
13724     removeBtnClick : function(e, h, el)
13725     {
13726         e.preventDefault();
13727         
13728         if(this.fireEvent("remove", this) !== false){
13729             this.reset();
13730             this.fireEvent("afterremove", this)
13731         }
13732     },
13733     
13734     createList : function()
13735     {
13736         this.list = Roo.get(document.body).createChild({
13737             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13738             cls: 'typeahead typeahead-long dropdown-menu shadow',
13739             style: 'display:none'
13740         });
13741         
13742         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13743         
13744     },
13745
13746     // private
13747     initTrigger : function(){
13748        
13749     },
13750
13751     // private
13752     onDestroy : function(){
13753         if(this.trigger){
13754             this.trigger.removeAllListeners();
13755           //  this.trigger.remove();
13756         }
13757         //if(this.wrap){
13758         //    this.wrap.remove();
13759         //}
13760         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13761     },
13762
13763     // private
13764     onFocus : function(){
13765         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13766         /*
13767         if(!this.mimicing){
13768             this.wrap.addClass('x-trigger-wrap-focus');
13769             this.mimicing = true;
13770             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13771             if(this.monitorTab){
13772                 this.el.on("keydown", this.checkTab, this);
13773             }
13774         }
13775         */
13776     },
13777
13778     // private
13779     checkTab : function(e){
13780         if(e.getKey() == e.TAB){
13781             this.triggerBlur();
13782         }
13783     },
13784
13785     // private
13786     onBlur : function(){
13787         // do nothing
13788     },
13789
13790     // private
13791     mimicBlur : function(e, t){
13792         /*
13793         if(!this.wrap.contains(t) && this.validateBlur()){
13794             this.triggerBlur();
13795         }
13796         */
13797     },
13798
13799     // private
13800     triggerBlur : function(){
13801         this.mimicing = false;
13802         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13803         if(this.monitorTab){
13804             this.el.un("keydown", this.checkTab, this);
13805         }
13806         //this.wrap.removeClass('x-trigger-wrap-focus');
13807         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13808     },
13809
13810     // private
13811     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13812     validateBlur : function(e, t){
13813         return true;
13814     },
13815
13816     // private
13817     onDisable : function(){
13818         this.inputEl().dom.disabled = true;
13819         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13820         //if(this.wrap){
13821         //    this.wrap.addClass('x-item-disabled');
13822         //}
13823     },
13824
13825     // private
13826     onEnable : function(){
13827         this.inputEl().dom.disabled = false;
13828         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13829         //if(this.wrap){
13830         //    this.el.removeClass('x-item-disabled');
13831         //}
13832     },
13833
13834     // private
13835     onShow : function(){
13836         var ae = this.getActionEl();
13837         
13838         if(ae){
13839             ae.dom.style.display = '';
13840             ae.dom.style.visibility = 'visible';
13841         }
13842     },
13843
13844     // private
13845     
13846     onHide : function(){
13847         var ae = this.getActionEl();
13848         ae.dom.style.display = 'none';
13849     },
13850
13851     /**
13852      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13853      * by an implementing function.
13854      * @method
13855      * @param {EventObject} e
13856      */
13857     onTriggerClick : Roo.emptyFn
13858 });
13859  
13860 /*
13861 * Licence: LGPL
13862 */
13863
13864 /**
13865  * @class Roo.bootstrap.CardUploader
13866  * @extends Roo.bootstrap.Button
13867  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13868  * @cfg {Number} errorTimeout default 3000
13869  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13870  * @cfg {Array}  html The button text.
13871
13872  *
13873  * @constructor
13874  * Create a new CardUploader
13875  * @param {Object} config The config object
13876  */
13877
13878 Roo.bootstrap.CardUploader = function(config){
13879     
13880  
13881     
13882     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13883     
13884     
13885     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13886         return r.data.id
13887      });
13888     
13889      this.addEvents({
13890          // raw events
13891         /**
13892          * @event preview
13893          * When a image is clicked on - and needs to display a slideshow or similar..
13894          * @param {Roo.bootstrap.Card} this
13895          * @param {Object} The image information data 
13896          *
13897          */
13898         'preview' : true,
13899          /**
13900          * @event download
13901          * When a the download link is clicked
13902          * @param {Roo.bootstrap.Card} this
13903          * @param {Object} The image information data  contains 
13904          */
13905         'download' : true
13906         
13907     });
13908 };
13909  
13910 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13911     
13912      
13913     errorTimeout : 3000,
13914      
13915     images : false,
13916    
13917     fileCollection : false,
13918     allowBlank : true,
13919     
13920     getAutoCreate : function()
13921     {
13922         
13923         var cfg =  {
13924             cls :'form-group' ,
13925             cn : [
13926                
13927                 {
13928                     tag: 'label',
13929                    //cls : 'input-group-addon',
13930                     html : this.fieldLabel
13931
13932                 },
13933
13934                 {
13935                     tag: 'input',
13936                     type : 'hidden',
13937                     name : this.name,
13938                     value : this.value,
13939                     cls : 'd-none  form-control'
13940                 },
13941                 
13942                 {
13943                     tag: 'input',
13944                     multiple : 'multiple',
13945                     type : 'file',
13946                     cls : 'd-none  roo-card-upload-selector'
13947                 },
13948                 
13949                 {
13950                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13951                 },
13952                 {
13953                     cls : 'card-columns roo-card-uploader-container'
13954                 }
13955
13956             ]
13957         };
13958            
13959          
13960         return cfg;
13961     },
13962     
13963     getChildContainer : function() /// what children are added to.
13964     {
13965         return this.containerEl;
13966     },
13967    
13968     getButtonContainer : function() /// what children are added to.
13969     {
13970         return this.el.select(".roo-card-uploader-button-container").first();
13971     },
13972    
13973     initEvents : function()
13974     {
13975         
13976         Roo.bootstrap.Input.prototype.initEvents.call(this);
13977         
13978         var t = this;
13979         this.addxtype({
13980             xns: Roo.bootstrap,
13981
13982             xtype : 'Button',
13983             container_method : 'getButtonContainer' ,            
13984             html :  this.html, // fix changable?
13985             cls : 'w-100 ',
13986             listeners : {
13987                 'click' : function(btn, e) {
13988                     t.onClick(e);
13989                 }
13990             }
13991         });
13992         
13993         
13994         
13995         
13996         this.urlAPI = (window.createObjectURL && window) || 
13997                                 (window.URL && URL.revokeObjectURL && URL) || 
13998                                 (window.webkitURL && webkitURL);
13999                         
14000          
14001          
14002          
14003         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14004         
14005         this.selectorEl.on('change', this.onFileSelected, this);
14006         if (this.images) {
14007             var t = this;
14008             this.images.forEach(function(img) {
14009                 t.addCard(img)
14010             });
14011             this.images = false;
14012         }
14013         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14014          
14015        
14016     },
14017     
14018    
14019     onClick : function(e)
14020     {
14021         e.preventDefault();
14022          
14023         this.selectorEl.dom.click();
14024          
14025     },
14026     
14027     onFileSelected : function(e)
14028     {
14029         e.preventDefault();
14030         
14031         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14032             return;
14033         }
14034         
14035         Roo.each(this.selectorEl.dom.files, function(file){    
14036             this.addFile(file);
14037         }, this);
14038          
14039     },
14040     
14041       
14042     
14043       
14044     
14045     addFile : function(file)
14046     {
14047            
14048         if(typeof(file) === 'string'){
14049             throw "Add file by name?"; // should not happen
14050             return;
14051         }
14052         
14053         if(!file || !this.urlAPI){
14054             return;
14055         }
14056         
14057         // file;
14058         // file.type;
14059         
14060         var _this = this;
14061         
14062         
14063         var url = _this.urlAPI.createObjectURL( file);
14064            
14065         this.addCard({
14066             id : Roo.bootstrap.CardUploader.ID--,
14067             is_uploaded : false,
14068             src : url,
14069             srcfile : file,
14070             title : file.name,
14071             mimetype : file.type,
14072             preview : false,
14073             is_deleted : 0
14074         });
14075         
14076     },
14077     
14078     /**
14079      * addCard - add an Attachment to the uploader
14080      * @param data - the data about the image to upload
14081      *
14082      * {
14083           id : 123
14084           title : "Title of file",
14085           is_uploaded : false,
14086           src : "http://.....",
14087           srcfile : { the File upload object },
14088           mimetype : file.type,
14089           preview : false,
14090           is_deleted : 0
14091           .. any other data...
14092         }
14093      *
14094      * 
14095     */
14096     
14097     addCard : function (data)
14098     {
14099         // hidden input element?
14100         // if the file is not an image...
14101         //then we need to use something other that and header_image
14102         var t = this;
14103         //   remove.....
14104         var footer = [
14105             {
14106                 xns : Roo.bootstrap,
14107                 xtype : 'CardFooter',
14108                  items: [
14109                     {
14110                         xns : Roo.bootstrap,
14111                         xtype : 'Element',
14112                         cls : 'd-flex',
14113                         items : [
14114                             
14115                             {
14116                                 xns : Roo.bootstrap,
14117                                 xtype : 'Button',
14118                                 html : String.format("<small>{0}</small>", data.title),
14119                                 cls : 'col-10 text-left',
14120                                 size: 'sm',
14121                                 weight: 'link',
14122                                 fa : 'download',
14123                                 listeners : {
14124                                     click : function() {
14125                                      
14126                                         t.fireEvent( "download", t, data );
14127                                     }
14128                                 }
14129                             },
14130                           
14131                             {
14132                                 xns : Roo.bootstrap,
14133                                 xtype : 'Button',
14134                                 style: 'max-height: 28px; ',
14135                                 size : 'sm',
14136                                 weight: 'danger',
14137                                 cls : 'col-2',
14138                                 fa : 'times',
14139                                 listeners : {
14140                                     click : function() {
14141                                         t.removeCard(data.id)
14142                                     }
14143                                 }
14144                             }
14145                         ]
14146                     }
14147                     
14148                 ] 
14149             }
14150             
14151         ];
14152         
14153         var cn = this.addxtype(
14154             {
14155                  
14156                 xns : Roo.bootstrap,
14157                 xtype : 'Card',
14158                 closeable : true,
14159                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14160                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14161                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14162                 data : data,
14163                 html : false,
14164                  
14165                 items : footer,
14166                 initEvents : function() {
14167                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14168                     var card = this;
14169                     this.imgEl = this.el.select('.card-img-top').first();
14170                     if (this.imgEl) {
14171                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14172                         this.imgEl.set({ 'pointer' : 'cursor' });
14173                                   
14174                     }
14175                     this.getCardFooter().addClass('p-1');
14176                     
14177                   
14178                 }
14179                 
14180             }
14181         );
14182         // dont' really need ot update items.
14183         // this.items.push(cn);
14184         this.fileCollection.add(cn);
14185         
14186         if (!data.srcfile) {
14187             this.updateInput();
14188             return;
14189         }
14190             
14191         var _t = this;
14192         var reader = new FileReader();
14193         reader.addEventListener("load", function() {  
14194             data.srcdata =  reader.result;
14195             _t.updateInput();
14196         });
14197         reader.readAsDataURL(data.srcfile);
14198         
14199         
14200         
14201     },
14202     removeCard : function(id)
14203     {
14204         
14205         var card  = this.fileCollection.get(id);
14206         card.data.is_deleted = 1;
14207         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14208         //this.fileCollection.remove(card);
14209         //this.items = this.items.filter(function(e) { return e != card });
14210         // dont' really need ot update items.
14211         card.el.dom.parentNode.removeChild(card.el.dom);
14212         this.updateInput();
14213
14214         
14215     },
14216     reset: function()
14217     {
14218         this.fileCollection.each(function(card) {
14219             if (card.el.dom && card.el.dom.parentNode) {
14220                 card.el.dom.parentNode.removeChild(card.el.dom);
14221             }
14222         });
14223         this.fileCollection.clear();
14224         this.updateInput();
14225     },
14226     
14227     updateInput : function()
14228     {
14229          var data = [];
14230         this.fileCollection.each(function(e) {
14231             data.push(e.data);
14232             
14233         });
14234         this.inputEl().dom.value = JSON.stringify(data);
14235         
14236         
14237         
14238     }
14239     
14240     
14241 });
14242
14243
14244 Roo.bootstrap.CardUploader.ID = -1;/*
14245  * Based on:
14246  * Ext JS Library 1.1.1
14247  * Copyright(c) 2006-2007, Ext JS, LLC.
14248  *
14249  * Originally Released Under LGPL - original licence link has changed is not relivant.
14250  *
14251  * Fork - LGPL
14252  * <script type="text/javascript">
14253  */
14254
14255
14256 /**
14257  * @class Roo.data.SortTypes
14258  * @singleton
14259  * Defines the default sorting (casting?) comparison functions used when sorting data.
14260  */
14261 Roo.data.SortTypes = {
14262     /**
14263      * Default sort that does nothing
14264      * @param {Mixed} s The value being converted
14265      * @return {Mixed} The comparison value
14266      */
14267     none : function(s){
14268         return s;
14269     },
14270     
14271     /**
14272      * The regular expression used to strip tags
14273      * @type {RegExp}
14274      * @property
14275      */
14276     stripTagsRE : /<\/?[^>]+>/gi,
14277     
14278     /**
14279      * Strips all HTML tags to sort on text only
14280      * @param {Mixed} s The value being converted
14281      * @return {String} The comparison value
14282      */
14283     asText : function(s){
14284         return String(s).replace(this.stripTagsRE, "");
14285     },
14286     
14287     /**
14288      * Strips all HTML tags to sort on text only - Case insensitive
14289      * @param {Mixed} s The value being converted
14290      * @return {String} The comparison value
14291      */
14292     asUCText : function(s){
14293         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14294     },
14295     
14296     /**
14297      * Case insensitive string
14298      * @param {Mixed} s The value being converted
14299      * @return {String} The comparison value
14300      */
14301     asUCString : function(s) {
14302         return String(s).toUpperCase();
14303     },
14304     
14305     /**
14306      * Date sorting
14307      * @param {Mixed} s The value being converted
14308      * @return {Number} The comparison value
14309      */
14310     asDate : function(s) {
14311         if(!s){
14312             return 0;
14313         }
14314         if(s instanceof Date){
14315             return s.getTime();
14316         }
14317         return Date.parse(String(s));
14318     },
14319     
14320     /**
14321      * Float sorting
14322      * @param {Mixed} s The value being converted
14323      * @return {Float} The comparison value
14324      */
14325     asFloat : function(s) {
14326         var val = parseFloat(String(s).replace(/,/g, ""));
14327         if(isNaN(val)) {
14328             val = 0;
14329         }
14330         return val;
14331     },
14332     
14333     /**
14334      * Integer sorting
14335      * @param {Mixed} s The value being converted
14336      * @return {Number} The comparison value
14337      */
14338     asInt : function(s) {
14339         var val = parseInt(String(s).replace(/,/g, ""));
14340         if(isNaN(val)) {
14341             val = 0;
14342         }
14343         return val;
14344     }
14345 };/*
14346  * Based on:
14347  * Ext JS Library 1.1.1
14348  * Copyright(c) 2006-2007, Ext JS, LLC.
14349  *
14350  * Originally Released Under LGPL - original licence link has changed is not relivant.
14351  *
14352  * Fork - LGPL
14353  * <script type="text/javascript">
14354  */
14355
14356 /**
14357 * @class Roo.data.Record
14358  * Instances of this class encapsulate both record <em>definition</em> information, and record
14359  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14360  * to access Records cached in an {@link Roo.data.Store} object.<br>
14361  * <p>
14362  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14363  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14364  * objects.<br>
14365  * <p>
14366  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14367  * @constructor
14368  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14369  * {@link #create}. The parameters are the same.
14370  * @param {Array} data An associative Array of data values keyed by the field name.
14371  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14372  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14373  * not specified an integer id is generated.
14374  */
14375 Roo.data.Record = function(data, id){
14376     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14377     this.data = data;
14378 };
14379
14380 /**
14381  * Generate a constructor for a specific record layout.
14382  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14383  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14384  * Each field definition object may contain the following properties: <ul>
14385  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14386  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14387  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14388  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14389  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14390  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14391  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14392  * this may be omitted.</p></li>
14393  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14394  * <ul><li>auto (Default, implies no conversion)</li>
14395  * <li>string</li>
14396  * <li>int</li>
14397  * <li>float</li>
14398  * <li>boolean</li>
14399  * <li>date</li></ul></p></li>
14400  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14401  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14402  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14403  * by the Reader into an object that will be stored in the Record. It is passed the
14404  * following parameters:<ul>
14405  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14406  * </ul></p></li>
14407  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14408  * </ul>
14409  * <br>usage:<br><pre><code>
14410 var TopicRecord = Roo.data.Record.create(
14411     {name: 'title', mapping: 'topic_title'},
14412     {name: 'author', mapping: 'username'},
14413     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14414     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14415     {name: 'lastPoster', mapping: 'user2'},
14416     {name: 'excerpt', mapping: 'post_text'}
14417 );
14418
14419 var myNewRecord = new TopicRecord({
14420     title: 'Do my job please',
14421     author: 'noobie',
14422     totalPosts: 1,
14423     lastPost: new Date(),
14424     lastPoster: 'Animal',
14425     excerpt: 'No way dude!'
14426 });
14427 myStore.add(myNewRecord);
14428 </code></pre>
14429  * @method create
14430  * @static
14431  */
14432 Roo.data.Record.create = function(o){
14433     var f = function(){
14434         f.superclass.constructor.apply(this, arguments);
14435     };
14436     Roo.extend(f, Roo.data.Record);
14437     var p = f.prototype;
14438     p.fields = new Roo.util.MixedCollection(false, function(field){
14439         return field.name;
14440     });
14441     for(var i = 0, len = o.length; i < len; i++){
14442         p.fields.add(new Roo.data.Field(o[i]));
14443     }
14444     f.getField = function(name){
14445         return p.fields.get(name);  
14446     };
14447     return f;
14448 };
14449
14450 Roo.data.Record.AUTO_ID = 1000;
14451 Roo.data.Record.EDIT = 'edit';
14452 Roo.data.Record.REJECT = 'reject';
14453 Roo.data.Record.COMMIT = 'commit';
14454
14455 Roo.data.Record.prototype = {
14456     /**
14457      * Readonly flag - true if this record has been modified.
14458      * @type Boolean
14459      */
14460     dirty : false,
14461     editing : false,
14462     error: null,
14463     modified: null,
14464
14465     // private
14466     join : function(store){
14467         this.store = store;
14468     },
14469
14470     /**
14471      * Set the named field to the specified value.
14472      * @param {String} name The name of the field to set.
14473      * @param {Object} value The value to set the field to.
14474      */
14475     set : function(name, value){
14476         if(this.data[name] == value){
14477             return;
14478         }
14479         this.dirty = true;
14480         if(!this.modified){
14481             this.modified = {};
14482         }
14483         if(typeof this.modified[name] == 'undefined'){
14484             this.modified[name] = this.data[name];
14485         }
14486         this.data[name] = value;
14487         if(!this.editing && this.store){
14488             this.store.afterEdit(this);
14489         }       
14490     },
14491
14492     /**
14493      * Get the value of the named field.
14494      * @param {String} name The name of the field to get the value of.
14495      * @return {Object} The value of the field.
14496      */
14497     get : function(name){
14498         return this.data[name]; 
14499     },
14500
14501     // private
14502     beginEdit : function(){
14503         this.editing = true;
14504         this.modified = {}; 
14505     },
14506
14507     // private
14508     cancelEdit : function(){
14509         this.editing = false;
14510         delete this.modified;
14511     },
14512
14513     // private
14514     endEdit : function(){
14515         this.editing = false;
14516         if(this.dirty && this.store){
14517             this.store.afterEdit(this);
14518         }
14519     },
14520
14521     /**
14522      * Usually called by the {@link Roo.data.Store} which owns the Record.
14523      * Rejects all changes made to the Record since either creation, or the last commit operation.
14524      * Modified fields are reverted to their original values.
14525      * <p>
14526      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14527      * of reject operations.
14528      */
14529     reject : function(){
14530         var m = this.modified;
14531         for(var n in m){
14532             if(typeof m[n] != "function"){
14533                 this.data[n] = m[n];
14534             }
14535         }
14536         this.dirty = false;
14537         delete this.modified;
14538         this.editing = false;
14539         if(this.store){
14540             this.store.afterReject(this);
14541         }
14542     },
14543
14544     /**
14545      * Usually called by the {@link Roo.data.Store} which owns the Record.
14546      * Commits all changes made to the Record since either creation, or the last commit operation.
14547      * <p>
14548      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14549      * of commit operations.
14550      */
14551     commit : function(){
14552         this.dirty = false;
14553         delete this.modified;
14554         this.editing = false;
14555         if(this.store){
14556             this.store.afterCommit(this);
14557         }
14558     },
14559
14560     // private
14561     hasError : function(){
14562         return this.error != null;
14563     },
14564
14565     // private
14566     clearError : function(){
14567         this.error = null;
14568     },
14569
14570     /**
14571      * Creates a copy of this record.
14572      * @param {String} id (optional) A new record id if you don't want to use this record's id
14573      * @return {Record}
14574      */
14575     copy : function(newId) {
14576         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14577     }
14578 };/*
14579  * Based on:
14580  * Ext JS Library 1.1.1
14581  * Copyright(c) 2006-2007, Ext JS, LLC.
14582  *
14583  * Originally Released Under LGPL - original licence link has changed is not relivant.
14584  *
14585  * Fork - LGPL
14586  * <script type="text/javascript">
14587  */
14588
14589
14590
14591 /**
14592  * @class Roo.data.Store
14593  * @extends Roo.util.Observable
14594  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14595  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14596  * <p>
14597  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
14598  * has no knowledge of the format of the data returned by the Proxy.<br>
14599  * <p>
14600  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14601  * instances from the data object. These records are cached and made available through accessor functions.
14602  * @constructor
14603  * Creates a new Store.
14604  * @param {Object} config A config object containing the objects needed for the Store to access data,
14605  * and read the data into Records.
14606  */
14607 Roo.data.Store = function(config){
14608     this.data = new Roo.util.MixedCollection(false);
14609     this.data.getKey = function(o){
14610         return o.id;
14611     };
14612     this.baseParams = {};
14613     // private
14614     this.paramNames = {
14615         "start" : "start",
14616         "limit" : "limit",
14617         "sort" : "sort",
14618         "dir" : "dir",
14619         "multisort" : "_multisort"
14620     };
14621
14622     if(config && config.data){
14623         this.inlineData = config.data;
14624         delete config.data;
14625     }
14626
14627     Roo.apply(this, config);
14628     
14629     if(this.reader){ // reader passed
14630         this.reader = Roo.factory(this.reader, Roo.data);
14631         this.reader.xmodule = this.xmodule || false;
14632         if(!this.recordType){
14633             this.recordType = this.reader.recordType;
14634         }
14635         if(this.reader.onMetaChange){
14636             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14637         }
14638     }
14639
14640     if(this.recordType){
14641         this.fields = this.recordType.prototype.fields;
14642     }
14643     this.modified = [];
14644
14645     this.addEvents({
14646         /**
14647          * @event datachanged
14648          * Fires when the data cache has changed, and a widget which is using this Store
14649          * as a Record cache should refresh its view.
14650          * @param {Store} this
14651          */
14652         datachanged : true,
14653         /**
14654          * @event metachange
14655          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14656          * @param {Store} this
14657          * @param {Object} meta The JSON metadata
14658          */
14659         metachange : true,
14660         /**
14661          * @event add
14662          * Fires when Records have been added to the Store
14663          * @param {Store} this
14664          * @param {Roo.data.Record[]} records The array of Records added
14665          * @param {Number} index The index at which the record(s) were added
14666          */
14667         add : true,
14668         /**
14669          * @event remove
14670          * Fires when a Record has been removed from the Store
14671          * @param {Store} this
14672          * @param {Roo.data.Record} record The Record that was removed
14673          * @param {Number} index The index at which the record was removed
14674          */
14675         remove : true,
14676         /**
14677          * @event update
14678          * Fires when a Record has been updated
14679          * @param {Store} this
14680          * @param {Roo.data.Record} record The Record that was updated
14681          * @param {String} operation The update operation being performed.  Value may be one of:
14682          * <pre><code>
14683  Roo.data.Record.EDIT
14684  Roo.data.Record.REJECT
14685  Roo.data.Record.COMMIT
14686          * </code></pre>
14687          */
14688         update : true,
14689         /**
14690          * @event clear
14691          * Fires when the data cache has been cleared.
14692          * @param {Store} this
14693          */
14694         clear : true,
14695         /**
14696          * @event beforeload
14697          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14698          * the load action will be canceled.
14699          * @param {Store} this
14700          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14701          */
14702         beforeload : true,
14703         /**
14704          * @event beforeloadadd
14705          * Fires after a new set of Records has been loaded.
14706          * @param {Store} this
14707          * @param {Roo.data.Record[]} records The Records that were loaded
14708          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14709          */
14710         beforeloadadd : true,
14711         /**
14712          * @event load
14713          * Fires after a new set of Records has been loaded, before they are added to the store.
14714          * @param {Store} this
14715          * @param {Roo.data.Record[]} records The Records that were loaded
14716          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14717          * @params {Object} return from reader
14718          */
14719         load : true,
14720         /**
14721          * @event loadexception
14722          * Fires if an exception occurs in the Proxy during loading.
14723          * Called with the signature of the Proxy's "loadexception" event.
14724          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14725          * 
14726          * @param {Proxy} 
14727          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14728          * @param {Object} load options 
14729          * @param {Object} jsonData from your request (normally this contains the Exception)
14730          */
14731         loadexception : true
14732     });
14733     
14734     if(this.proxy){
14735         this.proxy = Roo.factory(this.proxy, Roo.data);
14736         this.proxy.xmodule = this.xmodule || false;
14737         this.relayEvents(this.proxy,  ["loadexception"]);
14738     }
14739     this.sortToggle = {};
14740     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14741
14742     Roo.data.Store.superclass.constructor.call(this);
14743
14744     if(this.inlineData){
14745         this.loadData(this.inlineData);
14746         delete this.inlineData;
14747     }
14748 };
14749
14750 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14751      /**
14752     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14753     * without a remote query - used by combo/forms at present.
14754     */
14755     
14756     /**
14757     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14758     */
14759     /**
14760     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14761     */
14762     /**
14763     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14764     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14765     */
14766     /**
14767     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14768     * on any HTTP request
14769     */
14770     /**
14771     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14772     */
14773     /**
14774     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14775     */
14776     multiSort: false,
14777     /**
14778     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14779     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14780     */
14781     remoteSort : false,
14782
14783     /**
14784     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14785      * loaded or when a record is removed. (defaults to false).
14786     */
14787     pruneModifiedRecords : false,
14788
14789     // private
14790     lastOptions : null,
14791
14792     /**
14793      * Add Records to the Store and fires the add event.
14794      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14795      */
14796     add : function(records){
14797         records = [].concat(records);
14798         for(var i = 0, len = records.length; i < len; i++){
14799             records[i].join(this);
14800         }
14801         var index = this.data.length;
14802         this.data.addAll(records);
14803         this.fireEvent("add", this, records, index);
14804     },
14805
14806     /**
14807      * Remove a Record from the Store and fires the remove event.
14808      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14809      */
14810     remove : function(record){
14811         var index = this.data.indexOf(record);
14812         this.data.removeAt(index);
14813  
14814         if(this.pruneModifiedRecords){
14815             this.modified.remove(record);
14816         }
14817         this.fireEvent("remove", this, record, index);
14818     },
14819
14820     /**
14821      * Remove all Records from the Store and fires the clear event.
14822      */
14823     removeAll : function(){
14824         this.data.clear();
14825         if(this.pruneModifiedRecords){
14826             this.modified = [];
14827         }
14828         this.fireEvent("clear", this);
14829     },
14830
14831     /**
14832      * Inserts Records to the Store at the given index and fires the add event.
14833      * @param {Number} index The start index at which to insert the passed Records.
14834      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14835      */
14836     insert : function(index, records){
14837         records = [].concat(records);
14838         for(var i = 0, len = records.length; i < len; i++){
14839             this.data.insert(index, records[i]);
14840             records[i].join(this);
14841         }
14842         this.fireEvent("add", this, records, index);
14843     },
14844
14845     /**
14846      * Get the index within the cache of the passed Record.
14847      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14848      * @return {Number} The index of the passed Record. Returns -1 if not found.
14849      */
14850     indexOf : function(record){
14851         return this.data.indexOf(record);
14852     },
14853
14854     /**
14855      * Get the index within the cache of the Record with the passed id.
14856      * @param {String} id The id of the Record to find.
14857      * @return {Number} The index of the Record. Returns -1 if not found.
14858      */
14859     indexOfId : function(id){
14860         return this.data.indexOfKey(id);
14861     },
14862
14863     /**
14864      * Get the Record with the specified id.
14865      * @param {String} id The id of the Record to find.
14866      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14867      */
14868     getById : function(id){
14869         return this.data.key(id);
14870     },
14871
14872     /**
14873      * Get the Record at the specified index.
14874      * @param {Number} index The index of the Record to find.
14875      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14876      */
14877     getAt : function(index){
14878         return this.data.itemAt(index);
14879     },
14880
14881     /**
14882      * Returns a range of Records between specified indices.
14883      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14884      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14885      * @return {Roo.data.Record[]} An array of Records
14886      */
14887     getRange : function(start, end){
14888         return this.data.getRange(start, end);
14889     },
14890
14891     // private
14892     storeOptions : function(o){
14893         o = Roo.apply({}, o);
14894         delete o.callback;
14895         delete o.scope;
14896         this.lastOptions = o;
14897     },
14898
14899     /**
14900      * Loads the Record cache from the configured Proxy using the configured Reader.
14901      * <p>
14902      * If using remote paging, then the first load call must specify the <em>start</em>
14903      * and <em>limit</em> properties in the options.params property to establish the initial
14904      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14905      * <p>
14906      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14907      * and this call will return before the new data has been loaded. Perform any post-processing
14908      * in a callback function, or in a "load" event handler.</strong>
14909      * <p>
14910      * @param {Object} options An object containing properties which control loading options:<ul>
14911      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14912      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14913      * passed the following arguments:<ul>
14914      * <li>r : Roo.data.Record[]</li>
14915      * <li>options: Options object from the load call</li>
14916      * <li>success: Boolean success indicator</li></ul></li>
14917      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14918      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14919      * </ul>
14920      */
14921     load : function(options){
14922         options = options || {};
14923         if(this.fireEvent("beforeload", this, options) !== false){
14924             this.storeOptions(options);
14925             var p = Roo.apply(options.params || {}, this.baseParams);
14926             // if meta was not loaded from remote source.. try requesting it.
14927             if (!this.reader.metaFromRemote) {
14928                 p._requestMeta = 1;
14929             }
14930             if(this.sortInfo && this.remoteSort){
14931                 var pn = this.paramNames;
14932                 p[pn["sort"]] = this.sortInfo.field;
14933                 p[pn["dir"]] = this.sortInfo.direction;
14934             }
14935             if (this.multiSort) {
14936                 var pn = this.paramNames;
14937                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14938             }
14939             
14940             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14941         }
14942     },
14943
14944     /**
14945      * Reloads the Record cache from the configured Proxy using the configured Reader and
14946      * the options from the last load operation performed.
14947      * @param {Object} options (optional) An object containing properties which may override the options
14948      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14949      * the most recently used options are reused).
14950      */
14951     reload : function(options){
14952         this.load(Roo.applyIf(options||{}, this.lastOptions));
14953     },
14954
14955     // private
14956     // Called as a callback by the Reader during a load operation.
14957     loadRecords : function(o, options, success){
14958         if(!o || success === false){
14959             if(success !== false){
14960                 this.fireEvent("load", this, [], options, o);
14961             }
14962             if(options.callback){
14963                 options.callback.call(options.scope || this, [], options, false);
14964             }
14965             return;
14966         }
14967         // if data returned failure - throw an exception.
14968         if (o.success === false) {
14969             // show a message if no listener is registered.
14970             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14971                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14972             }
14973             // loadmask wil be hooked into this..
14974             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14975             return;
14976         }
14977         var r = o.records, t = o.totalRecords || r.length;
14978         
14979         this.fireEvent("beforeloadadd", this, r, options, o);
14980         
14981         if(!options || options.add !== true){
14982             if(this.pruneModifiedRecords){
14983                 this.modified = [];
14984             }
14985             for(var i = 0, len = r.length; i < len; i++){
14986                 r[i].join(this);
14987             }
14988             if(this.snapshot){
14989                 this.data = this.snapshot;
14990                 delete this.snapshot;
14991             }
14992             this.data.clear();
14993             this.data.addAll(r);
14994             this.totalLength = t;
14995             this.applySort();
14996             this.fireEvent("datachanged", this);
14997         }else{
14998             this.totalLength = Math.max(t, this.data.length+r.length);
14999             this.add(r);
15000         }
15001         
15002         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15003                 
15004             var e = new Roo.data.Record({});
15005
15006             e.set(this.parent.displayField, this.parent.emptyTitle);
15007             e.set(this.parent.valueField, '');
15008
15009             this.insert(0, e);
15010         }
15011             
15012         this.fireEvent("load", this, r, options, o);
15013         if(options.callback){
15014             options.callback.call(options.scope || this, r, options, true);
15015         }
15016     },
15017
15018
15019     /**
15020      * Loads data from a passed data block. A Reader which understands the format of the data
15021      * must have been configured in the constructor.
15022      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15023      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15024      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15025      */
15026     loadData : function(o, append){
15027         var r = this.reader.readRecords(o);
15028         this.loadRecords(r, {add: append}, true);
15029     },
15030     
15031      /**
15032      * using 'cn' the nested child reader read the child array into it's child stores.
15033      * @param {Object} rec The record with a 'children array
15034      */
15035     loadDataFromChildren : function(rec)
15036     {
15037         this.loadData(this.reader.toLoadData(rec));
15038     },
15039     
15040
15041     /**
15042      * Gets the number of cached records.
15043      * <p>
15044      * <em>If using paging, this may not be the total size of the dataset. If the data object
15045      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15046      * the data set size</em>
15047      */
15048     getCount : function(){
15049         return this.data.length || 0;
15050     },
15051
15052     /**
15053      * Gets the total number of records in the dataset as returned by the server.
15054      * <p>
15055      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15056      * the dataset size</em>
15057      */
15058     getTotalCount : function(){
15059         return this.totalLength || 0;
15060     },
15061
15062     /**
15063      * Returns the sort state of the Store as an object with two properties:
15064      * <pre><code>
15065  field {String} The name of the field by which the Records are sorted
15066  direction {String} The sort order, "ASC" or "DESC"
15067      * </code></pre>
15068      */
15069     getSortState : function(){
15070         return this.sortInfo;
15071     },
15072
15073     // private
15074     applySort : function(){
15075         if(this.sortInfo && !this.remoteSort){
15076             var s = this.sortInfo, f = s.field;
15077             var st = this.fields.get(f).sortType;
15078             var fn = function(r1, r2){
15079                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15080                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15081             };
15082             this.data.sort(s.direction, fn);
15083             if(this.snapshot && this.snapshot != this.data){
15084                 this.snapshot.sort(s.direction, fn);
15085             }
15086         }
15087     },
15088
15089     /**
15090      * Sets the default sort column and order to be used by the next load operation.
15091      * @param {String} fieldName The name of the field to sort by.
15092      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15093      */
15094     setDefaultSort : function(field, dir){
15095         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15096     },
15097
15098     /**
15099      * Sort the Records.
15100      * If remote sorting is used, the sort is performed on the server, and the cache is
15101      * reloaded. If local sorting is used, the cache is sorted internally.
15102      * @param {String} fieldName The name of the field to sort by.
15103      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15104      */
15105     sort : function(fieldName, dir){
15106         var f = this.fields.get(fieldName);
15107         if(!dir){
15108             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15109             
15110             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15111                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15112             }else{
15113                 dir = f.sortDir;
15114             }
15115         }
15116         this.sortToggle[f.name] = dir;
15117         this.sortInfo = {field: f.name, direction: dir};
15118         if(!this.remoteSort){
15119             this.applySort();
15120             this.fireEvent("datachanged", this);
15121         }else{
15122             this.load(this.lastOptions);
15123         }
15124     },
15125
15126     /**
15127      * Calls the specified function for each of the Records in the cache.
15128      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15129      * Returning <em>false</em> aborts and exits the iteration.
15130      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15131      */
15132     each : function(fn, scope){
15133         this.data.each(fn, scope);
15134     },
15135
15136     /**
15137      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15138      * (e.g., during paging).
15139      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15140      */
15141     getModifiedRecords : function(){
15142         return this.modified;
15143     },
15144
15145     // private
15146     createFilterFn : function(property, value, anyMatch){
15147         if(!value.exec){ // not a regex
15148             value = String(value);
15149             if(value.length == 0){
15150                 return false;
15151             }
15152             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15153         }
15154         return function(r){
15155             return value.test(r.data[property]);
15156         };
15157     },
15158
15159     /**
15160      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15161      * @param {String} property A field on your records
15162      * @param {Number} start The record index to start at (defaults to 0)
15163      * @param {Number} end The last record index to include (defaults to length - 1)
15164      * @return {Number} The sum
15165      */
15166     sum : function(property, start, end){
15167         var rs = this.data.items, v = 0;
15168         start = start || 0;
15169         end = (end || end === 0) ? end : rs.length-1;
15170
15171         for(var i = start; i <= end; i++){
15172             v += (rs[i].data[property] || 0);
15173         }
15174         return v;
15175     },
15176
15177     /**
15178      * Filter the records by a specified property.
15179      * @param {String} field A field on your records
15180      * @param {String/RegExp} value Either a string that the field
15181      * should start with or a RegExp to test against the field
15182      * @param {Boolean} anyMatch True to match any part not just the beginning
15183      */
15184     filter : function(property, value, anyMatch){
15185         var fn = this.createFilterFn(property, value, anyMatch);
15186         return fn ? this.filterBy(fn) : this.clearFilter();
15187     },
15188
15189     /**
15190      * Filter by a function. The specified function will be called with each
15191      * record in this data source. If the function returns true the record is included,
15192      * otherwise it is filtered.
15193      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15194      * @param {Object} scope (optional) The scope of the function (defaults to this)
15195      */
15196     filterBy : function(fn, scope){
15197         this.snapshot = this.snapshot || this.data;
15198         this.data = this.queryBy(fn, scope||this);
15199         this.fireEvent("datachanged", this);
15200     },
15201
15202     /**
15203      * Query the records by a specified property.
15204      * @param {String} field A field on your records
15205      * @param {String/RegExp} value Either a string that the field
15206      * should start with or a RegExp to test against the field
15207      * @param {Boolean} anyMatch True to match any part not just the beginning
15208      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15209      */
15210     query : function(property, value, anyMatch){
15211         var fn = this.createFilterFn(property, value, anyMatch);
15212         return fn ? this.queryBy(fn) : this.data.clone();
15213     },
15214
15215     /**
15216      * Query by a function. The specified function will be called with each
15217      * record in this data source. If the function returns true the record is included
15218      * in the results.
15219      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15220      * @param {Object} scope (optional) The scope of the function (defaults to this)
15221       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15222      **/
15223     queryBy : function(fn, scope){
15224         var data = this.snapshot || this.data;
15225         return data.filterBy(fn, scope||this);
15226     },
15227
15228     /**
15229      * Collects unique values for a particular dataIndex from this store.
15230      * @param {String} dataIndex The property to collect
15231      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15232      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15233      * @return {Array} An array of the unique values
15234      **/
15235     collect : function(dataIndex, allowNull, bypassFilter){
15236         var d = (bypassFilter === true && this.snapshot) ?
15237                 this.snapshot.items : this.data.items;
15238         var v, sv, r = [], l = {};
15239         for(var i = 0, len = d.length; i < len; i++){
15240             v = d[i].data[dataIndex];
15241             sv = String(v);
15242             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15243                 l[sv] = true;
15244                 r[r.length] = v;
15245             }
15246         }
15247         return r;
15248     },
15249
15250     /**
15251      * Revert to a view of the Record cache with no filtering applied.
15252      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15253      */
15254     clearFilter : function(suppressEvent){
15255         if(this.snapshot && this.snapshot != this.data){
15256             this.data = this.snapshot;
15257             delete this.snapshot;
15258             if(suppressEvent !== true){
15259                 this.fireEvent("datachanged", this);
15260             }
15261         }
15262     },
15263
15264     // private
15265     afterEdit : function(record){
15266         if(this.modified.indexOf(record) == -1){
15267             this.modified.push(record);
15268         }
15269         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15270     },
15271     
15272     // private
15273     afterReject : function(record){
15274         this.modified.remove(record);
15275         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15276     },
15277
15278     // private
15279     afterCommit : function(record){
15280         this.modified.remove(record);
15281         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15282     },
15283
15284     /**
15285      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15286      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15287      */
15288     commitChanges : function(){
15289         var m = this.modified.slice(0);
15290         this.modified = [];
15291         for(var i = 0, len = m.length; i < len; i++){
15292             m[i].commit();
15293         }
15294     },
15295
15296     /**
15297      * Cancel outstanding changes on all changed records.
15298      */
15299     rejectChanges : function(){
15300         var m = this.modified.slice(0);
15301         this.modified = [];
15302         for(var i = 0, len = m.length; i < len; i++){
15303             m[i].reject();
15304         }
15305     },
15306
15307     onMetaChange : function(meta, rtype, o){
15308         this.recordType = rtype;
15309         this.fields = rtype.prototype.fields;
15310         delete this.snapshot;
15311         this.sortInfo = meta.sortInfo || this.sortInfo;
15312         this.modified = [];
15313         this.fireEvent('metachange', this, this.reader.meta);
15314     },
15315     
15316     moveIndex : function(data, type)
15317     {
15318         var index = this.indexOf(data);
15319         
15320         var newIndex = index + type;
15321         
15322         this.remove(data);
15323         
15324         this.insert(newIndex, data);
15325         
15326     }
15327 });/*
15328  * Based on:
15329  * Ext JS Library 1.1.1
15330  * Copyright(c) 2006-2007, Ext JS, LLC.
15331  *
15332  * Originally Released Under LGPL - original licence link has changed is not relivant.
15333  *
15334  * Fork - LGPL
15335  * <script type="text/javascript">
15336  */
15337
15338 /**
15339  * @class Roo.data.SimpleStore
15340  * @extends Roo.data.Store
15341  * Small helper class to make creating Stores from Array data easier.
15342  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15343  * @cfg {Array} fields An array of field definition objects, or field name strings.
15344  * @cfg {Object} an existing reader (eg. copied from another store)
15345  * @cfg {Array} data The multi-dimensional array of data
15346  * @constructor
15347  * @param {Object} config
15348  */
15349 Roo.data.SimpleStore = function(config)
15350 {
15351     Roo.data.SimpleStore.superclass.constructor.call(this, {
15352         isLocal : true,
15353         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15354                 id: config.id
15355             },
15356             Roo.data.Record.create(config.fields)
15357         ),
15358         proxy : new Roo.data.MemoryProxy(config.data)
15359     });
15360     this.load();
15361 };
15362 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15363  * Based on:
15364  * Ext JS Library 1.1.1
15365  * Copyright(c) 2006-2007, Ext JS, LLC.
15366  *
15367  * Originally Released Under LGPL - original licence link has changed is not relivant.
15368  *
15369  * Fork - LGPL
15370  * <script type="text/javascript">
15371  */
15372
15373 /**
15374 /**
15375  * @extends Roo.data.Store
15376  * @class Roo.data.JsonStore
15377  * Small helper class to make creating Stores for JSON data easier. <br/>
15378 <pre><code>
15379 var store = new Roo.data.JsonStore({
15380     url: 'get-images.php',
15381     root: 'images',
15382     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15383 });
15384 </code></pre>
15385  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15386  * JsonReader and HttpProxy (unless inline data is provided).</b>
15387  * @cfg {Array} fields An array of field definition objects, or field name strings.
15388  * @constructor
15389  * @param {Object} config
15390  */
15391 Roo.data.JsonStore = function(c){
15392     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15393         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15394         reader: new Roo.data.JsonReader(c, c.fields)
15395     }));
15396 };
15397 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15398  * Based on:
15399  * Ext JS Library 1.1.1
15400  * Copyright(c) 2006-2007, Ext JS, LLC.
15401  *
15402  * Originally Released Under LGPL - original licence link has changed is not relivant.
15403  *
15404  * Fork - LGPL
15405  * <script type="text/javascript">
15406  */
15407
15408  
15409 Roo.data.Field = function(config){
15410     if(typeof config == "string"){
15411         config = {name: config};
15412     }
15413     Roo.apply(this, config);
15414     
15415     if(!this.type){
15416         this.type = "auto";
15417     }
15418     
15419     var st = Roo.data.SortTypes;
15420     // named sortTypes are supported, here we look them up
15421     if(typeof this.sortType == "string"){
15422         this.sortType = st[this.sortType];
15423     }
15424     
15425     // set default sortType for strings and dates
15426     if(!this.sortType){
15427         switch(this.type){
15428             case "string":
15429                 this.sortType = st.asUCString;
15430                 break;
15431             case "date":
15432                 this.sortType = st.asDate;
15433                 break;
15434             default:
15435                 this.sortType = st.none;
15436         }
15437     }
15438
15439     // define once
15440     var stripRe = /[\$,%]/g;
15441
15442     // prebuilt conversion function for this field, instead of
15443     // switching every time we're reading a value
15444     if(!this.convert){
15445         var cv, dateFormat = this.dateFormat;
15446         switch(this.type){
15447             case "":
15448             case "auto":
15449             case undefined:
15450                 cv = function(v){ return v; };
15451                 break;
15452             case "string":
15453                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15454                 break;
15455             case "int":
15456                 cv = function(v){
15457                     return v !== undefined && v !== null && v !== '' ?
15458                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15459                     };
15460                 break;
15461             case "float":
15462                 cv = function(v){
15463                     return v !== undefined && v !== null && v !== '' ?
15464                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15465                     };
15466                 break;
15467             case "bool":
15468             case "boolean":
15469                 cv = function(v){ return v === true || v === "true" || v == 1; };
15470                 break;
15471             case "date":
15472                 cv = function(v){
15473                     if(!v){
15474                         return '';
15475                     }
15476                     if(v instanceof Date){
15477                         return v;
15478                     }
15479                     if(dateFormat){
15480                         if(dateFormat == "timestamp"){
15481                             return new Date(v*1000);
15482                         }
15483                         return Date.parseDate(v, dateFormat);
15484                     }
15485                     var parsed = Date.parse(v);
15486                     return parsed ? new Date(parsed) : null;
15487                 };
15488              break;
15489             
15490         }
15491         this.convert = cv;
15492     }
15493 };
15494
15495 Roo.data.Field.prototype = {
15496     dateFormat: null,
15497     defaultValue: "",
15498     mapping: null,
15499     sortType : null,
15500     sortDir : "ASC"
15501 };/*
15502  * Based on:
15503  * Ext JS Library 1.1.1
15504  * Copyright(c) 2006-2007, Ext JS, LLC.
15505  *
15506  * Originally Released Under LGPL - original licence link has changed is not relivant.
15507  *
15508  * Fork - LGPL
15509  * <script type="text/javascript">
15510  */
15511  
15512 // Base class for reading structured data from a data source.  This class is intended to be
15513 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15514
15515 /**
15516  * @class Roo.data.DataReader
15517  * Base class for reading structured data from a data source.  This class is intended to be
15518  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15519  */
15520
15521 Roo.data.DataReader = function(meta, recordType){
15522     
15523     this.meta = meta;
15524     
15525     this.recordType = recordType instanceof Array ? 
15526         Roo.data.Record.create(recordType) : recordType;
15527 };
15528
15529 Roo.data.DataReader.prototype = {
15530     
15531     
15532     readerType : 'Data',
15533      /**
15534      * Create an empty record
15535      * @param {Object} data (optional) - overlay some values
15536      * @return {Roo.data.Record} record created.
15537      */
15538     newRow :  function(d) {
15539         var da =  {};
15540         this.recordType.prototype.fields.each(function(c) {
15541             switch( c.type) {
15542                 case 'int' : da[c.name] = 0; break;
15543                 case 'date' : da[c.name] = new Date(); break;
15544                 case 'float' : da[c.name] = 0.0; break;
15545                 case 'boolean' : da[c.name] = false; break;
15546                 default : da[c.name] = ""; break;
15547             }
15548             
15549         });
15550         return new this.recordType(Roo.apply(da, d));
15551     }
15552     
15553     
15554 };/*
15555  * Based on:
15556  * Ext JS Library 1.1.1
15557  * Copyright(c) 2006-2007, Ext JS, LLC.
15558  *
15559  * Originally Released Under LGPL - original licence link has changed is not relivant.
15560  *
15561  * Fork - LGPL
15562  * <script type="text/javascript">
15563  */
15564
15565 /**
15566  * @class Roo.data.DataProxy
15567  * @extends Roo.data.Observable
15568  * This class is an abstract base class for implementations which provide retrieval of
15569  * unformatted data objects.<br>
15570  * <p>
15571  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15572  * (of the appropriate type which knows how to parse the data object) to provide a block of
15573  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15574  * <p>
15575  * Custom implementations must implement the load method as described in
15576  * {@link Roo.data.HttpProxy#load}.
15577  */
15578 Roo.data.DataProxy = function(){
15579     this.addEvents({
15580         /**
15581          * @event beforeload
15582          * Fires before a network request is made to retrieve a data object.
15583          * @param {Object} This DataProxy object.
15584          * @param {Object} params The params parameter to the load function.
15585          */
15586         beforeload : true,
15587         /**
15588          * @event load
15589          * Fires before the load method's callback is called.
15590          * @param {Object} This DataProxy object.
15591          * @param {Object} o The data object.
15592          * @param {Object} arg The callback argument object passed to the load function.
15593          */
15594         load : true,
15595         /**
15596          * @event loadexception
15597          * Fires if an Exception occurs during data retrieval.
15598          * @param {Object} This DataProxy object.
15599          * @param {Object} o The data object.
15600          * @param {Object} arg The callback argument object passed to the load function.
15601          * @param {Object} e The Exception.
15602          */
15603         loadexception : true
15604     });
15605     Roo.data.DataProxy.superclass.constructor.call(this);
15606 };
15607
15608 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15609
15610     /**
15611      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15612      */
15613 /*
15614  * Based on:
15615  * Ext JS Library 1.1.1
15616  * Copyright(c) 2006-2007, Ext JS, LLC.
15617  *
15618  * Originally Released Under LGPL - original licence link has changed is not relivant.
15619  *
15620  * Fork - LGPL
15621  * <script type="text/javascript">
15622  */
15623 /**
15624  * @class Roo.data.MemoryProxy
15625  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15626  * to the Reader when its load method is called.
15627  * @constructor
15628  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15629  */
15630 Roo.data.MemoryProxy = function(data){
15631     if (data.data) {
15632         data = data.data;
15633     }
15634     Roo.data.MemoryProxy.superclass.constructor.call(this);
15635     this.data = data;
15636 };
15637
15638 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15639     
15640     /**
15641      * Load data from the requested source (in this case an in-memory
15642      * data object passed to the constructor), read the data object into
15643      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15644      * process that block using the passed callback.
15645      * @param {Object} params This parameter is not used by the MemoryProxy class.
15646      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15647      * object into a block of Roo.data.Records.
15648      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15649      * The function must be passed <ul>
15650      * <li>The Record block object</li>
15651      * <li>The "arg" argument from the load function</li>
15652      * <li>A boolean success indicator</li>
15653      * </ul>
15654      * @param {Object} scope The scope in which to call the callback
15655      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15656      */
15657     load : function(params, reader, callback, scope, arg){
15658         params = params || {};
15659         var result;
15660         try {
15661             result = reader.readRecords(params.data ? params.data :this.data);
15662         }catch(e){
15663             this.fireEvent("loadexception", this, arg, null, e);
15664             callback.call(scope, null, arg, false);
15665             return;
15666         }
15667         callback.call(scope, result, arg, true);
15668     },
15669     
15670     // private
15671     update : function(params, records){
15672         
15673     }
15674 });/*
15675  * Based on:
15676  * Ext JS Library 1.1.1
15677  * Copyright(c) 2006-2007, Ext JS, LLC.
15678  *
15679  * Originally Released Under LGPL - original licence link has changed is not relivant.
15680  *
15681  * Fork - LGPL
15682  * <script type="text/javascript">
15683  */
15684 /**
15685  * @class Roo.data.HttpProxy
15686  * @extends Roo.data.DataProxy
15687  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15688  * configured to reference a certain URL.<br><br>
15689  * <p>
15690  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15691  * from which the running page was served.<br><br>
15692  * <p>
15693  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15694  * <p>
15695  * Be aware that to enable the browser to parse an XML document, the server must set
15696  * the Content-Type header in the HTTP response to "text/xml".
15697  * @constructor
15698  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15699  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15700  * will be used to make the request.
15701  */
15702 Roo.data.HttpProxy = function(conn){
15703     Roo.data.HttpProxy.superclass.constructor.call(this);
15704     // is conn a conn config or a real conn?
15705     this.conn = conn;
15706     this.useAjax = !conn || !conn.events;
15707   
15708 };
15709
15710 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15711     // thse are take from connection...
15712     
15713     /**
15714      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15715      */
15716     /**
15717      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15718      * extra parameters to each request made by this object. (defaults to undefined)
15719      */
15720     /**
15721      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15722      *  to each request made by this object. (defaults to undefined)
15723      */
15724     /**
15725      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
15726      */
15727     /**
15728      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15729      */
15730      /**
15731      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15732      * @type Boolean
15733      */
15734   
15735
15736     /**
15737      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15738      * @type Boolean
15739      */
15740     /**
15741      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15742      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15743      * a finer-grained basis than the DataProxy events.
15744      */
15745     getConnection : function(){
15746         return this.useAjax ? Roo.Ajax : this.conn;
15747     },
15748
15749     /**
15750      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15751      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15752      * process that block using the passed callback.
15753      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15754      * for the request to the remote server.
15755      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15756      * object into a block of Roo.data.Records.
15757      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15758      * The function must be passed <ul>
15759      * <li>The Record block object</li>
15760      * <li>The "arg" argument from the load function</li>
15761      * <li>A boolean success indicator</li>
15762      * </ul>
15763      * @param {Object} scope The scope in which to call the callback
15764      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15765      */
15766     load : function(params, reader, callback, scope, arg){
15767         if(this.fireEvent("beforeload", this, params) !== false){
15768             var  o = {
15769                 params : params || {},
15770                 request: {
15771                     callback : callback,
15772                     scope : scope,
15773                     arg : arg
15774                 },
15775                 reader: reader,
15776                 callback : this.loadResponse,
15777                 scope: this
15778             };
15779             if(this.useAjax){
15780                 Roo.applyIf(o, this.conn);
15781                 if(this.activeRequest){
15782                     Roo.Ajax.abort(this.activeRequest);
15783                 }
15784                 this.activeRequest = Roo.Ajax.request(o);
15785             }else{
15786                 this.conn.request(o);
15787             }
15788         }else{
15789             callback.call(scope||this, null, arg, false);
15790         }
15791     },
15792
15793     // private
15794     loadResponse : function(o, success, response){
15795         delete this.activeRequest;
15796         if(!success){
15797             this.fireEvent("loadexception", this, o, response);
15798             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15799             return;
15800         }
15801         var result;
15802         try {
15803             result = o.reader.read(response);
15804         }catch(e){
15805             this.fireEvent("loadexception", this, o, response, e);
15806             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15807             return;
15808         }
15809         
15810         this.fireEvent("load", this, o, o.request.arg);
15811         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15812     },
15813
15814     // private
15815     update : function(dataSet){
15816
15817     },
15818
15819     // private
15820     updateResponse : function(dataSet){
15821
15822     }
15823 });/*
15824  * Based on:
15825  * Ext JS Library 1.1.1
15826  * Copyright(c) 2006-2007, Ext JS, LLC.
15827  *
15828  * Originally Released Under LGPL - original licence link has changed is not relivant.
15829  *
15830  * Fork - LGPL
15831  * <script type="text/javascript">
15832  */
15833
15834 /**
15835  * @class Roo.data.ScriptTagProxy
15836  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15837  * other than the originating domain of the running page.<br><br>
15838  * <p>
15839  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
15840  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15841  * <p>
15842  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15843  * source code that is used as the source inside a &lt;script> tag.<br><br>
15844  * <p>
15845  * In order for the browser to process the returned data, the server must wrap the data object
15846  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15847  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15848  * depending on whether the callback name was passed:
15849  * <p>
15850  * <pre><code>
15851 boolean scriptTag = false;
15852 String cb = request.getParameter("callback");
15853 if (cb != null) {
15854     scriptTag = true;
15855     response.setContentType("text/javascript");
15856 } else {
15857     response.setContentType("application/x-json");
15858 }
15859 Writer out = response.getWriter();
15860 if (scriptTag) {
15861     out.write(cb + "(");
15862 }
15863 out.print(dataBlock.toJsonString());
15864 if (scriptTag) {
15865     out.write(");");
15866 }
15867 </pre></code>
15868  *
15869  * @constructor
15870  * @param {Object} config A configuration object.
15871  */
15872 Roo.data.ScriptTagProxy = function(config){
15873     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15874     Roo.apply(this, config);
15875     this.head = document.getElementsByTagName("head")[0];
15876 };
15877
15878 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15879
15880 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15881     /**
15882      * @cfg {String} url The URL from which to request the data object.
15883      */
15884     /**
15885      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15886      */
15887     timeout : 30000,
15888     /**
15889      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15890      * the server the name of the callback function set up by the load call to process the returned data object.
15891      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15892      * javascript output which calls this named function passing the data object as its only parameter.
15893      */
15894     callbackParam : "callback",
15895     /**
15896      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15897      * name to the request.
15898      */
15899     nocache : true,
15900
15901     /**
15902      * Load data from the configured URL, read the data object into
15903      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15904      * process that block using the passed callback.
15905      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15906      * for the request to the remote server.
15907      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15908      * object into a block of Roo.data.Records.
15909      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15910      * The function must be passed <ul>
15911      * <li>The Record block object</li>
15912      * <li>The "arg" argument from the load function</li>
15913      * <li>A boolean success indicator</li>
15914      * </ul>
15915      * @param {Object} scope The scope in which to call the callback
15916      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15917      */
15918     load : function(params, reader, callback, scope, arg){
15919         if(this.fireEvent("beforeload", this, params) !== false){
15920
15921             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15922
15923             var url = this.url;
15924             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15925             if(this.nocache){
15926                 url += "&_dc=" + (new Date().getTime());
15927             }
15928             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15929             var trans = {
15930                 id : transId,
15931                 cb : "stcCallback"+transId,
15932                 scriptId : "stcScript"+transId,
15933                 params : params,
15934                 arg : arg,
15935                 url : url,
15936                 callback : callback,
15937                 scope : scope,
15938                 reader : reader
15939             };
15940             var conn = this;
15941
15942             window[trans.cb] = function(o){
15943                 conn.handleResponse(o, trans);
15944             };
15945
15946             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15947
15948             if(this.autoAbort !== false){
15949                 this.abort();
15950             }
15951
15952             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15953
15954             var script = document.createElement("script");
15955             script.setAttribute("src", url);
15956             script.setAttribute("type", "text/javascript");
15957             script.setAttribute("id", trans.scriptId);
15958             this.head.appendChild(script);
15959
15960             this.trans = trans;
15961         }else{
15962             callback.call(scope||this, null, arg, false);
15963         }
15964     },
15965
15966     // private
15967     isLoading : function(){
15968         return this.trans ? true : false;
15969     },
15970
15971     /**
15972      * Abort the current server request.
15973      */
15974     abort : function(){
15975         if(this.isLoading()){
15976             this.destroyTrans(this.trans);
15977         }
15978     },
15979
15980     // private
15981     destroyTrans : function(trans, isLoaded){
15982         this.head.removeChild(document.getElementById(trans.scriptId));
15983         clearTimeout(trans.timeoutId);
15984         if(isLoaded){
15985             window[trans.cb] = undefined;
15986             try{
15987                 delete window[trans.cb];
15988             }catch(e){}
15989         }else{
15990             // if hasn't been loaded, wait for load to remove it to prevent script error
15991             window[trans.cb] = function(){
15992                 window[trans.cb] = undefined;
15993                 try{
15994                     delete window[trans.cb];
15995                 }catch(e){}
15996             };
15997         }
15998     },
15999
16000     // private
16001     handleResponse : function(o, trans){
16002         this.trans = false;
16003         this.destroyTrans(trans, true);
16004         var result;
16005         try {
16006             result = trans.reader.readRecords(o);
16007         }catch(e){
16008             this.fireEvent("loadexception", this, o, trans.arg, e);
16009             trans.callback.call(trans.scope||window, null, trans.arg, false);
16010             return;
16011         }
16012         this.fireEvent("load", this, o, trans.arg);
16013         trans.callback.call(trans.scope||window, result, trans.arg, true);
16014     },
16015
16016     // private
16017     handleFailure : function(trans){
16018         this.trans = false;
16019         this.destroyTrans(trans, false);
16020         this.fireEvent("loadexception", this, null, trans.arg);
16021         trans.callback.call(trans.scope||window, null, trans.arg, false);
16022     }
16023 });/*
16024  * Based on:
16025  * Ext JS Library 1.1.1
16026  * Copyright(c) 2006-2007, Ext JS, LLC.
16027  *
16028  * Originally Released Under LGPL - original licence link has changed is not relivant.
16029  *
16030  * Fork - LGPL
16031  * <script type="text/javascript">
16032  */
16033
16034 /**
16035  * @class Roo.data.JsonReader
16036  * @extends Roo.data.DataReader
16037  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16038  * based on mappings in a provided Roo.data.Record constructor.
16039  * 
16040  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16041  * in the reply previously. 
16042  * 
16043  * <p>
16044  * Example code:
16045  * <pre><code>
16046 var RecordDef = Roo.data.Record.create([
16047     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16048     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16049 ]);
16050 var myReader = new Roo.data.JsonReader({
16051     totalProperty: "results",    // The property which contains the total dataset size (optional)
16052     root: "rows",                // The property which contains an Array of row objects
16053     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16054 }, RecordDef);
16055 </code></pre>
16056  * <p>
16057  * This would consume a JSON file like this:
16058  * <pre><code>
16059 { 'results': 2, 'rows': [
16060     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16061     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16062 }
16063 </code></pre>
16064  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16065  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16066  * paged from the remote server.
16067  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16068  * @cfg {String} root name of the property which contains the Array of row objects.
16069  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16070  * @cfg {Array} fields Array of field definition objects
16071  * @constructor
16072  * Create a new JsonReader
16073  * @param {Object} meta Metadata configuration options
16074  * @param {Object} recordType Either an Array of field definition objects,
16075  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16076  */
16077 Roo.data.JsonReader = function(meta, recordType){
16078     
16079     meta = meta || {};
16080     // set some defaults:
16081     Roo.applyIf(meta, {
16082         totalProperty: 'total',
16083         successProperty : 'success',
16084         root : 'data',
16085         id : 'id'
16086     });
16087     
16088     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16089 };
16090 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16091     
16092     readerType : 'Json',
16093     
16094     /**
16095      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16096      * Used by Store query builder to append _requestMeta to params.
16097      * 
16098      */
16099     metaFromRemote : false,
16100     /**
16101      * This method is only used by a DataProxy which has retrieved data from a remote server.
16102      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16103      * @return {Object} data A data block which is used by an Roo.data.Store object as
16104      * a cache of Roo.data.Records.
16105      */
16106     read : function(response){
16107         var json = response.responseText;
16108        
16109         var o = /* eval:var:o */ eval("("+json+")");
16110         if(!o) {
16111             throw {message: "JsonReader.read: Json object not found"};
16112         }
16113         
16114         if(o.metaData){
16115             
16116             delete this.ef;
16117             this.metaFromRemote = true;
16118             this.meta = o.metaData;
16119             this.recordType = Roo.data.Record.create(o.metaData.fields);
16120             this.onMetaChange(this.meta, this.recordType, o);
16121         }
16122         return this.readRecords(o);
16123     },
16124
16125     // private function a store will implement
16126     onMetaChange : function(meta, recordType, o){
16127
16128     },
16129
16130     /**
16131          * @ignore
16132          */
16133     simpleAccess: function(obj, subsc) {
16134         return obj[subsc];
16135     },
16136
16137         /**
16138          * @ignore
16139          */
16140     getJsonAccessor: function(){
16141         var re = /[\[\.]/;
16142         return function(expr) {
16143             try {
16144                 return(re.test(expr))
16145                     ? new Function("obj", "return obj." + expr)
16146                     : function(obj){
16147                         return obj[expr];
16148                     };
16149             } catch(e){}
16150             return Roo.emptyFn;
16151         };
16152     }(),
16153
16154     /**
16155      * Create a data block containing Roo.data.Records from an XML document.
16156      * @param {Object} o An object which contains an Array of row objects in the property specified
16157      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16158      * which contains the total size of the dataset.
16159      * @return {Object} data A data block which is used by an Roo.data.Store object as
16160      * a cache of Roo.data.Records.
16161      */
16162     readRecords : function(o){
16163         /**
16164          * After any data loads, the raw JSON data is available for further custom processing.
16165          * @type Object
16166          */
16167         this.o = o;
16168         var s = this.meta, Record = this.recordType,
16169             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16170
16171 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16172         if (!this.ef) {
16173             if(s.totalProperty) {
16174                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16175                 }
16176                 if(s.successProperty) {
16177                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16178                 }
16179                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16180                 if (s.id) {
16181                         var g = this.getJsonAccessor(s.id);
16182                         this.getId = function(rec) {
16183                                 var r = g(rec);  
16184                                 return (r === undefined || r === "") ? null : r;
16185                         };
16186                 } else {
16187                         this.getId = function(){return null;};
16188                 }
16189             this.ef = [];
16190             for(var jj = 0; jj < fl; jj++){
16191                 f = fi[jj];
16192                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16193                 this.ef[jj] = this.getJsonAccessor(map);
16194             }
16195         }
16196
16197         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16198         if(s.totalProperty){
16199             var vt = parseInt(this.getTotal(o), 10);
16200             if(!isNaN(vt)){
16201                 totalRecords = vt;
16202             }
16203         }
16204         if(s.successProperty){
16205             var vs = this.getSuccess(o);
16206             if(vs === false || vs === 'false'){
16207                 success = false;
16208             }
16209         }
16210         var records = [];
16211         for(var i = 0; i < c; i++){
16212                 var n = root[i];
16213             var values = {};
16214             var id = this.getId(n);
16215             for(var j = 0; j < fl; j++){
16216                 f = fi[j];
16217             var v = this.ef[j](n);
16218             if (!f.convert) {
16219                 Roo.log('missing convert for ' + f.name);
16220                 Roo.log(f);
16221                 continue;
16222             }
16223             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16224             }
16225             var record = new Record(values, id);
16226             record.json = n;
16227             records[i] = record;
16228         }
16229         return {
16230             raw : o,
16231             success : success,
16232             records : records,
16233             totalRecords : totalRecords
16234         };
16235     },
16236     // used when loading children.. @see loadDataFromChildren
16237     toLoadData: function(rec)
16238     {
16239         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16240         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16241         return { data : data, total : data.length };
16242         
16243     }
16244 });/*
16245  * Based on:
16246  * Ext JS Library 1.1.1
16247  * Copyright(c) 2006-2007, Ext JS, LLC.
16248  *
16249  * Originally Released Under LGPL - original licence link has changed is not relivant.
16250  *
16251  * Fork - LGPL
16252  * <script type="text/javascript">
16253  */
16254
16255 /**
16256  * @class Roo.data.ArrayReader
16257  * @extends Roo.data.DataReader
16258  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16259  * Each element of that Array represents a row of data fields. The
16260  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16261  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16262  * <p>
16263  * Example code:.
16264  * <pre><code>
16265 var RecordDef = Roo.data.Record.create([
16266     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16267     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16268 ]);
16269 var myReader = new Roo.data.ArrayReader({
16270     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16271 }, RecordDef);
16272 </code></pre>
16273  * <p>
16274  * This would consume an Array like this:
16275  * <pre><code>
16276 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16277   </code></pre>
16278  
16279  * @constructor
16280  * Create a new JsonReader
16281  * @param {Object} meta Metadata configuration options.
16282  * @param {Object|Array} recordType Either an Array of field definition objects
16283  * 
16284  * @cfg {Array} fields Array of field definition objects
16285  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16286  * as specified to {@link Roo.data.Record#create},
16287  * or an {@link Roo.data.Record} object
16288  *
16289  * 
16290  * created using {@link Roo.data.Record#create}.
16291  */
16292 Roo.data.ArrayReader = function(meta, recordType)
16293 {    
16294     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16295 };
16296
16297 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16298     
16299       /**
16300      * Create a data block containing Roo.data.Records from an XML document.
16301      * @param {Object} o An Array of row objects which represents the dataset.
16302      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16303      * a cache of Roo.data.Records.
16304      */
16305     readRecords : function(o)
16306     {
16307         var sid = this.meta ? this.meta.id : null;
16308         var recordType = this.recordType, fields = recordType.prototype.fields;
16309         var records = [];
16310         var root = o;
16311         for(var i = 0; i < root.length; i++){
16312             var n = root[i];
16313             var values = {};
16314             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16315             for(var j = 0, jlen = fields.length; j < jlen; j++){
16316                 var f = fields.items[j];
16317                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16318                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16319                 v = f.convert(v);
16320                 values[f.name] = v;
16321             }
16322             var record = new recordType(values, id);
16323             record.json = n;
16324             records[records.length] = record;
16325         }
16326         return {
16327             records : records,
16328             totalRecords : records.length
16329         };
16330     },
16331     // used when loading children.. @see loadDataFromChildren
16332     toLoadData: function(rec)
16333     {
16334         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16335         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16336         
16337     }
16338     
16339     
16340 });/*
16341  * - LGPL
16342  * * 
16343  */
16344
16345 /**
16346  * @class Roo.bootstrap.ComboBox
16347  * @extends Roo.bootstrap.TriggerField
16348  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16349  * @cfg {Boolean} append (true|false) default false
16350  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16351  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16352  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16353  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16354  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16355  * @cfg {Boolean} animate default true
16356  * @cfg {Boolean} emptyResultText only for touch device
16357  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16358  * @cfg {String} emptyTitle default ''
16359  * @cfg {Number} width fixed with? experimental
16360  * @constructor
16361  * Create a new ComboBox.
16362  * @param {Object} config Configuration options
16363  */
16364 Roo.bootstrap.ComboBox = function(config){
16365     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16366     this.addEvents({
16367         /**
16368          * @event expand
16369          * Fires when the dropdown list is expanded
16370         * @param {Roo.bootstrap.ComboBox} combo This combo box
16371         */
16372         'expand' : true,
16373         /**
16374          * @event collapse
16375          * Fires when the dropdown list is collapsed
16376         * @param {Roo.bootstrap.ComboBox} combo This combo box
16377         */
16378         'collapse' : true,
16379         /**
16380          * @event beforeselect
16381          * Fires before a list item is selected. Return false to cancel the selection.
16382         * @param {Roo.bootstrap.ComboBox} combo This combo box
16383         * @param {Roo.data.Record} record The data record returned from the underlying store
16384         * @param {Number} index The index of the selected item in the dropdown list
16385         */
16386         'beforeselect' : true,
16387         /**
16388          * @event select
16389          * Fires when a list item is selected
16390         * @param {Roo.bootstrap.ComboBox} combo This combo box
16391         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16392         * @param {Number} index The index of the selected item in the dropdown list
16393         */
16394         'select' : true,
16395         /**
16396          * @event beforequery
16397          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16398          * The event object passed has these properties:
16399         * @param {Roo.bootstrap.ComboBox} combo This combo box
16400         * @param {String} query The query
16401         * @param {Boolean} forceAll true to force "all" query
16402         * @param {Boolean} cancel true to cancel the query
16403         * @param {Object} e The query event object
16404         */
16405         'beforequery': true,
16406          /**
16407          * @event add
16408          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16409         * @param {Roo.bootstrap.ComboBox} combo This combo box
16410         */
16411         'add' : true,
16412         /**
16413          * @event edit
16414          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16415         * @param {Roo.bootstrap.ComboBox} combo This combo box
16416         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16417         */
16418         'edit' : true,
16419         /**
16420          * @event remove
16421          * Fires when the remove value from the combobox array
16422         * @param {Roo.bootstrap.ComboBox} combo This combo box
16423         */
16424         'remove' : true,
16425         /**
16426          * @event afterremove
16427          * Fires when the remove value from the combobox array
16428         * @param {Roo.bootstrap.ComboBox} combo This combo box
16429         */
16430         'afterremove' : true,
16431         /**
16432          * @event specialfilter
16433          * Fires when specialfilter
16434             * @param {Roo.bootstrap.ComboBox} combo This combo box
16435             */
16436         'specialfilter' : true,
16437         /**
16438          * @event tick
16439          * Fires when tick the element
16440             * @param {Roo.bootstrap.ComboBox} combo This combo box
16441             */
16442         'tick' : true,
16443         /**
16444          * @event touchviewdisplay
16445          * Fires when touch view require special display (default is using displayField)
16446             * @param {Roo.bootstrap.ComboBox} combo This combo box
16447             * @param {Object} cfg set html .
16448             */
16449         'touchviewdisplay' : true
16450         
16451     });
16452     
16453     this.item = [];
16454     this.tickItems = [];
16455     
16456     this.selectedIndex = -1;
16457     if(this.mode == 'local'){
16458         if(config.queryDelay === undefined){
16459             this.queryDelay = 10;
16460         }
16461         if(config.minChars === undefined){
16462             this.minChars = 0;
16463         }
16464     }
16465 };
16466
16467 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16468      
16469     /**
16470      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16471      * rendering into an Roo.Editor, defaults to false)
16472      */
16473     /**
16474      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16475      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16476      */
16477     /**
16478      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16479      */
16480     /**
16481      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16482      * the dropdown list (defaults to undefined, with no header element)
16483      */
16484
16485      /**
16486      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16487      */
16488      
16489      /**
16490      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16491      */
16492     listWidth: undefined,
16493     /**
16494      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16495      * mode = 'remote' or 'text' if mode = 'local')
16496      */
16497     displayField: undefined,
16498     
16499     /**
16500      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16501      * mode = 'remote' or 'value' if mode = 'local'). 
16502      * Note: use of a valueField requires the user make a selection
16503      * in order for a value to be mapped.
16504      */
16505     valueField: undefined,
16506     /**
16507      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16508      */
16509     modalTitle : '',
16510     
16511     /**
16512      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16513      * field's data value (defaults to the underlying DOM element's name)
16514      */
16515     hiddenName: undefined,
16516     /**
16517      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16518      */
16519     listClass: '',
16520     /**
16521      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16522      */
16523     selectedClass: 'active',
16524     
16525     /**
16526      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16527      */
16528     shadow:'sides',
16529     /**
16530      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16531      * anchor positions (defaults to 'tl-bl')
16532      */
16533     listAlign: 'tl-bl?',
16534     /**
16535      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16536      */
16537     maxHeight: 300,
16538     /**
16539      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16540      * query specified by the allQuery config option (defaults to 'query')
16541      */
16542     triggerAction: 'query',
16543     /**
16544      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16545      * (defaults to 4, does not apply if editable = false)
16546      */
16547     minChars : 4,
16548     /**
16549      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16550      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16551      */
16552     typeAhead: false,
16553     /**
16554      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16555      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16556      */
16557     queryDelay: 500,
16558     /**
16559      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16560      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16561      */
16562     pageSize: 0,
16563     /**
16564      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16565      * when editable = true (defaults to false)
16566      */
16567     selectOnFocus:false,
16568     /**
16569      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16570      */
16571     queryParam: 'query',
16572     /**
16573      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16574      * when mode = 'remote' (defaults to 'Loading...')
16575      */
16576     loadingText: 'Loading...',
16577     /**
16578      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16579      */
16580     resizable: false,
16581     /**
16582      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16583      */
16584     handleHeight : 8,
16585     /**
16586      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16587      * traditional select (defaults to true)
16588      */
16589     editable: true,
16590     /**
16591      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16592      */
16593     allQuery: '',
16594     /**
16595      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16596      */
16597     mode: 'remote',
16598     /**
16599      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16600      * listWidth has a higher value)
16601      */
16602     minListWidth : 70,
16603     /**
16604      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16605      * allow the user to set arbitrary text into the field (defaults to false)
16606      */
16607     forceSelection:false,
16608     /**
16609      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16610      * if typeAhead = true (defaults to 250)
16611      */
16612     typeAheadDelay : 250,
16613     /**
16614      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16615      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16616      */
16617     valueNotFoundText : undefined,
16618     /**
16619      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16620      */
16621     blockFocus : false,
16622     
16623     /**
16624      * @cfg {Boolean} disableClear Disable showing of clear button.
16625      */
16626     disableClear : false,
16627     /**
16628      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16629      */
16630     alwaysQuery : false,
16631     
16632     /**
16633      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16634      */
16635     multiple : false,
16636     
16637     /**
16638      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16639      */
16640     invalidClass : "has-warning",
16641     
16642     /**
16643      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16644      */
16645     validClass : "has-success",
16646     
16647     /**
16648      * @cfg {Boolean} specialFilter (true|false) special filter default false
16649      */
16650     specialFilter : false,
16651     
16652     /**
16653      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16654      */
16655     mobileTouchView : true,
16656     
16657     /**
16658      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16659      */
16660     useNativeIOS : false,
16661     
16662     /**
16663      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16664      */
16665     mobile_restrict_height : false,
16666     
16667     ios_options : false,
16668     
16669     //private
16670     addicon : false,
16671     editicon: false,
16672     
16673     page: 0,
16674     hasQuery: false,
16675     append: false,
16676     loadNext: false,
16677     autoFocus : true,
16678     tickable : false,
16679     btnPosition : 'right',
16680     triggerList : true,
16681     showToggleBtn : true,
16682     animate : true,
16683     emptyResultText: 'Empty',
16684     triggerText : 'Select',
16685     emptyTitle : '',
16686     width : false,
16687     
16688     // element that contains real text value.. (when hidden is used..)
16689     
16690     getAutoCreate : function()
16691     {   
16692         var cfg = false;
16693         //render
16694         /*
16695          * Render classic select for iso
16696          */
16697         
16698         if(Roo.isIOS && this.useNativeIOS){
16699             cfg = this.getAutoCreateNativeIOS();
16700             return cfg;
16701         }
16702         
16703         /*
16704          * Touch Devices
16705          */
16706         
16707         if(Roo.isTouch && this.mobileTouchView){
16708             cfg = this.getAutoCreateTouchView();
16709             return cfg;;
16710         }
16711         
16712         /*
16713          *  Normal ComboBox
16714          */
16715         if(!this.tickable){
16716             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16717             return cfg;
16718         }
16719         
16720         /*
16721          *  ComboBox with tickable selections
16722          */
16723              
16724         var align = this.labelAlign || this.parentLabelAlign();
16725         
16726         cfg = {
16727             cls : 'form-group roo-combobox-tickable' //input-group
16728         };
16729         
16730         var btn_text_select = '';
16731         var btn_text_done = '';
16732         var btn_text_cancel = '';
16733         
16734         if (this.btn_text_show) {
16735             btn_text_select = 'Select';
16736             btn_text_done = 'Done';
16737             btn_text_cancel = 'Cancel'; 
16738         }
16739         
16740         var buttons = {
16741             tag : 'div',
16742             cls : 'tickable-buttons',
16743             cn : [
16744                 {
16745                     tag : 'button',
16746                     type : 'button',
16747                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16748                     //html : this.triggerText
16749                     html: btn_text_select
16750                 },
16751                 {
16752                     tag : 'button',
16753                     type : 'button',
16754                     name : 'ok',
16755                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16756                     //html : 'Done'
16757                     html: btn_text_done
16758                 },
16759                 {
16760                     tag : 'button',
16761                     type : 'button',
16762                     name : 'cancel',
16763                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16764                     //html : 'Cancel'
16765                     html: btn_text_cancel
16766                 }
16767             ]
16768         };
16769         
16770         if(this.editable){
16771             buttons.cn.unshift({
16772                 tag: 'input',
16773                 cls: 'roo-select2-search-field-input'
16774             });
16775         }
16776         
16777         var _this = this;
16778         
16779         Roo.each(buttons.cn, function(c){
16780             if (_this.size) {
16781                 c.cls += ' btn-' + _this.size;
16782             }
16783
16784             if (_this.disabled) {
16785                 c.disabled = true;
16786             }
16787         });
16788         
16789         var box = {
16790             tag: 'div',
16791             style : 'display: contents',
16792             cn: [
16793                 {
16794                     tag: 'input',
16795                     type : 'hidden',
16796                     cls: 'form-hidden-field'
16797                 },
16798                 {
16799                     tag: 'ul',
16800                     cls: 'roo-select2-choices',
16801                     cn:[
16802                         {
16803                             tag: 'li',
16804                             cls: 'roo-select2-search-field',
16805                             cn: [
16806                                 buttons
16807                             ]
16808                         }
16809                     ]
16810                 }
16811             ]
16812         };
16813         
16814         var combobox = {
16815             cls: 'roo-select2-container input-group roo-select2-container-multi',
16816             cn: [
16817                 
16818                 box
16819 //                {
16820 //                    tag: 'ul',
16821 //                    cls: 'typeahead typeahead-long dropdown-menu',
16822 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16823 //                }
16824             ]
16825         };
16826         
16827         if(this.hasFeedback && !this.allowBlank){
16828             
16829             var feedback = {
16830                 tag: 'span',
16831                 cls: 'glyphicon form-control-feedback'
16832             };
16833
16834             combobox.cn.push(feedback);
16835         }
16836         
16837         
16838         
16839         var indicator = {
16840             tag : 'i',
16841             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16842             tooltip : 'This field is required'
16843         };
16844         if (Roo.bootstrap.version == 4) {
16845             indicator = {
16846                 tag : 'i',
16847                 style : 'display:none'
16848             };
16849         }
16850         if (align ==='left' && this.fieldLabel.length) {
16851             
16852             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16853             
16854             cfg.cn = [
16855                 indicator,
16856                 {
16857                     tag: 'label',
16858                     'for' :  id,
16859                     cls : 'control-label col-form-label',
16860                     html : this.fieldLabel
16861
16862                 },
16863                 {
16864                     cls : "", 
16865                     cn: [
16866                         combobox
16867                     ]
16868                 }
16869
16870             ];
16871             
16872             var labelCfg = cfg.cn[1];
16873             var contentCfg = cfg.cn[2];
16874             
16875
16876             if(this.indicatorpos == 'right'){
16877                 
16878                 cfg.cn = [
16879                     {
16880                         tag: 'label',
16881                         'for' :  id,
16882                         cls : 'control-label col-form-label',
16883                         cn : [
16884                             {
16885                                 tag : 'span',
16886                                 html : this.fieldLabel
16887                             },
16888                             indicator
16889                         ]
16890                     },
16891                     {
16892                         cls : "",
16893                         cn: [
16894                             combobox
16895                         ]
16896                     }
16897
16898                 ];
16899                 
16900                 
16901                 
16902                 labelCfg = cfg.cn[0];
16903                 contentCfg = cfg.cn[1];
16904             
16905             }
16906             
16907             if(this.labelWidth > 12){
16908                 labelCfg.style = "width: " + this.labelWidth + 'px';
16909             }
16910             if(this.width * 1 > 0){
16911                 contentCfg.style = "width: " + this.width + 'px';
16912             }
16913             if(this.labelWidth < 13 && this.labelmd == 0){
16914                 this.labelmd = this.labelWidth;
16915             }
16916             
16917             if(this.labellg > 0){
16918                 labelCfg.cls += ' col-lg-' + this.labellg;
16919                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16920             }
16921             
16922             if(this.labelmd > 0){
16923                 labelCfg.cls += ' col-md-' + this.labelmd;
16924                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16925             }
16926             
16927             if(this.labelsm > 0){
16928                 labelCfg.cls += ' col-sm-' + this.labelsm;
16929                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16930             }
16931             
16932             if(this.labelxs > 0){
16933                 labelCfg.cls += ' col-xs-' + this.labelxs;
16934                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16935             }
16936                 
16937                 
16938         } else if ( this.fieldLabel.length) {
16939 //                Roo.log(" label");
16940                  cfg.cn = [
16941                    indicator,
16942                     {
16943                         tag: 'label',
16944                         //cls : 'input-group-addon',
16945                         html : this.fieldLabel
16946                     },
16947                     combobox
16948                 ];
16949                 
16950                 if(this.indicatorpos == 'right'){
16951                     cfg.cn = [
16952                         {
16953                             tag: 'label',
16954                             //cls : 'input-group-addon',
16955                             html : this.fieldLabel
16956                         },
16957                         indicator,
16958                         combobox
16959                     ];
16960                     
16961                 }
16962
16963         } else {
16964             
16965 //                Roo.log(" no label && no align");
16966                 cfg = combobox
16967                      
16968                 
16969         }
16970          
16971         var settings=this;
16972         ['xs','sm','md','lg'].map(function(size){
16973             if (settings[size]) {
16974                 cfg.cls += ' col-' + size + '-' + settings[size];
16975             }
16976         });
16977         
16978         return cfg;
16979         
16980     },
16981     
16982     _initEventsCalled : false,
16983     
16984     // private
16985     initEvents: function()
16986     {   
16987         if (this._initEventsCalled) { // as we call render... prevent looping...
16988             return;
16989         }
16990         this._initEventsCalled = true;
16991         
16992         if (!this.store) {
16993             throw "can not find store for combo";
16994         }
16995         
16996         this.indicator = this.indicatorEl();
16997         
16998         this.store = Roo.factory(this.store, Roo.data);
16999         this.store.parent = this;
17000         
17001         // if we are building from html. then this element is so complex, that we can not really
17002         // use the rendered HTML.
17003         // so we have to trash and replace the previous code.
17004         if (Roo.XComponent.build_from_html) {
17005             // remove this element....
17006             var e = this.el.dom, k=0;
17007             while (e ) { e = e.previousSibling;  ++k;}
17008
17009             this.el.remove();
17010             
17011             this.el=false;
17012             this.rendered = false;
17013             
17014             this.render(this.parent().getChildContainer(true), k);
17015         }
17016         
17017         if(Roo.isIOS && this.useNativeIOS){
17018             this.initIOSView();
17019             return;
17020         }
17021         
17022         /*
17023          * Touch Devices
17024          */
17025         
17026         if(Roo.isTouch && this.mobileTouchView){
17027             this.initTouchView();
17028             return;
17029         }
17030         
17031         if(this.tickable){
17032             this.initTickableEvents();
17033             return;
17034         }
17035         
17036         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17037         
17038         if(this.hiddenName){
17039             
17040             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17041             
17042             this.hiddenField.dom.value =
17043                 this.hiddenValue !== undefined ? this.hiddenValue :
17044                 this.value !== undefined ? this.value : '';
17045
17046             // prevent input submission
17047             this.el.dom.removeAttribute('name');
17048             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17049              
17050              
17051         }
17052         //if(Roo.isGecko){
17053         //    this.el.dom.setAttribute('autocomplete', 'off');
17054         //}
17055         
17056         var cls = 'x-combo-list';
17057         
17058         //this.list = new Roo.Layer({
17059         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17060         //});
17061         
17062         var _this = this;
17063         
17064         (function(){
17065             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17066             _this.list.setWidth(lw);
17067         }).defer(100);
17068         
17069         this.list.on('mouseover', this.onViewOver, this);
17070         this.list.on('mousemove', this.onViewMove, this);
17071         this.list.on('scroll', this.onViewScroll, this);
17072         
17073         /*
17074         this.list.swallowEvent('mousewheel');
17075         this.assetHeight = 0;
17076
17077         if(this.title){
17078             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17079             this.assetHeight += this.header.getHeight();
17080         }
17081
17082         this.innerList = this.list.createChild({cls:cls+'-inner'});
17083         this.innerList.on('mouseover', this.onViewOver, this);
17084         this.innerList.on('mousemove', this.onViewMove, this);
17085         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17086         
17087         if(this.allowBlank && !this.pageSize && !this.disableClear){
17088             this.footer = this.list.createChild({cls:cls+'-ft'});
17089             this.pageTb = new Roo.Toolbar(this.footer);
17090            
17091         }
17092         if(this.pageSize){
17093             this.footer = this.list.createChild({cls:cls+'-ft'});
17094             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17095                     {pageSize: this.pageSize});
17096             
17097         }
17098         
17099         if (this.pageTb && this.allowBlank && !this.disableClear) {
17100             var _this = this;
17101             this.pageTb.add(new Roo.Toolbar.Fill(), {
17102                 cls: 'x-btn-icon x-btn-clear',
17103                 text: '&#160;',
17104                 handler: function()
17105                 {
17106                     _this.collapse();
17107                     _this.clearValue();
17108                     _this.onSelect(false, -1);
17109                 }
17110             });
17111         }
17112         if (this.footer) {
17113             this.assetHeight += this.footer.getHeight();
17114         }
17115         */
17116             
17117         if(!this.tpl){
17118             this.tpl = Roo.bootstrap.version == 4 ?
17119                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17120                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17121         }
17122
17123         this.view = new Roo.View(this.list, this.tpl, {
17124             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17125         });
17126         //this.view.wrapEl.setDisplayed(false);
17127         this.view.on('click', this.onViewClick, this);
17128         
17129         
17130         this.store.on('beforeload', this.onBeforeLoad, this);
17131         this.store.on('load', this.onLoad, this);
17132         this.store.on('loadexception', this.onLoadException, this);
17133         /*
17134         if(this.resizable){
17135             this.resizer = new Roo.Resizable(this.list,  {
17136                pinned:true, handles:'se'
17137             });
17138             this.resizer.on('resize', function(r, w, h){
17139                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17140                 this.listWidth = w;
17141                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17142                 this.restrictHeight();
17143             }, this);
17144             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17145         }
17146         */
17147         if(!this.editable){
17148             this.editable = true;
17149             this.setEditable(false);
17150         }
17151         
17152         /*
17153         
17154         if (typeof(this.events.add.listeners) != 'undefined') {
17155             
17156             this.addicon = this.wrap.createChild(
17157                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17158        
17159             this.addicon.on('click', function(e) {
17160                 this.fireEvent('add', this);
17161             }, this);
17162         }
17163         if (typeof(this.events.edit.listeners) != 'undefined') {
17164             
17165             this.editicon = this.wrap.createChild(
17166                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17167             if (this.addicon) {
17168                 this.editicon.setStyle('margin-left', '40px');
17169             }
17170             this.editicon.on('click', function(e) {
17171                 
17172                 // we fire even  if inothing is selected..
17173                 this.fireEvent('edit', this, this.lastData );
17174                 
17175             }, this);
17176         }
17177         */
17178         
17179         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17180             "up" : function(e){
17181                 this.inKeyMode = true;
17182                 this.selectPrev();
17183             },
17184
17185             "down" : function(e){
17186                 if(!this.isExpanded()){
17187                     this.onTriggerClick();
17188                 }else{
17189                     this.inKeyMode = true;
17190                     this.selectNext();
17191                 }
17192             },
17193
17194             "enter" : function(e){
17195 //                this.onViewClick();
17196                 //return true;
17197                 this.collapse();
17198                 
17199                 if(this.fireEvent("specialkey", this, e)){
17200                     this.onViewClick(false);
17201                 }
17202                 
17203                 return true;
17204             },
17205
17206             "esc" : function(e){
17207                 this.collapse();
17208             },
17209
17210             "tab" : function(e){
17211                 this.collapse();
17212                 
17213                 if(this.fireEvent("specialkey", this, e)){
17214                     this.onViewClick(false);
17215                 }
17216                 
17217                 return true;
17218             },
17219
17220             scope : this,
17221
17222             doRelay : function(foo, bar, hname){
17223                 if(hname == 'down' || this.scope.isExpanded()){
17224                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17225                 }
17226                 return true;
17227             },
17228
17229             forceKeyDown: true
17230         });
17231         
17232         
17233         this.queryDelay = Math.max(this.queryDelay || 10,
17234                 this.mode == 'local' ? 10 : 250);
17235         
17236         
17237         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17238         
17239         if(this.typeAhead){
17240             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17241         }
17242         if(this.editable !== false){
17243             this.inputEl().on("keyup", this.onKeyUp, this);
17244         }
17245         if(this.forceSelection){
17246             this.inputEl().on('blur', this.doForce, this);
17247         }
17248         
17249         if(this.multiple){
17250             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17251             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17252         }
17253     },
17254     
17255     initTickableEvents: function()
17256     {   
17257         this.createList();
17258         
17259         if(this.hiddenName){
17260             
17261             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17262             
17263             this.hiddenField.dom.value =
17264                 this.hiddenValue !== undefined ? this.hiddenValue :
17265                 this.value !== undefined ? this.value : '';
17266
17267             // prevent input submission
17268             this.el.dom.removeAttribute('name');
17269             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17270              
17271              
17272         }
17273         
17274 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17275         
17276         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17277         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17278         if(this.triggerList){
17279             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17280         }
17281          
17282         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17283         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17284         
17285         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17286         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17287         
17288         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17289         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17290         
17291         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17292         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17293         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17294         
17295         this.okBtn.hide();
17296         this.cancelBtn.hide();
17297         
17298         var _this = this;
17299         
17300         (function(){
17301             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17302             _this.list.setWidth(lw);
17303         }).defer(100);
17304         
17305         this.list.on('mouseover', this.onViewOver, this);
17306         this.list.on('mousemove', this.onViewMove, this);
17307         
17308         this.list.on('scroll', this.onViewScroll, this);
17309         
17310         if(!this.tpl){
17311             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17312                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17313         }
17314
17315         this.view = new Roo.View(this.list, this.tpl, {
17316             singleSelect:true,
17317             tickable:true,
17318             parent:this,
17319             store: this.store,
17320             selectedClass: this.selectedClass
17321         });
17322         
17323         //this.view.wrapEl.setDisplayed(false);
17324         this.view.on('click', this.onViewClick, this);
17325         
17326         
17327         
17328         this.store.on('beforeload', this.onBeforeLoad, this);
17329         this.store.on('load', this.onLoad, this);
17330         this.store.on('loadexception', this.onLoadException, this);
17331         
17332         if(this.editable){
17333             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17334                 "up" : function(e){
17335                     this.inKeyMode = true;
17336                     this.selectPrev();
17337                 },
17338
17339                 "down" : function(e){
17340                     this.inKeyMode = true;
17341                     this.selectNext();
17342                 },
17343
17344                 "enter" : function(e){
17345                     if(this.fireEvent("specialkey", this, e)){
17346                         this.onViewClick(false);
17347                     }
17348                     
17349                     return true;
17350                 },
17351
17352                 "esc" : function(e){
17353                     this.onTickableFooterButtonClick(e, false, false);
17354                 },
17355
17356                 "tab" : function(e){
17357                     this.fireEvent("specialkey", this, e);
17358                     
17359                     this.onTickableFooterButtonClick(e, false, false);
17360                     
17361                     return true;
17362                 },
17363
17364                 scope : this,
17365
17366                 doRelay : function(e, fn, key){
17367                     if(this.scope.isExpanded()){
17368                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17369                     }
17370                     return true;
17371                 },
17372
17373                 forceKeyDown: true
17374             });
17375         }
17376         
17377         this.queryDelay = Math.max(this.queryDelay || 10,
17378                 this.mode == 'local' ? 10 : 250);
17379         
17380         
17381         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17382         
17383         if(this.typeAhead){
17384             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17385         }
17386         
17387         if(this.editable !== false){
17388             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17389         }
17390         
17391         this.indicator = this.indicatorEl();
17392         
17393         if(this.indicator){
17394             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17395             this.indicator.hide();
17396         }
17397         
17398     },
17399
17400     onDestroy : function(){
17401         if(this.view){
17402             this.view.setStore(null);
17403             this.view.el.removeAllListeners();
17404             this.view.el.remove();
17405             this.view.purgeListeners();
17406         }
17407         if(this.list){
17408             this.list.dom.innerHTML  = '';
17409         }
17410         
17411         if(this.store){
17412             this.store.un('beforeload', this.onBeforeLoad, this);
17413             this.store.un('load', this.onLoad, this);
17414             this.store.un('loadexception', this.onLoadException, this);
17415         }
17416         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17417     },
17418
17419     // private
17420     fireKey : function(e){
17421         if(e.isNavKeyPress() && !this.list.isVisible()){
17422             this.fireEvent("specialkey", this, e);
17423         }
17424     },
17425
17426     // private
17427     onResize: function(w, h)
17428     {
17429         
17430         
17431 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17432 //        
17433 //        if(typeof w != 'number'){
17434 //            // we do not handle it!?!?
17435 //            return;
17436 //        }
17437 //        var tw = this.trigger.getWidth();
17438 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17439 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17440 //        var x = w - tw;
17441 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17442 //            
17443 //        //this.trigger.setStyle('left', x+'px');
17444 //        
17445 //        if(this.list && this.listWidth === undefined){
17446 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17447 //            this.list.setWidth(lw);
17448 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17449 //        }
17450         
17451     
17452         
17453     },
17454
17455     /**
17456      * Allow or prevent the user from directly editing the field text.  If false is passed,
17457      * the user will only be able to select from the items defined in the dropdown list.  This method
17458      * is the runtime equivalent of setting the 'editable' config option at config time.
17459      * @param {Boolean} value True to allow the user to directly edit the field text
17460      */
17461     setEditable : function(value){
17462         if(value == this.editable){
17463             return;
17464         }
17465         this.editable = value;
17466         if(!value){
17467             this.inputEl().dom.setAttribute('readOnly', true);
17468             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17469             this.inputEl().addClass('x-combo-noedit');
17470         }else{
17471             this.inputEl().dom.removeAttribute('readOnly');
17472             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17473             this.inputEl().removeClass('x-combo-noedit');
17474         }
17475     },
17476
17477     // private
17478     
17479     onBeforeLoad : function(combo,opts){
17480         if(!this.hasFocus){
17481             return;
17482         }
17483          if (!opts.add) {
17484             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17485          }
17486         this.restrictHeight();
17487         this.selectedIndex = -1;
17488     },
17489
17490     // private
17491     onLoad : function(){
17492         
17493         this.hasQuery = false;
17494         
17495         if(!this.hasFocus){
17496             return;
17497         }
17498         
17499         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17500             this.loading.hide();
17501         }
17502         
17503         if(this.store.getCount() > 0){
17504             
17505             this.expand();
17506             this.restrictHeight();
17507             if(this.lastQuery == this.allQuery){
17508                 if(this.editable && !this.tickable){
17509                     this.inputEl().dom.select();
17510                 }
17511                 
17512                 if(
17513                     !this.selectByValue(this.value, true) &&
17514                     this.autoFocus && 
17515                     (
17516                         !this.store.lastOptions ||
17517                         typeof(this.store.lastOptions.add) == 'undefined' || 
17518                         this.store.lastOptions.add != true
17519                     )
17520                 ){
17521                     this.select(0, true);
17522                 }
17523             }else{
17524                 if(this.autoFocus){
17525                     this.selectNext();
17526                 }
17527                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17528                     this.taTask.delay(this.typeAheadDelay);
17529                 }
17530             }
17531         }else{
17532             this.onEmptyResults();
17533         }
17534         
17535         //this.el.focus();
17536     },
17537     // private
17538     onLoadException : function()
17539     {
17540         this.hasQuery = false;
17541         
17542         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17543             this.loading.hide();
17544         }
17545         
17546         if(this.tickable && this.editable){
17547             return;
17548         }
17549         
17550         this.collapse();
17551         // only causes errors at present
17552         //Roo.log(this.store.reader.jsonData);
17553         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17554             // fixme
17555             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17556         //}
17557         
17558         
17559     },
17560     // private
17561     onTypeAhead : function(){
17562         if(this.store.getCount() > 0){
17563             var r = this.store.getAt(0);
17564             var newValue = r.data[this.displayField];
17565             var len = newValue.length;
17566             var selStart = this.getRawValue().length;
17567             
17568             if(selStart != len){
17569                 this.setRawValue(newValue);
17570                 this.selectText(selStart, newValue.length);
17571             }
17572         }
17573     },
17574
17575     // private
17576     onSelect : function(record, index){
17577         
17578         if(this.fireEvent('beforeselect', this, record, index) !== false){
17579         
17580             this.setFromData(index > -1 ? record.data : false);
17581             
17582             this.collapse();
17583             this.fireEvent('select', this, record, index);
17584         }
17585     },
17586
17587     /**
17588      * Returns the currently selected field value or empty string if no value is set.
17589      * @return {String} value The selected value
17590      */
17591     getValue : function()
17592     {
17593         if(Roo.isIOS && this.useNativeIOS){
17594             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17595         }
17596         
17597         if(this.multiple){
17598             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17599         }
17600         
17601         if(this.valueField){
17602             return typeof this.value != 'undefined' ? this.value : '';
17603         }else{
17604             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17605         }
17606     },
17607     
17608     getRawValue : function()
17609     {
17610         if(Roo.isIOS && this.useNativeIOS){
17611             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17612         }
17613         
17614         var v = this.inputEl().getValue();
17615         
17616         return v;
17617     },
17618
17619     /**
17620      * Clears any text/value currently set in the field
17621      */
17622     clearValue : function(){
17623         
17624         if(this.hiddenField){
17625             this.hiddenField.dom.value = '';
17626         }
17627         this.value = '';
17628         this.setRawValue('');
17629         this.lastSelectionText = '';
17630         this.lastData = false;
17631         
17632         var close = this.closeTriggerEl();
17633         
17634         if(close){
17635             close.hide();
17636         }
17637         
17638         this.validate();
17639         
17640     },
17641
17642     /**
17643      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17644      * will be displayed in the field.  If the value does not match the data value of an existing item,
17645      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17646      * Otherwise the field will be blank (although the value will still be set).
17647      * @param {String} value The value to match
17648      */
17649     setValue : function(v)
17650     {
17651         if(Roo.isIOS && this.useNativeIOS){
17652             this.setIOSValue(v);
17653             return;
17654         }
17655         
17656         if(this.multiple){
17657             this.syncValue();
17658             return;
17659         }
17660         
17661         var text = v;
17662         if(this.valueField){
17663             var r = this.findRecord(this.valueField, v);
17664             if(r){
17665                 text = r.data[this.displayField];
17666             }else if(this.valueNotFoundText !== undefined){
17667                 text = this.valueNotFoundText;
17668             }
17669         }
17670         this.lastSelectionText = text;
17671         if(this.hiddenField){
17672             this.hiddenField.dom.value = v;
17673         }
17674         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17675         this.value = v;
17676         
17677         var close = this.closeTriggerEl();
17678         
17679         if(close){
17680             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17681         }
17682         
17683         this.validate();
17684     },
17685     /**
17686      * @property {Object} the last set data for the element
17687      */
17688     
17689     lastData : false,
17690     /**
17691      * Sets the value of the field based on a object which is related to the record format for the store.
17692      * @param {Object} value the value to set as. or false on reset?
17693      */
17694     setFromData : function(o){
17695         
17696         if(this.multiple){
17697             this.addItem(o);
17698             return;
17699         }
17700             
17701         var dv = ''; // display value
17702         var vv = ''; // value value..
17703         this.lastData = o;
17704         if (this.displayField) {
17705             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17706         } else {
17707             // this is an error condition!!!
17708             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17709         }
17710         
17711         if(this.valueField){
17712             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17713         }
17714         
17715         var close = this.closeTriggerEl();
17716         
17717         if(close){
17718             if(dv.length || vv * 1 > 0){
17719                 close.show() ;
17720                 this.blockFocus=true;
17721             } else {
17722                 close.hide();
17723             }             
17724         }
17725         
17726         if(this.hiddenField){
17727             this.hiddenField.dom.value = vv;
17728             
17729             this.lastSelectionText = dv;
17730             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17731             this.value = vv;
17732             return;
17733         }
17734         // no hidden field.. - we store the value in 'value', but still display
17735         // display field!!!!
17736         this.lastSelectionText = dv;
17737         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17738         this.value = vv;
17739         
17740         
17741         
17742     },
17743     // private
17744     reset : function(){
17745         // overridden so that last data is reset..
17746         
17747         if(this.multiple){
17748             this.clearItem();
17749             return;
17750         }
17751         
17752         this.setValue(this.originalValue);
17753         //this.clearInvalid();
17754         this.lastData = false;
17755         if (this.view) {
17756             this.view.clearSelections();
17757         }
17758         
17759         this.validate();
17760     },
17761     // private
17762     findRecord : function(prop, value){
17763         var record;
17764         if(this.store.getCount() > 0){
17765             this.store.each(function(r){
17766                 if(r.data[prop] == value){
17767                     record = r;
17768                     return false;
17769                 }
17770                 return true;
17771             });
17772         }
17773         return record;
17774     },
17775     
17776     getName: function()
17777     {
17778         // returns hidden if it's set..
17779         if (!this.rendered) {return ''};
17780         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17781         
17782     },
17783     // private
17784     onViewMove : function(e, t){
17785         this.inKeyMode = false;
17786     },
17787
17788     // private
17789     onViewOver : function(e, t){
17790         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17791             return;
17792         }
17793         var item = this.view.findItemFromChild(t);
17794         
17795         if(item){
17796             var index = this.view.indexOf(item);
17797             this.select(index, false);
17798         }
17799     },
17800
17801     // private
17802     onViewClick : function(view, doFocus, el, e)
17803     {
17804         var index = this.view.getSelectedIndexes()[0];
17805         
17806         var r = this.store.getAt(index);
17807         
17808         if(this.tickable){
17809             
17810             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17811                 return;
17812             }
17813             
17814             var rm = false;
17815             var _this = this;
17816             
17817             Roo.each(this.tickItems, function(v,k){
17818                 
17819                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17820                     Roo.log(v);
17821                     _this.tickItems.splice(k, 1);
17822                     
17823                     if(typeof(e) == 'undefined' && view == false){
17824                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17825                     }
17826                     
17827                     rm = true;
17828                     return;
17829                 }
17830             });
17831             
17832             if(rm){
17833                 return;
17834             }
17835             
17836             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17837                 this.tickItems.push(r.data);
17838             }
17839             
17840             if(typeof(e) == 'undefined' && view == false){
17841                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17842             }
17843                     
17844             return;
17845         }
17846         
17847         if(r){
17848             this.onSelect(r, index);
17849         }
17850         if(doFocus !== false && !this.blockFocus){
17851             this.inputEl().focus();
17852         }
17853     },
17854
17855     // private
17856     restrictHeight : function(){
17857         //this.innerList.dom.style.height = '';
17858         //var inner = this.innerList.dom;
17859         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17860         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17861         //this.list.beginUpdate();
17862         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17863         this.list.alignTo(this.inputEl(), this.listAlign);
17864         this.list.alignTo(this.inputEl(), this.listAlign);
17865         //this.list.endUpdate();
17866     },
17867
17868     // private
17869     onEmptyResults : function(){
17870         
17871         if(this.tickable && this.editable){
17872             this.hasFocus = false;
17873             this.restrictHeight();
17874             return;
17875         }
17876         
17877         this.collapse();
17878     },
17879
17880     /**
17881      * Returns true if the dropdown list is expanded, else false.
17882      */
17883     isExpanded : function(){
17884         return this.list.isVisible();
17885     },
17886
17887     /**
17888      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17889      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17890      * @param {String} value The data value of the item to select
17891      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17892      * selected item if it is not currently in view (defaults to true)
17893      * @return {Boolean} True if the value matched an item in the list, else false
17894      */
17895     selectByValue : function(v, scrollIntoView){
17896         if(v !== undefined && v !== null){
17897             var r = this.findRecord(this.valueField || this.displayField, v);
17898             if(r){
17899                 this.select(this.store.indexOf(r), scrollIntoView);
17900                 return true;
17901             }
17902         }
17903         return false;
17904     },
17905
17906     /**
17907      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17908      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17909      * @param {Number} index The zero-based index of the list item to select
17910      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17911      * selected item if it is not currently in view (defaults to true)
17912      */
17913     select : function(index, scrollIntoView){
17914         this.selectedIndex = index;
17915         this.view.select(index);
17916         if(scrollIntoView !== false){
17917             var el = this.view.getNode(index);
17918             /*
17919              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17920              */
17921             if(el){
17922                 this.list.scrollChildIntoView(el, false);
17923             }
17924         }
17925     },
17926
17927     // private
17928     selectNext : function(){
17929         var ct = this.store.getCount();
17930         if(ct > 0){
17931             if(this.selectedIndex == -1){
17932                 this.select(0);
17933             }else if(this.selectedIndex < ct-1){
17934                 this.select(this.selectedIndex+1);
17935             }
17936         }
17937     },
17938
17939     // private
17940     selectPrev : function(){
17941         var ct = this.store.getCount();
17942         if(ct > 0){
17943             if(this.selectedIndex == -1){
17944                 this.select(0);
17945             }else if(this.selectedIndex != 0){
17946                 this.select(this.selectedIndex-1);
17947             }
17948         }
17949     },
17950
17951     // private
17952     onKeyUp : function(e){
17953         if(this.editable !== false && !e.isSpecialKey()){
17954             this.lastKey = e.getKey();
17955             this.dqTask.delay(this.queryDelay);
17956         }
17957     },
17958
17959     // private
17960     validateBlur : function(){
17961         return !this.list || !this.list.isVisible();   
17962     },
17963
17964     // private
17965     initQuery : function(){
17966         
17967         var v = this.getRawValue();
17968         
17969         if(this.tickable && this.editable){
17970             v = this.tickableInputEl().getValue();
17971         }
17972         
17973         this.doQuery(v);
17974     },
17975
17976     // private
17977     doForce : function(){
17978         if(this.inputEl().dom.value.length > 0){
17979             this.inputEl().dom.value =
17980                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17981              
17982         }
17983     },
17984
17985     /**
17986      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17987      * query allowing the query action to be canceled if needed.
17988      * @param {String} query The SQL query to execute
17989      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17990      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17991      * saved in the current store (defaults to false)
17992      */
17993     doQuery : function(q, forceAll){
17994         
17995         if(q === undefined || q === null){
17996             q = '';
17997         }
17998         var qe = {
17999             query: q,
18000             forceAll: forceAll,
18001             combo: this,
18002             cancel:false
18003         };
18004         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18005             return false;
18006         }
18007         q = qe.query;
18008         
18009         forceAll = qe.forceAll;
18010         if(forceAll === true || (q.length >= this.minChars)){
18011             
18012             this.hasQuery = true;
18013             
18014             if(this.lastQuery != q || this.alwaysQuery){
18015                 this.lastQuery = q;
18016                 if(this.mode == 'local'){
18017                     this.selectedIndex = -1;
18018                     if(forceAll){
18019                         this.store.clearFilter();
18020                     }else{
18021                         
18022                         if(this.specialFilter){
18023                             this.fireEvent('specialfilter', this);
18024                             this.onLoad();
18025                             return;
18026                         }
18027                         
18028                         this.store.filter(this.displayField, q);
18029                     }
18030                     
18031                     this.store.fireEvent("datachanged", this.store);
18032                     
18033                     this.onLoad();
18034                     
18035                     
18036                 }else{
18037                     
18038                     this.store.baseParams[this.queryParam] = q;
18039                     
18040                     var options = {params : this.getParams(q)};
18041                     
18042                     if(this.loadNext){
18043                         options.add = true;
18044                         options.params.start = this.page * this.pageSize;
18045                     }
18046                     
18047                     this.store.load(options);
18048                     
18049                     /*
18050                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18051                      *  we should expand the list on onLoad
18052                      *  so command out it
18053                      */
18054 //                    this.expand();
18055                 }
18056             }else{
18057                 this.selectedIndex = -1;
18058                 this.onLoad();   
18059             }
18060         }
18061         
18062         this.loadNext = false;
18063     },
18064     
18065     // private
18066     getParams : function(q){
18067         var p = {};
18068         //p[this.queryParam] = q;
18069         
18070         if(this.pageSize){
18071             p.start = 0;
18072             p.limit = this.pageSize;
18073         }
18074         return p;
18075     },
18076
18077     /**
18078      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18079      */
18080     collapse : function(){
18081         if(!this.isExpanded()){
18082             return;
18083         }
18084         
18085         this.list.hide();
18086         
18087         this.hasFocus = false;
18088         
18089         if(this.tickable){
18090             this.okBtn.hide();
18091             this.cancelBtn.hide();
18092             this.trigger.show();
18093             
18094             if(this.editable){
18095                 this.tickableInputEl().dom.value = '';
18096                 this.tickableInputEl().blur();
18097             }
18098             
18099         }
18100         
18101         Roo.get(document).un('mousedown', this.collapseIf, this);
18102         Roo.get(document).un('mousewheel', this.collapseIf, this);
18103         if (!this.editable) {
18104             Roo.get(document).un('keydown', this.listKeyPress, this);
18105         }
18106         this.fireEvent('collapse', this);
18107         
18108         this.validate();
18109     },
18110
18111     // private
18112     collapseIf : function(e){
18113         var in_combo  = e.within(this.el);
18114         var in_list =  e.within(this.list);
18115         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18116         
18117         if (in_combo || in_list || is_list) {
18118             //e.stopPropagation();
18119             return;
18120         }
18121         
18122         if(this.tickable){
18123             this.onTickableFooterButtonClick(e, false, false);
18124         }
18125
18126         this.collapse();
18127         
18128     },
18129
18130     /**
18131      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18132      */
18133     expand : function(){
18134        
18135         if(this.isExpanded() || !this.hasFocus){
18136             return;
18137         }
18138         
18139         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18140         this.list.setWidth(lw);
18141         
18142         Roo.log('expand');
18143         
18144         this.list.show();
18145         
18146         this.restrictHeight();
18147         
18148         if(this.tickable){
18149             
18150             this.tickItems = Roo.apply([], this.item);
18151             
18152             this.okBtn.show();
18153             this.cancelBtn.show();
18154             this.trigger.hide();
18155             
18156             if(this.editable){
18157                 this.tickableInputEl().focus();
18158             }
18159             
18160         }
18161         
18162         Roo.get(document).on('mousedown', this.collapseIf, this);
18163         Roo.get(document).on('mousewheel', this.collapseIf, this);
18164         if (!this.editable) {
18165             Roo.get(document).on('keydown', this.listKeyPress, this);
18166         }
18167         
18168         this.fireEvent('expand', this);
18169     },
18170
18171     // private
18172     // Implements the default empty TriggerField.onTriggerClick function
18173     onTriggerClick : function(e)
18174     {
18175         Roo.log('trigger click');
18176         
18177         if(this.disabled || !this.triggerList){
18178             return;
18179         }
18180         
18181         this.page = 0;
18182         this.loadNext = false;
18183         
18184         if(this.isExpanded()){
18185             this.collapse();
18186             if (!this.blockFocus) {
18187                 this.inputEl().focus();
18188             }
18189             
18190         }else {
18191             this.hasFocus = true;
18192             if(this.triggerAction == 'all') {
18193                 this.doQuery(this.allQuery, true);
18194             } else {
18195                 this.doQuery(this.getRawValue());
18196             }
18197             if (!this.blockFocus) {
18198                 this.inputEl().focus();
18199             }
18200         }
18201     },
18202     
18203     onTickableTriggerClick : function(e)
18204     {
18205         if(this.disabled){
18206             return;
18207         }
18208         
18209         this.page = 0;
18210         this.loadNext = false;
18211         this.hasFocus = true;
18212         
18213         if(this.triggerAction == 'all') {
18214             this.doQuery(this.allQuery, true);
18215         } else {
18216             this.doQuery(this.getRawValue());
18217         }
18218     },
18219     
18220     onSearchFieldClick : function(e)
18221     {
18222         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18223             this.onTickableFooterButtonClick(e, false, false);
18224             return;
18225         }
18226         
18227         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18228             return;
18229         }
18230         
18231         this.page = 0;
18232         this.loadNext = false;
18233         this.hasFocus = true;
18234         
18235         if(this.triggerAction == 'all') {
18236             this.doQuery(this.allQuery, true);
18237         } else {
18238             this.doQuery(this.getRawValue());
18239         }
18240     },
18241     
18242     listKeyPress : function(e)
18243     {
18244         //Roo.log('listkeypress');
18245         // scroll to first matching element based on key pres..
18246         if (e.isSpecialKey()) {
18247             return false;
18248         }
18249         var k = String.fromCharCode(e.getKey()).toUpperCase();
18250         //Roo.log(k);
18251         var match  = false;
18252         var csel = this.view.getSelectedNodes();
18253         var cselitem = false;
18254         if (csel.length) {
18255             var ix = this.view.indexOf(csel[0]);
18256             cselitem  = this.store.getAt(ix);
18257             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18258                 cselitem = false;
18259             }
18260             
18261         }
18262         
18263         this.store.each(function(v) { 
18264             if (cselitem) {
18265                 // start at existing selection.
18266                 if (cselitem.id == v.id) {
18267                     cselitem = false;
18268                 }
18269                 return true;
18270             }
18271                 
18272             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18273                 match = this.store.indexOf(v);
18274                 return false;
18275             }
18276             return true;
18277         }, this);
18278         
18279         if (match === false) {
18280             return true; // no more action?
18281         }
18282         // scroll to?
18283         this.view.select(match);
18284         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18285         sn.scrollIntoView(sn.dom.parentNode, false);
18286     },
18287     
18288     onViewScroll : function(e, t){
18289         
18290         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
18291             return;
18292         }
18293         
18294         this.hasQuery = true;
18295         
18296         this.loading = this.list.select('.loading', true).first();
18297         
18298         if(this.loading === null){
18299             this.list.createChild({
18300                 tag: 'div',
18301                 cls: 'loading roo-select2-more-results roo-select2-active',
18302                 html: 'Loading more results...'
18303             });
18304             
18305             this.loading = this.list.select('.loading', true).first();
18306             
18307             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18308             
18309             this.loading.hide();
18310         }
18311         
18312         this.loading.show();
18313         
18314         var _combo = this;
18315         
18316         this.page++;
18317         this.loadNext = true;
18318         
18319         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18320         
18321         return;
18322     },
18323     
18324     addItem : function(o)
18325     {   
18326         var dv = ''; // display value
18327         
18328         if (this.displayField) {
18329             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18330         } else {
18331             // this is an error condition!!!
18332             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18333         }
18334         
18335         if(!dv.length){
18336             return;
18337         }
18338         
18339         var choice = this.choices.createChild({
18340             tag: 'li',
18341             cls: 'roo-select2-search-choice',
18342             cn: [
18343                 {
18344                     tag: 'div',
18345                     html: dv
18346                 },
18347                 {
18348                     tag: 'a',
18349                     href: '#',
18350                     cls: 'roo-select2-search-choice-close fa fa-times',
18351                     tabindex: '-1'
18352                 }
18353             ]
18354             
18355         }, this.searchField);
18356         
18357         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18358         
18359         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18360         
18361         this.item.push(o);
18362         
18363         this.lastData = o;
18364         
18365         this.syncValue();
18366         
18367         this.inputEl().dom.value = '';
18368         
18369         this.validate();
18370     },
18371     
18372     onRemoveItem : function(e, _self, o)
18373     {
18374         e.preventDefault();
18375         
18376         this.lastItem = Roo.apply([], this.item);
18377         
18378         var index = this.item.indexOf(o.data) * 1;
18379         
18380         if( index < 0){
18381             Roo.log('not this item?!');
18382             return;
18383         }
18384         
18385         this.item.splice(index, 1);
18386         o.item.remove();
18387         
18388         this.syncValue();
18389         
18390         this.fireEvent('remove', this, e);
18391         
18392         this.validate();
18393         
18394     },
18395     
18396     syncValue : function()
18397     {
18398         if(!this.item.length){
18399             this.clearValue();
18400             return;
18401         }
18402             
18403         var value = [];
18404         var _this = this;
18405         Roo.each(this.item, function(i){
18406             if(_this.valueField){
18407                 value.push(i[_this.valueField]);
18408                 return;
18409             }
18410
18411             value.push(i);
18412         });
18413
18414         this.value = value.join(',');
18415
18416         if(this.hiddenField){
18417             this.hiddenField.dom.value = this.value;
18418         }
18419         
18420         this.store.fireEvent("datachanged", this.store);
18421         
18422         this.validate();
18423     },
18424     
18425     clearItem : function()
18426     {
18427         if(!this.multiple){
18428             return;
18429         }
18430         
18431         this.item = [];
18432         
18433         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18434            c.remove();
18435         });
18436         
18437         this.syncValue();
18438         
18439         this.validate();
18440         
18441         if(this.tickable && !Roo.isTouch){
18442             this.view.refresh();
18443         }
18444     },
18445     
18446     inputEl: function ()
18447     {
18448         if(Roo.isIOS && this.useNativeIOS){
18449             return this.el.select('select.roo-ios-select', true).first();
18450         }
18451         
18452         if(Roo.isTouch && this.mobileTouchView){
18453             return this.el.select('input.form-control',true).first();
18454         }
18455         
18456         if(this.tickable){
18457             return this.searchField;
18458         }
18459         
18460         return this.el.select('input.form-control',true).first();
18461     },
18462     
18463     onTickableFooterButtonClick : function(e, btn, el)
18464     {
18465         e.preventDefault();
18466         
18467         this.lastItem = Roo.apply([], this.item);
18468         
18469         if(btn && btn.name == 'cancel'){
18470             this.tickItems = Roo.apply([], this.item);
18471             this.collapse();
18472             return;
18473         }
18474         
18475         this.clearItem();
18476         
18477         var _this = this;
18478         
18479         Roo.each(this.tickItems, function(o){
18480             _this.addItem(o);
18481         });
18482         
18483         this.collapse();
18484         
18485     },
18486     
18487     validate : function()
18488     {
18489         if(this.getVisibilityEl().hasClass('hidden')){
18490             return true;
18491         }
18492         
18493         var v = this.getRawValue();
18494         
18495         if(this.multiple){
18496             v = this.getValue();
18497         }
18498         
18499         if(this.disabled || this.allowBlank || v.length){
18500             this.markValid();
18501             return true;
18502         }
18503         
18504         this.markInvalid();
18505         return false;
18506     },
18507     
18508     tickableInputEl : function()
18509     {
18510         if(!this.tickable || !this.editable){
18511             return this.inputEl();
18512         }
18513         
18514         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18515     },
18516     
18517     
18518     getAutoCreateTouchView : function()
18519     {
18520         var id = Roo.id();
18521         
18522         var cfg = {
18523             cls: 'form-group' //input-group
18524         };
18525         
18526         var input =  {
18527             tag: 'input',
18528             id : id,
18529             type : this.inputType,
18530             cls : 'form-control x-combo-noedit',
18531             autocomplete: 'new-password',
18532             placeholder : this.placeholder || '',
18533             readonly : true
18534         };
18535         
18536         if (this.name) {
18537             input.name = this.name;
18538         }
18539         
18540         if (this.size) {
18541             input.cls += ' input-' + this.size;
18542         }
18543         
18544         if (this.disabled) {
18545             input.disabled = true;
18546         }
18547         
18548         var inputblock = {
18549             cls : 'roo-combobox-wrap',
18550             cn : [
18551                 input
18552             ]
18553         };
18554         
18555         if(this.before){
18556             inputblock.cls += ' input-group';
18557             
18558             inputblock.cn.unshift({
18559                 tag :'span',
18560                 cls : 'input-group-addon input-group-prepend input-group-text',
18561                 html : this.before
18562             });
18563         }
18564         
18565         if(this.removable && !this.multiple){
18566             inputblock.cls += ' roo-removable';
18567             
18568             inputblock.cn.push({
18569                 tag: 'button',
18570                 html : 'x',
18571                 cls : 'roo-combo-removable-btn close'
18572             });
18573         }
18574
18575         if(this.hasFeedback && !this.allowBlank){
18576             
18577             inputblock.cls += ' has-feedback';
18578             
18579             inputblock.cn.push({
18580                 tag: 'span',
18581                 cls: 'glyphicon form-control-feedback'
18582             });
18583             
18584         }
18585         
18586         if (this.after) {
18587             
18588             inputblock.cls += (this.before) ? '' : ' input-group';
18589             
18590             inputblock.cn.push({
18591                 tag :'span',
18592                 cls : 'input-group-addon input-group-append input-group-text',
18593                 html : this.after
18594             });
18595         }
18596
18597         
18598         var ibwrap = inputblock;
18599         
18600         if(this.multiple){
18601             ibwrap = {
18602                 tag: 'ul',
18603                 cls: 'roo-select2-choices',
18604                 cn:[
18605                     {
18606                         tag: 'li',
18607                         cls: 'roo-select2-search-field',
18608                         cn: [
18609
18610                             inputblock
18611                         ]
18612                     }
18613                 ]
18614             };
18615         
18616             
18617         }
18618         
18619         var combobox = {
18620             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18621             cn: [
18622                 {
18623                     tag: 'input',
18624                     type : 'hidden',
18625                     cls: 'form-hidden-field'
18626                 },
18627                 ibwrap
18628             ]
18629         };
18630         
18631         if(!this.multiple && this.showToggleBtn){
18632             
18633             var caret = {
18634                 cls: 'caret'
18635             };
18636             
18637             if (this.caret != false) {
18638                 caret = {
18639                      tag: 'i',
18640                      cls: 'fa fa-' + this.caret
18641                 };
18642                 
18643             }
18644             
18645             combobox.cn.push({
18646                 tag :'span',
18647                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18648                 cn : [
18649                     Roo.bootstrap.version == 3 ? caret : '',
18650                     {
18651                         tag: 'span',
18652                         cls: 'combobox-clear',
18653                         cn  : [
18654                             {
18655                                 tag : 'i',
18656                                 cls: 'icon-remove'
18657                             }
18658                         ]
18659                     }
18660                 ]
18661
18662             })
18663         }
18664         
18665         if(this.multiple){
18666             combobox.cls += ' roo-select2-container-multi';
18667         }
18668         
18669         var required =  this.allowBlank ?  {
18670                     tag : 'i',
18671                     style: 'display: none'
18672                 } : {
18673                    tag : 'i',
18674                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18675                    tooltip : 'This field is required'
18676                 };
18677         
18678         var align = this.labelAlign || this.parentLabelAlign();
18679         
18680         if (align ==='left' && this.fieldLabel.length) {
18681
18682             cfg.cn = [
18683                 required,
18684                 {
18685                     tag: 'label',
18686                     cls : 'control-label col-form-label',
18687                     html : this.fieldLabel
18688
18689                 },
18690                 {
18691                     cls : 'roo-combobox-wrap ', 
18692                     cn: [
18693                         combobox
18694                     ]
18695                 }
18696             ];
18697             
18698             var labelCfg = cfg.cn[1];
18699             var contentCfg = cfg.cn[2];
18700             
18701
18702             if(this.indicatorpos == 'right'){
18703                 cfg.cn = [
18704                     {
18705                         tag: 'label',
18706                         'for' :  id,
18707                         cls : 'control-label col-form-label',
18708                         cn : [
18709                             {
18710                                 tag : 'span',
18711                                 html : this.fieldLabel
18712                             },
18713                             required
18714                         ]
18715                     },
18716                     {
18717                         cls : "roo-combobox-wrap ",
18718                         cn: [
18719                             combobox
18720                         ]
18721                     }
18722
18723                 ];
18724                 
18725                 labelCfg = cfg.cn[0];
18726                 contentCfg = cfg.cn[1];
18727             }
18728             
18729            
18730             
18731             if(this.labelWidth > 12){
18732                 labelCfg.style = "width: " + this.labelWidth + 'px';
18733             }
18734            
18735             if(this.labelWidth < 13 && this.labelmd == 0){
18736                 this.labelmd = this.labelWidth;
18737             }
18738             
18739             if(this.labellg > 0){
18740                 labelCfg.cls += ' col-lg-' + this.labellg;
18741                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18742             }
18743             
18744             if(this.labelmd > 0){
18745                 labelCfg.cls += ' col-md-' + this.labelmd;
18746                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18747             }
18748             
18749             if(this.labelsm > 0){
18750                 labelCfg.cls += ' col-sm-' + this.labelsm;
18751                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18752             }
18753             
18754             if(this.labelxs > 0){
18755                 labelCfg.cls += ' col-xs-' + this.labelxs;
18756                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18757             }
18758                 
18759                 
18760         } else if ( this.fieldLabel.length) {
18761             cfg.cn = [
18762                required,
18763                 {
18764                     tag: 'label',
18765                     cls : 'control-label',
18766                     html : this.fieldLabel
18767
18768                 },
18769                 {
18770                     cls : '', 
18771                     cn: [
18772                         combobox
18773                     ]
18774                 }
18775             ];
18776             
18777             if(this.indicatorpos == 'right'){
18778                 cfg.cn = [
18779                     {
18780                         tag: 'label',
18781                         cls : 'control-label',
18782                         html : this.fieldLabel,
18783                         cn : [
18784                             required
18785                         ]
18786                     },
18787                     {
18788                         cls : '', 
18789                         cn: [
18790                             combobox
18791                         ]
18792                     }
18793                 ];
18794             }
18795         } else {
18796             cfg.cn = combobox;    
18797         }
18798         
18799         
18800         var settings = this;
18801         
18802         ['xs','sm','md','lg'].map(function(size){
18803             if (settings[size]) {
18804                 cfg.cls += ' col-' + size + '-' + settings[size];
18805             }
18806         });
18807         
18808         return cfg;
18809     },
18810     
18811     initTouchView : function()
18812     {
18813         this.renderTouchView();
18814         
18815         this.touchViewEl.on('scroll', function(){
18816             this.el.dom.scrollTop = 0;
18817         }, this);
18818         
18819         this.originalValue = this.getValue();
18820         
18821         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18822         
18823         this.inputEl().on("click", this.showTouchView, this);
18824         if (this.triggerEl) {
18825             this.triggerEl.on("click", this.showTouchView, this);
18826         }
18827         
18828         
18829         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18830         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18831         
18832         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18833         
18834         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18835         this.store.on('load', this.onTouchViewLoad, this);
18836         this.store.on('loadexception', this.onTouchViewLoadException, this);
18837         
18838         if(this.hiddenName){
18839             
18840             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18841             
18842             this.hiddenField.dom.value =
18843                 this.hiddenValue !== undefined ? this.hiddenValue :
18844                 this.value !== undefined ? this.value : '';
18845         
18846             this.el.dom.removeAttribute('name');
18847             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18848         }
18849         
18850         if(this.multiple){
18851             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18852             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18853         }
18854         
18855         if(this.removable && !this.multiple){
18856             var close = this.closeTriggerEl();
18857             if(close){
18858                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18859                 close.on('click', this.removeBtnClick, this, close);
18860             }
18861         }
18862         /*
18863          * fix the bug in Safari iOS8
18864          */
18865         this.inputEl().on("focus", function(e){
18866             document.activeElement.blur();
18867         }, this);
18868         
18869         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18870         
18871         return;
18872         
18873         
18874     },
18875     
18876     renderTouchView : function()
18877     {
18878         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18879         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18880         
18881         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18882         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18883         
18884         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18885         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18886         this.touchViewBodyEl.setStyle('overflow', 'auto');
18887         
18888         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18889         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18890         
18891         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18892         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18893         
18894     },
18895     
18896     showTouchView : function()
18897     {
18898         if(this.disabled){
18899             return;
18900         }
18901         
18902         this.touchViewHeaderEl.hide();
18903
18904         if(this.modalTitle.length){
18905             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18906             this.touchViewHeaderEl.show();
18907         }
18908
18909         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18910         this.touchViewEl.show();
18911
18912         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18913         
18914         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18915         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18916
18917         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18918
18919         if(this.modalTitle.length){
18920             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18921         }
18922         
18923         this.touchViewBodyEl.setHeight(bodyHeight);
18924
18925         if(this.animate){
18926             var _this = this;
18927             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18928         }else{
18929             this.touchViewEl.addClass(['in','show']);
18930         }
18931         
18932         if(this._touchViewMask){
18933             Roo.get(document.body).addClass("x-body-masked");
18934             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18935             this._touchViewMask.setStyle('z-index', 10000);
18936             this._touchViewMask.addClass('show');
18937         }
18938         
18939         this.doTouchViewQuery();
18940         
18941     },
18942     
18943     hideTouchView : function()
18944     {
18945         this.touchViewEl.removeClass(['in','show']);
18946
18947         if(this.animate){
18948             var _this = this;
18949             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18950         }else{
18951             this.touchViewEl.setStyle('display', 'none');
18952         }
18953         
18954         if(this._touchViewMask){
18955             this._touchViewMask.removeClass('show');
18956             Roo.get(document.body).removeClass("x-body-masked");
18957         }
18958     },
18959     
18960     setTouchViewValue : function()
18961     {
18962         if(this.multiple){
18963             this.clearItem();
18964         
18965             var _this = this;
18966
18967             Roo.each(this.tickItems, function(o){
18968                 this.addItem(o);
18969             }, this);
18970         }
18971         
18972         this.hideTouchView();
18973     },
18974     
18975     doTouchViewQuery : function()
18976     {
18977         var qe = {
18978             query: '',
18979             forceAll: true,
18980             combo: this,
18981             cancel:false
18982         };
18983         
18984         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18985             return false;
18986         }
18987         
18988         if(!this.alwaysQuery || this.mode == 'local'){
18989             this.onTouchViewLoad();
18990             return;
18991         }
18992         
18993         this.store.load();
18994     },
18995     
18996     onTouchViewBeforeLoad : function(combo,opts)
18997     {
18998         return;
18999     },
19000
19001     // private
19002     onTouchViewLoad : function()
19003     {
19004         if(this.store.getCount() < 1){
19005             this.onTouchViewEmptyResults();
19006             return;
19007         }
19008         
19009         this.clearTouchView();
19010         
19011         var rawValue = this.getRawValue();
19012         
19013         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19014         
19015         this.tickItems = [];
19016         
19017         this.store.data.each(function(d, rowIndex){
19018             var row = this.touchViewListGroup.createChild(template);
19019             
19020             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19021                 row.addClass(d.data.cls);
19022             }
19023             
19024             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19025                 var cfg = {
19026                     data : d.data,
19027                     html : d.data[this.displayField]
19028                 };
19029                 
19030                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19031                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19032                 }
19033             }
19034             row.removeClass('selected');
19035             if(!this.multiple && this.valueField &&
19036                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19037             {
19038                 // radio buttons..
19039                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19040                 row.addClass('selected');
19041             }
19042             
19043             if(this.multiple && this.valueField &&
19044                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19045             {
19046                 
19047                 // checkboxes...
19048                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19049                 this.tickItems.push(d.data);
19050             }
19051             
19052             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19053             
19054         }, this);
19055         
19056         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19057         
19058         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19059
19060         if(this.modalTitle.length){
19061             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19062         }
19063
19064         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19065         
19066         if(this.mobile_restrict_height && listHeight < bodyHeight){
19067             this.touchViewBodyEl.setHeight(listHeight);
19068         }
19069         
19070         var _this = this;
19071         
19072         if(firstChecked && listHeight > bodyHeight){
19073             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19074         }
19075         
19076     },
19077     
19078     onTouchViewLoadException : function()
19079     {
19080         this.hideTouchView();
19081     },
19082     
19083     onTouchViewEmptyResults : function()
19084     {
19085         this.clearTouchView();
19086         
19087         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19088         
19089         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19090         
19091     },
19092     
19093     clearTouchView : function()
19094     {
19095         this.touchViewListGroup.dom.innerHTML = '';
19096     },
19097     
19098     onTouchViewClick : function(e, el, o)
19099     {
19100         e.preventDefault();
19101         
19102         var row = o.row;
19103         var rowIndex = o.rowIndex;
19104         
19105         var r = this.store.getAt(rowIndex);
19106         
19107         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19108             
19109             if(!this.multiple){
19110                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19111                     c.dom.removeAttribute('checked');
19112                 }, this);
19113
19114                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19115
19116                 this.setFromData(r.data);
19117
19118                 var close = this.closeTriggerEl();
19119
19120                 if(close){
19121                     close.show();
19122                 }
19123
19124                 this.hideTouchView();
19125
19126                 this.fireEvent('select', this, r, rowIndex);
19127
19128                 return;
19129             }
19130
19131             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19132                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19133                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19134                 return;
19135             }
19136
19137             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19138             this.addItem(r.data);
19139             this.tickItems.push(r.data);
19140         }
19141     },
19142     
19143     getAutoCreateNativeIOS : function()
19144     {
19145         var cfg = {
19146             cls: 'form-group' //input-group,
19147         };
19148         
19149         var combobox =  {
19150             tag: 'select',
19151             cls : 'roo-ios-select'
19152         };
19153         
19154         if (this.name) {
19155             combobox.name = this.name;
19156         }
19157         
19158         if (this.disabled) {
19159             combobox.disabled = true;
19160         }
19161         
19162         var settings = this;
19163         
19164         ['xs','sm','md','lg'].map(function(size){
19165             if (settings[size]) {
19166                 cfg.cls += ' col-' + size + '-' + settings[size];
19167             }
19168         });
19169         
19170         cfg.cn = combobox;
19171         
19172         return cfg;
19173         
19174     },
19175     
19176     initIOSView : function()
19177     {
19178         this.store.on('load', this.onIOSViewLoad, this);
19179         
19180         return;
19181     },
19182     
19183     onIOSViewLoad : function()
19184     {
19185         if(this.store.getCount() < 1){
19186             return;
19187         }
19188         
19189         this.clearIOSView();
19190         
19191         if(this.allowBlank) {
19192             
19193             var default_text = '-- SELECT --';
19194             
19195             if(this.placeholder.length){
19196                 default_text = this.placeholder;
19197             }
19198             
19199             if(this.emptyTitle.length){
19200                 default_text += ' - ' + this.emptyTitle + ' -';
19201             }
19202             
19203             var opt = this.inputEl().createChild({
19204                 tag: 'option',
19205                 value : 0,
19206                 html : default_text
19207             });
19208             
19209             var o = {};
19210             o[this.valueField] = 0;
19211             o[this.displayField] = default_text;
19212             
19213             this.ios_options.push({
19214                 data : o,
19215                 el : opt
19216             });
19217             
19218         }
19219         
19220         this.store.data.each(function(d, rowIndex){
19221             
19222             var html = '';
19223             
19224             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19225                 html = d.data[this.displayField];
19226             }
19227             
19228             var value = '';
19229             
19230             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19231                 value = d.data[this.valueField];
19232             }
19233             
19234             var option = {
19235                 tag: 'option',
19236                 value : value,
19237                 html : html
19238             };
19239             
19240             if(this.value == d.data[this.valueField]){
19241                 option['selected'] = true;
19242             }
19243             
19244             var opt = this.inputEl().createChild(option);
19245             
19246             this.ios_options.push({
19247                 data : d.data,
19248                 el : opt
19249             });
19250             
19251         }, this);
19252         
19253         this.inputEl().on('change', function(){
19254            this.fireEvent('select', this);
19255         }, this);
19256         
19257     },
19258     
19259     clearIOSView: function()
19260     {
19261         this.inputEl().dom.innerHTML = '';
19262         
19263         this.ios_options = [];
19264     },
19265     
19266     setIOSValue: function(v)
19267     {
19268         this.value = v;
19269         
19270         if(!this.ios_options){
19271             return;
19272         }
19273         
19274         Roo.each(this.ios_options, function(opts){
19275            
19276            opts.el.dom.removeAttribute('selected');
19277            
19278            if(opts.data[this.valueField] != v){
19279                return;
19280            }
19281            
19282            opts.el.dom.setAttribute('selected', true);
19283            
19284         }, this);
19285     }
19286
19287     /** 
19288     * @cfg {Boolean} grow 
19289     * @hide 
19290     */
19291     /** 
19292     * @cfg {Number} growMin 
19293     * @hide 
19294     */
19295     /** 
19296     * @cfg {Number} growMax 
19297     * @hide 
19298     */
19299     /**
19300      * @hide
19301      * @method autoSize
19302      */
19303 });
19304
19305 Roo.apply(Roo.bootstrap.ComboBox,  {
19306     
19307     header : {
19308         tag: 'div',
19309         cls: 'modal-header',
19310         cn: [
19311             {
19312                 tag: 'h4',
19313                 cls: 'modal-title'
19314             }
19315         ]
19316     },
19317     
19318     body : {
19319         tag: 'div',
19320         cls: 'modal-body',
19321         cn: [
19322             {
19323                 tag: 'ul',
19324                 cls: 'list-group'
19325             }
19326         ]
19327     },
19328     
19329     listItemRadio : {
19330         tag: 'li',
19331         cls: 'list-group-item',
19332         cn: [
19333             {
19334                 tag: 'span',
19335                 cls: 'roo-combobox-list-group-item-value'
19336             },
19337             {
19338                 tag: 'div',
19339                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19340                 cn: [
19341                     {
19342                         tag: 'input',
19343                         type: 'radio'
19344                     },
19345                     {
19346                         tag: 'label'
19347                     }
19348                 ]
19349             }
19350         ]
19351     },
19352     
19353     listItemCheckbox : {
19354         tag: 'li',
19355         cls: 'list-group-item',
19356         cn: [
19357             {
19358                 tag: 'span',
19359                 cls: 'roo-combobox-list-group-item-value'
19360             },
19361             {
19362                 tag: 'div',
19363                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19364                 cn: [
19365                     {
19366                         tag: 'input',
19367                         type: 'checkbox'
19368                     },
19369                     {
19370                         tag: 'label'
19371                     }
19372                 ]
19373             }
19374         ]
19375     },
19376     
19377     emptyResult : {
19378         tag: 'div',
19379         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19380     },
19381     
19382     footer : {
19383         tag: 'div',
19384         cls: 'modal-footer',
19385         cn: [
19386             {
19387                 tag: 'div',
19388                 cls: 'row',
19389                 cn: [
19390                     {
19391                         tag: 'div',
19392                         cls: 'col-xs-6 text-left',
19393                         cn: {
19394                             tag: 'button',
19395                             cls: 'btn btn-danger roo-touch-view-cancel',
19396                             html: 'Cancel'
19397                         }
19398                     },
19399                     {
19400                         tag: 'div',
19401                         cls: 'col-xs-6 text-right',
19402                         cn: {
19403                             tag: 'button',
19404                             cls: 'btn btn-success roo-touch-view-ok',
19405                             html: 'OK'
19406                         }
19407                     }
19408                 ]
19409             }
19410         ]
19411         
19412     }
19413 });
19414
19415 Roo.apply(Roo.bootstrap.ComboBox,  {
19416     
19417     touchViewTemplate : {
19418         tag: 'div',
19419         cls: 'modal fade roo-combobox-touch-view',
19420         cn: [
19421             {
19422                 tag: 'div',
19423                 cls: 'modal-dialog',
19424                 style : 'position:fixed', // we have to fix position....
19425                 cn: [
19426                     {
19427                         tag: 'div',
19428                         cls: 'modal-content',
19429                         cn: [
19430                             Roo.bootstrap.ComboBox.header,
19431                             Roo.bootstrap.ComboBox.body,
19432                             Roo.bootstrap.ComboBox.footer
19433                         ]
19434                     }
19435                 ]
19436             }
19437         ]
19438     }
19439 });/*
19440  * Based on:
19441  * Ext JS Library 1.1.1
19442  * Copyright(c) 2006-2007, Ext JS, LLC.
19443  *
19444  * Originally Released Under LGPL - original licence link has changed is not relivant.
19445  *
19446  * Fork - LGPL
19447  * <script type="text/javascript">
19448  */
19449
19450 /**
19451  * @class Roo.View
19452  * @extends Roo.util.Observable
19453  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19454  * This class also supports single and multi selection modes. <br>
19455  * Create a data model bound view:
19456  <pre><code>
19457  var store = new Roo.data.Store(...);
19458
19459  var view = new Roo.View({
19460     el : "my-element",
19461     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19462  
19463     singleSelect: true,
19464     selectedClass: "ydataview-selected",
19465     store: store
19466  });
19467
19468  // listen for node click?
19469  view.on("click", function(vw, index, node, e){
19470  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19471  });
19472
19473  // load XML data
19474  dataModel.load("foobar.xml");
19475  </code></pre>
19476  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19477  * <br><br>
19478  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19479  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19480  * 
19481  * Note: old style constructor is still suported (container, template, config)
19482  * 
19483  * @constructor
19484  * Create a new View
19485  * @param {Object} config The config object
19486  * 
19487  */
19488 Roo.View = function(config, depreciated_tpl, depreciated_config){
19489     
19490     this.parent = false;
19491     
19492     if (typeof(depreciated_tpl) == 'undefined') {
19493         // new way.. - universal constructor.
19494         Roo.apply(this, config);
19495         this.el  = Roo.get(this.el);
19496     } else {
19497         // old format..
19498         this.el  = Roo.get(config);
19499         this.tpl = depreciated_tpl;
19500         Roo.apply(this, depreciated_config);
19501     }
19502     this.wrapEl  = this.el.wrap().wrap();
19503     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19504     
19505     
19506     if(typeof(this.tpl) == "string"){
19507         this.tpl = new Roo.Template(this.tpl);
19508     } else {
19509         // support xtype ctors..
19510         this.tpl = new Roo.factory(this.tpl, Roo);
19511     }
19512     
19513     
19514     this.tpl.compile();
19515     
19516     /** @private */
19517     this.addEvents({
19518         /**
19519          * @event beforeclick
19520          * Fires before a click is processed. Returns false to cancel the default action.
19521          * @param {Roo.View} this
19522          * @param {Number} index The index of the target node
19523          * @param {HTMLElement} node The target node
19524          * @param {Roo.EventObject} e The raw event object
19525          */
19526             "beforeclick" : true,
19527         /**
19528          * @event click
19529          * Fires when a template node is clicked.
19530          * @param {Roo.View} this
19531          * @param {Number} index The index of the target node
19532          * @param {HTMLElement} node The target node
19533          * @param {Roo.EventObject} e The raw event object
19534          */
19535             "click" : true,
19536         /**
19537          * @event dblclick
19538          * Fires when a template node is double clicked.
19539          * @param {Roo.View} this
19540          * @param {Number} index The index of the target node
19541          * @param {HTMLElement} node The target node
19542          * @param {Roo.EventObject} e The raw event object
19543          */
19544             "dblclick" : true,
19545         /**
19546          * @event contextmenu
19547          * Fires when a template node is right clicked.
19548          * @param {Roo.View} this
19549          * @param {Number} index The index of the target node
19550          * @param {HTMLElement} node The target node
19551          * @param {Roo.EventObject} e The raw event object
19552          */
19553             "contextmenu" : true,
19554         /**
19555          * @event selectionchange
19556          * Fires when the selected nodes change.
19557          * @param {Roo.View} this
19558          * @param {Array} selections Array of the selected nodes
19559          */
19560             "selectionchange" : true,
19561     
19562         /**
19563          * @event beforeselect
19564          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19565          * @param {Roo.View} this
19566          * @param {HTMLElement} node The node to be selected
19567          * @param {Array} selections Array of currently selected nodes
19568          */
19569             "beforeselect" : true,
19570         /**
19571          * @event preparedata
19572          * Fires on every row to render, to allow you to change the data.
19573          * @param {Roo.View} this
19574          * @param {Object} data to be rendered (change this)
19575          */
19576           "preparedata" : true
19577           
19578           
19579         });
19580
19581
19582
19583     this.el.on({
19584         "click": this.onClick,
19585         "dblclick": this.onDblClick,
19586         "contextmenu": this.onContextMenu,
19587         scope:this
19588     });
19589
19590     this.selections = [];
19591     this.nodes = [];
19592     this.cmp = new Roo.CompositeElementLite([]);
19593     if(this.store){
19594         this.store = Roo.factory(this.store, Roo.data);
19595         this.setStore(this.store, true);
19596     }
19597     
19598     if ( this.footer && this.footer.xtype) {
19599            
19600          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19601         
19602         this.footer.dataSource = this.store;
19603         this.footer.container = fctr;
19604         this.footer = Roo.factory(this.footer, Roo);
19605         fctr.insertFirst(this.el);
19606         
19607         // this is a bit insane - as the paging toolbar seems to detach the el..
19608 //        dom.parentNode.parentNode.parentNode
19609          // they get detached?
19610     }
19611     
19612     
19613     Roo.View.superclass.constructor.call(this);
19614     
19615     
19616 };
19617
19618 Roo.extend(Roo.View, Roo.util.Observable, {
19619     
19620      /**
19621      * @cfg {Roo.data.Store} store Data store to load data from.
19622      */
19623     store : false,
19624     
19625     /**
19626      * @cfg {String|Roo.Element} el The container element.
19627      */
19628     el : '',
19629     
19630     /**
19631      * @cfg {String|Roo.Template} tpl The template used by this View 
19632      */
19633     tpl : false,
19634     /**
19635      * @cfg {String} dataName the named area of the template to use as the data area
19636      *                          Works with domtemplates roo-name="name"
19637      */
19638     dataName: false,
19639     /**
19640      * @cfg {String} selectedClass The css class to add to selected nodes
19641      */
19642     selectedClass : "x-view-selected",
19643      /**
19644      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19645      */
19646     emptyText : "",
19647     
19648     /**
19649      * @cfg {String} text to display on mask (default Loading)
19650      */
19651     mask : false,
19652     /**
19653      * @cfg {Boolean} multiSelect Allow multiple selection
19654      */
19655     multiSelect : false,
19656     /**
19657      * @cfg {Boolean} singleSelect Allow single selection
19658      */
19659     singleSelect:  false,
19660     
19661     /**
19662      * @cfg {Boolean} toggleSelect - selecting 
19663      */
19664     toggleSelect : false,
19665     
19666     /**
19667      * @cfg {Boolean} tickable - selecting 
19668      */
19669     tickable : false,
19670     
19671     /**
19672      * Returns the element this view is bound to.
19673      * @return {Roo.Element}
19674      */
19675     getEl : function(){
19676         return this.wrapEl;
19677     },
19678     
19679     
19680
19681     /**
19682      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19683      */
19684     refresh : function(){
19685         //Roo.log('refresh');
19686         var t = this.tpl;
19687         
19688         // if we are using something like 'domtemplate', then
19689         // the what gets used is:
19690         // t.applySubtemplate(NAME, data, wrapping data..)
19691         // the outer template then get' applied with
19692         //     the store 'extra data'
19693         // and the body get's added to the
19694         //      roo-name="data" node?
19695         //      <span class='roo-tpl-{name}'></span> ?????
19696         
19697         
19698         
19699         this.clearSelections();
19700         this.el.update("");
19701         var html = [];
19702         var records = this.store.getRange();
19703         if(records.length < 1) {
19704             
19705             // is this valid??  = should it render a template??
19706             
19707             this.el.update(this.emptyText);
19708             return;
19709         }
19710         var el = this.el;
19711         if (this.dataName) {
19712             this.el.update(t.apply(this.store.meta)); //????
19713             el = this.el.child('.roo-tpl-' + this.dataName);
19714         }
19715         
19716         for(var i = 0, len = records.length; i < len; i++){
19717             var data = this.prepareData(records[i].data, i, records[i]);
19718             this.fireEvent("preparedata", this, data, i, records[i]);
19719             
19720             var d = Roo.apply({}, data);
19721             
19722             if(this.tickable){
19723                 Roo.apply(d, {'roo-id' : Roo.id()});
19724                 
19725                 var _this = this;
19726             
19727                 Roo.each(this.parent.item, function(item){
19728                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19729                         return;
19730                     }
19731                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19732                 });
19733             }
19734             
19735             html[html.length] = Roo.util.Format.trim(
19736                 this.dataName ?
19737                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19738                     t.apply(d)
19739             );
19740         }
19741         
19742         
19743         
19744         el.update(html.join(""));
19745         this.nodes = el.dom.childNodes;
19746         this.updateIndexes(0);
19747     },
19748     
19749
19750     /**
19751      * Function to override to reformat the data that is sent to
19752      * the template for each node.
19753      * DEPRICATED - use the preparedata event handler.
19754      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19755      * a JSON object for an UpdateManager bound view).
19756      */
19757     prepareData : function(data, index, record)
19758     {
19759         this.fireEvent("preparedata", this, data, index, record);
19760         return data;
19761     },
19762
19763     onUpdate : function(ds, record){
19764         // Roo.log('on update');   
19765         this.clearSelections();
19766         var index = this.store.indexOf(record);
19767         var n = this.nodes[index];
19768         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19769         n.parentNode.removeChild(n);
19770         this.updateIndexes(index, index);
19771     },
19772
19773     
19774     
19775 // --------- FIXME     
19776     onAdd : function(ds, records, index)
19777     {
19778         //Roo.log(['on Add', ds, records, index] );        
19779         this.clearSelections();
19780         if(this.nodes.length == 0){
19781             this.refresh();
19782             return;
19783         }
19784         var n = this.nodes[index];
19785         for(var i = 0, len = records.length; i < len; i++){
19786             var d = this.prepareData(records[i].data, i, records[i]);
19787             if(n){
19788                 this.tpl.insertBefore(n, d);
19789             }else{
19790                 
19791                 this.tpl.append(this.el, d);
19792             }
19793         }
19794         this.updateIndexes(index);
19795     },
19796
19797     onRemove : function(ds, record, index){
19798        // Roo.log('onRemove');
19799         this.clearSelections();
19800         var el = this.dataName  ?
19801             this.el.child('.roo-tpl-' + this.dataName) :
19802             this.el; 
19803         
19804         el.dom.removeChild(this.nodes[index]);
19805         this.updateIndexes(index);
19806     },
19807
19808     /**
19809      * Refresh an individual node.
19810      * @param {Number} index
19811      */
19812     refreshNode : function(index){
19813         this.onUpdate(this.store, this.store.getAt(index));
19814     },
19815
19816     updateIndexes : function(startIndex, endIndex){
19817         var ns = this.nodes;
19818         startIndex = startIndex || 0;
19819         endIndex = endIndex || ns.length - 1;
19820         for(var i = startIndex; i <= endIndex; i++){
19821             ns[i].nodeIndex = i;
19822         }
19823     },
19824
19825     /**
19826      * Changes the data store this view uses and refresh the view.
19827      * @param {Store} store
19828      */
19829     setStore : function(store, initial){
19830         if(!initial && this.store){
19831             this.store.un("datachanged", this.refresh);
19832             this.store.un("add", this.onAdd);
19833             this.store.un("remove", this.onRemove);
19834             this.store.un("update", this.onUpdate);
19835             this.store.un("clear", this.refresh);
19836             this.store.un("beforeload", this.onBeforeLoad);
19837             this.store.un("load", this.onLoad);
19838             this.store.un("loadexception", this.onLoad);
19839         }
19840         if(store){
19841           
19842             store.on("datachanged", this.refresh, this);
19843             store.on("add", this.onAdd, this);
19844             store.on("remove", this.onRemove, this);
19845             store.on("update", this.onUpdate, this);
19846             store.on("clear", this.refresh, this);
19847             store.on("beforeload", this.onBeforeLoad, this);
19848             store.on("load", this.onLoad, this);
19849             store.on("loadexception", this.onLoad, this);
19850         }
19851         
19852         if(store){
19853             this.refresh();
19854         }
19855     },
19856     /**
19857      * onbeforeLoad - masks the loading area.
19858      *
19859      */
19860     onBeforeLoad : function(store,opts)
19861     {
19862          //Roo.log('onBeforeLoad');   
19863         if (!opts.add) {
19864             this.el.update("");
19865         }
19866         this.el.mask(this.mask ? this.mask : "Loading" ); 
19867     },
19868     onLoad : function ()
19869     {
19870         this.el.unmask();
19871     },
19872     
19873
19874     /**
19875      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19876      * @param {HTMLElement} node
19877      * @return {HTMLElement} The template node
19878      */
19879     findItemFromChild : function(node){
19880         var el = this.dataName  ?
19881             this.el.child('.roo-tpl-' + this.dataName,true) :
19882             this.el.dom; 
19883         
19884         if(!node || node.parentNode == el){
19885                     return node;
19886             }
19887             var p = node.parentNode;
19888             while(p && p != el){
19889             if(p.parentNode == el){
19890                 return p;
19891             }
19892             p = p.parentNode;
19893         }
19894             return null;
19895     },
19896
19897     /** @ignore */
19898     onClick : function(e){
19899         var item = this.findItemFromChild(e.getTarget());
19900         if(item){
19901             var index = this.indexOf(item);
19902             if(this.onItemClick(item, index, e) !== false){
19903                 this.fireEvent("click", this, index, item, e);
19904             }
19905         }else{
19906             this.clearSelections();
19907         }
19908     },
19909
19910     /** @ignore */
19911     onContextMenu : function(e){
19912         var item = this.findItemFromChild(e.getTarget());
19913         if(item){
19914             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19915         }
19916     },
19917
19918     /** @ignore */
19919     onDblClick : function(e){
19920         var item = this.findItemFromChild(e.getTarget());
19921         if(item){
19922             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19923         }
19924     },
19925
19926     onItemClick : function(item, index, e)
19927     {
19928         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19929             return false;
19930         }
19931         if (this.toggleSelect) {
19932             var m = this.isSelected(item) ? 'unselect' : 'select';
19933             //Roo.log(m);
19934             var _t = this;
19935             _t[m](item, true, false);
19936             return true;
19937         }
19938         if(this.multiSelect || this.singleSelect){
19939             if(this.multiSelect && e.shiftKey && this.lastSelection){
19940                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19941             }else{
19942                 this.select(item, this.multiSelect && e.ctrlKey);
19943                 this.lastSelection = item;
19944             }
19945             
19946             if(!this.tickable){
19947                 e.preventDefault();
19948             }
19949             
19950         }
19951         return true;
19952     },
19953
19954     /**
19955      * Get the number of selected nodes.
19956      * @return {Number}
19957      */
19958     getSelectionCount : function(){
19959         return this.selections.length;
19960     },
19961
19962     /**
19963      * Get the currently selected nodes.
19964      * @return {Array} An array of HTMLElements
19965      */
19966     getSelectedNodes : function(){
19967         return this.selections;
19968     },
19969
19970     /**
19971      * Get the indexes of the selected nodes.
19972      * @return {Array}
19973      */
19974     getSelectedIndexes : function(){
19975         var indexes = [], s = this.selections;
19976         for(var i = 0, len = s.length; i < len; i++){
19977             indexes.push(s[i].nodeIndex);
19978         }
19979         return indexes;
19980     },
19981
19982     /**
19983      * Clear all selections
19984      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19985      */
19986     clearSelections : function(suppressEvent){
19987         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19988             this.cmp.elements = this.selections;
19989             this.cmp.removeClass(this.selectedClass);
19990             this.selections = [];
19991             if(!suppressEvent){
19992                 this.fireEvent("selectionchange", this, this.selections);
19993             }
19994         }
19995     },
19996
19997     /**
19998      * Returns true if the passed node is selected
19999      * @param {HTMLElement/Number} node The node or node index
20000      * @return {Boolean}
20001      */
20002     isSelected : function(node){
20003         var s = this.selections;
20004         if(s.length < 1){
20005             return false;
20006         }
20007         node = this.getNode(node);
20008         return s.indexOf(node) !== -1;
20009     },
20010
20011     /**
20012      * Selects nodes.
20013      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20014      * @param {Boolean} keepExisting (optional) true to keep existing selections
20015      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20016      */
20017     select : function(nodeInfo, keepExisting, suppressEvent){
20018         if(nodeInfo instanceof Array){
20019             if(!keepExisting){
20020                 this.clearSelections(true);
20021             }
20022             for(var i = 0, len = nodeInfo.length; i < len; i++){
20023                 this.select(nodeInfo[i], true, true);
20024             }
20025             return;
20026         } 
20027         var node = this.getNode(nodeInfo);
20028         if(!node || this.isSelected(node)){
20029             return; // already selected.
20030         }
20031         if(!keepExisting){
20032             this.clearSelections(true);
20033         }
20034         
20035         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20036             Roo.fly(node).addClass(this.selectedClass);
20037             this.selections.push(node);
20038             if(!suppressEvent){
20039                 this.fireEvent("selectionchange", this, this.selections);
20040             }
20041         }
20042         
20043         
20044     },
20045       /**
20046      * Unselects nodes.
20047      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20048      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20049      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20050      */
20051     unselect : function(nodeInfo, keepExisting, suppressEvent)
20052     {
20053         if(nodeInfo instanceof Array){
20054             Roo.each(this.selections, function(s) {
20055                 this.unselect(s, nodeInfo);
20056             }, this);
20057             return;
20058         }
20059         var node = this.getNode(nodeInfo);
20060         if(!node || !this.isSelected(node)){
20061             //Roo.log("not selected");
20062             return; // not selected.
20063         }
20064         // fireevent???
20065         var ns = [];
20066         Roo.each(this.selections, function(s) {
20067             if (s == node ) {
20068                 Roo.fly(node).removeClass(this.selectedClass);
20069
20070                 return;
20071             }
20072             ns.push(s);
20073         },this);
20074         
20075         this.selections= ns;
20076         this.fireEvent("selectionchange", this, this.selections);
20077     },
20078
20079     /**
20080      * Gets a template node.
20081      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20082      * @return {HTMLElement} The node or null if it wasn't found
20083      */
20084     getNode : function(nodeInfo){
20085         if(typeof nodeInfo == "string"){
20086             return document.getElementById(nodeInfo);
20087         }else if(typeof nodeInfo == "number"){
20088             return this.nodes[nodeInfo];
20089         }
20090         return nodeInfo;
20091     },
20092
20093     /**
20094      * Gets a range template nodes.
20095      * @param {Number} startIndex
20096      * @param {Number} endIndex
20097      * @return {Array} An array of nodes
20098      */
20099     getNodes : function(start, end){
20100         var ns = this.nodes;
20101         start = start || 0;
20102         end = typeof end == "undefined" ? ns.length - 1 : end;
20103         var nodes = [];
20104         if(start <= end){
20105             for(var i = start; i <= end; i++){
20106                 nodes.push(ns[i]);
20107             }
20108         } else{
20109             for(var i = start; i >= end; i--){
20110                 nodes.push(ns[i]);
20111             }
20112         }
20113         return nodes;
20114     },
20115
20116     /**
20117      * Finds the index of the passed node
20118      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20119      * @return {Number} The index of the node or -1
20120      */
20121     indexOf : function(node){
20122         node = this.getNode(node);
20123         if(typeof node.nodeIndex == "number"){
20124             return node.nodeIndex;
20125         }
20126         var ns = this.nodes;
20127         for(var i = 0, len = ns.length; i < len; i++){
20128             if(ns[i] == node){
20129                 return i;
20130             }
20131         }
20132         return -1;
20133     }
20134 });
20135 /*
20136  * - LGPL
20137  *
20138  * based on jquery fullcalendar
20139  * 
20140  */
20141
20142 Roo.bootstrap = Roo.bootstrap || {};
20143 /**
20144  * @class Roo.bootstrap.Calendar
20145  * @extends Roo.bootstrap.Component
20146  * Bootstrap Calendar class
20147  * @cfg {Boolean} loadMask (true|false) default false
20148  * @cfg {Object} header generate the user specific header of the calendar, default false
20149
20150  * @constructor
20151  * Create a new Container
20152  * @param {Object} config The config object
20153  */
20154
20155
20156
20157 Roo.bootstrap.Calendar = function(config){
20158     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20159      this.addEvents({
20160         /**
20161              * @event select
20162              * Fires when a date is selected
20163              * @param {DatePicker} this
20164              * @param {Date} date The selected date
20165              */
20166         'select': true,
20167         /**
20168              * @event monthchange
20169              * Fires when the displayed month changes 
20170              * @param {DatePicker} this
20171              * @param {Date} date The selected month
20172              */
20173         'monthchange': true,
20174         /**
20175              * @event evententer
20176              * Fires when mouse over an event
20177              * @param {Calendar} this
20178              * @param {event} Event
20179              */
20180         'evententer': true,
20181         /**
20182              * @event eventleave
20183              * Fires when the mouse leaves an
20184              * @param {Calendar} this
20185              * @param {event}
20186              */
20187         'eventleave': true,
20188         /**
20189              * @event eventclick
20190              * Fires when the mouse click an
20191              * @param {Calendar} this
20192              * @param {event}
20193              */
20194         'eventclick': true
20195         
20196     });
20197
20198 };
20199
20200 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20201     
20202      /**
20203      * @cfg {Number} startDay
20204      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20205      */
20206     startDay : 0,
20207     
20208     loadMask : false,
20209     
20210     header : false,
20211       
20212     getAutoCreate : function(){
20213         
20214         
20215         var fc_button = function(name, corner, style, content ) {
20216             return Roo.apply({},{
20217                 tag : 'span',
20218                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20219                          (corner.length ?
20220                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20221                             ''
20222                         ),
20223                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20224                 unselectable: 'on'
20225             });
20226         };
20227         
20228         var header = {};
20229         
20230         if(!this.header){
20231             header = {
20232                 tag : 'table',
20233                 cls : 'fc-header',
20234                 style : 'width:100%',
20235                 cn : [
20236                     {
20237                         tag: 'tr',
20238                         cn : [
20239                             {
20240                                 tag : 'td',
20241                                 cls : 'fc-header-left',
20242                                 cn : [
20243                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20244                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20245                                     { tag: 'span', cls: 'fc-header-space' },
20246                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20247
20248
20249                                 ]
20250                             },
20251
20252                             {
20253                                 tag : 'td',
20254                                 cls : 'fc-header-center',
20255                                 cn : [
20256                                     {
20257                                         tag: 'span',
20258                                         cls: 'fc-header-title',
20259                                         cn : {
20260                                             tag: 'H2',
20261                                             html : 'month / year'
20262                                         }
20263                                     }
20264
20265                                 ]
20266                             },
20267                             {
20268                                 tag : 'td',
20269                                 cls : 'fc-header-right',
20270                                 cn : [
20271                               /*      fc_button('month', 'left', '', 'month' ),
20272                                     fc_button('week', '', '', 'week' ),
20273                                     fc_button('day', 'right', '', 'day' )
20274                                 */    
20275
20276                                 ]
20277                             }
20278
20279                         ]
20280                     }
20281                 ]
20282             };
20283         }
20284         
20285         header = this.header;
20286         
20287        
20288         var cal_heads = function() {
20289             var ret = [];
20290             // fixme - handle this.
20291             
20292             for (var i =0; i < Date.dayNames.length; i++) {
20293                 var d = Date.dayNames[i];
20294                 ret.push({
20295                     tag: 'th',
20296                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20297                     html : d.substring(0,3)
20298                 });
20299                 
20300             }
20301             ret[0].cls += ' fc-first';
20302             ret[6].cls += ' fc-last';
20303             return ret;
20304         };
20305         var cal_cell = function(n) {
20306             return  {
20307                 tag: 'td',
20308                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20309                 cn : [
20310                     {
20311                         cn : [
20312                             {
20313                                 cls: 'fc-day-number',
20314                                 html: 'D'
20315                             },
20316                             {
20317                                 cls: 'fc-day-content',
20318                              
20319                                 cn : [
20320                                      {
20321                                         style: 'position: relative;' // height: 17px;
20322                                     }
20323                                 ]
20324                             }
20325                             
20326                             
20327                         ]
20328                     }
20329                 ]
20330                 
20331             }
20332         };
20333         var cal_rows = function() {
20334             
20335             var ret = [];
20336             for (var r = 0; r < 6; r++) {
20337                 var row= {
20338                     tag : 'tr',
20339                     cls : 'fc-week',
20340                     cn : []
20341                 };
20342                 
20343                 for (var i =0; i < Date.dayNames.length; i++) {
20344                     var d = Date.dayNames[i];
20345                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20346
20347                 }
20348                 row.cn[0].cls+=' fc-first';
20349                 row.cn[0].cn[0].style = 'min-height:90px';
20350                 row.cn[6].cls+=' fc-last';
20351                 ret.push(row);
20352                 
20353             }
20354             ret[0].cls += ' fc-first';
20355             ret[4].cls += ' fc-prev-last';
20356             ret[5].cls += ' fc-last';
20357             return ret;
20358             
20359         };
20360         
20361         var cal_table = {
20362             tag: 'table',
20363             cls: 'fc-border-separate',
20364             style : 'width:100%',
20365             cellspacing  : 0,
20366             cn : [
20367                 { 
20368                     tag: 'thead',
20369                     cn : [
20370                         { 
20371                             tag: 'tr',
20372                             cls : 'fc-first fc-last',
20373                             cn : cal_heads()
20374                         }
20375                     ]
20376                 },
20377                 { 
20378                     tag: 'tbody',
20379                     cn : cal_rows()
20380                 }
20381                   
20382             ]
20383         };
20384          
20385          var cfg = {
20386             cls : 'fc fc-ltr',
20387             cn : [
20388                 header,
20389                 {
20390                     cls : 'fc-content',
20391                     style : "position: relative;",
20392                     cn : [
20393                         {
20394                             cls : 'fc-view fc-view-month fc-grid',
20395                             style : 'position: relative',
20396                             unselectable : 'on',
20397                             cn : [
20398                                 {
20399                                     cls : 'fc-event-container',
20400                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20401                                 },
20402                                 cal_table
20403                             ]
20404                         }
20405                     ]
20406     
20407                 }
20408            ] 
20409             
20410         };
20411         
20412          
20413         
20414         return cfg;
20415     },
20416     
20417     
20418     initEvents : function()
20419     {
20420         if(!this.store){
20421             throw "can not find store for calendar";
20422         }
20423         
20424         var mark = {
20425             tag: "div",
20426             cls:"x-dlg-mask",
20427             style: "text-align:center",
20428             cn: [
20429                 {
20430                     tag: "div",
20431                     style: "background-color:white;width:50%;margin:250 auto",
20432                     cn: [
20433                         {
20434                             tag: "img",
20435                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20436                         },
20437                         {
20438                             tag: "span",
20439                             html: "Loading"
20440                         }
20441                         
20442                     ]
20443                 }
20444             ]
20445         };
20446         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20447         
20448         var size = this.el.select('.fc-content', true).first().getSize();
20449         this.maskEl.setSize(size.width, size.height);
20450         this.maskEl.enableDisplayMode("block");
20451         if(!this.loadMask){
20452             this.maskEl.hide();
20453         }
20454         
20455         this.store = Roo.factory(this.store, Roo.data);
20456         this.store.on('load', this.onLoad, this);
20457         this.store.on('beforeload', this.onBeforeLoad, this);
20458         
20459         this.resize();
20460         
20461         this.cells = this.el.select('.fc-day',true);
20462         //Roo.log(this.cells);
20463         this.textNodes = this.el.query('.fc-day-number');
20464         this.cells.addClassOnOver('fc-state-hover');
20465         
20466         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20467         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20468         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20469         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20470         
20471         this.on('monthchange', this.onMonthChange, this);
20472         
20473         this.update(new Date().clearTime());
20474     },
20475     
20476     resize : function() {
20477         var sz  = this.el.getSize();
20478         
20479         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20480         this.el.select('.fc-day-content div',true).setHeight(34);
20481     },
20482     
20483     
20484     // private
20485     showPrevMonth : function(e){
20486         this.update(this.activeDate.add("mo", -1));
20487     },
20488     showToday : function(e){
20489         this.update(new Date().clearTime());
20490     },
20491     // private
20492     showNextMonth : function(e){
20493         this.update(this.activeDate.add("mo", 1));
20494     },
20495
20496     // private
20497     showPrevYear : function(){
20498         this.update(this.activeDate.add("y", -1));
20499     },
20500
20501     // private
20502     showNextYear : function(){
20503         this.update(this.activeDate.add("y", 1));
20504     },
20505
20506     
20507    // private
20508     update : function(date)
20509     {
20510         var vd = this.activeDate;
20511         this.activeDate = date;
20512 //        if(vd && this.el){
20513 //            var t = date.getTime();
20514 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20515 //                Roo.log('using add remove');
20516 //                
20517 //                this.fireEvent('monthchange', this, date);
20518 //                
20519 //                this.cells.removeClass("fc-state-highlight");
20520 //                this.cells.each(function(c){
20521 //                   if(c.dateValue == t){
20522 //                       c.addClass("fc-state-highlight");
20523 //                       setTimeout(function(){
20524 //                            try{c.dom.firstChild.focus();}catch(e){}
20525 //                       }, 50);
20526 //                       return false;
20527 //                   }
20528 //                   return true;
20529 //                });
20530 //                return;
20531 //            }
20532 //        }
20533         
20534         var days = date.getDaysInMonth();
20535         
20536         var firstOfMonth = date.getFirstDateOfMonth();
20537         var startingPos = firstOfMonth.getDay()-this.startDay;
20538         
20539         if(startingPos < this.startDay){
20540             startingPos += 7;
20541         }
20542         
20543         var pm = date.add(Date.MONTH, -1);
20544         var prevStart = pm.getDaysInMonth()-startingPos;
20545 //        
20546         this.cells = this.el.select('.fc-day',true);
20547         this.textNodes = this.el.query('.fc-day-number');
20548         this.cells.addClassOnOver('fc-state-hover');
20549         
20550         var cells = this.cells.elements;
20551         var textEls = this.textNodes;
20552         
20553         Roo.each(cells, function(cell){
20554             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20555         });
20556         
20557         days += startingPos;
20558
20559         // convert everything to numbers so it's fast
20560         var day = 86400000;
20561         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20562         //Roo.log(d);
20563         //Roo.log(pm);
20564         //Roo.log(prevStart);
20565         
20566         var today = new Date().clearTime().getTime();
20567         var sel = date.clearTime().getTime();
20568         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20569         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20570         var ddMatch = this.disabledDatesRE;
20571         var ddText = this.disabledDatesText;
20572         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20573         var ddaysText = this.disabledDaysText;
20574         var format = this.format;
20575         
20576         var setCellClass = function(cal, cell){
20577             cell.row = 0;
20578             cell.events = [];
20579             cell.more = [];
20580             //Roo.log('set Cell Class');
20581             cell.title = "";
20582             var t = d.getTime();
20583             
20584             //Roo.log(d);
20585             
20586             cell.dateValue = t;
20587             if(t == today){
20588                 cell.className += " fc-today";
20589                 cell.className += " fc-state-highlight";
20590                 cell.title = cal.todayText;
20591             }
20592             if(t == sel){
20593                 // disable highlight in other month..
20594                 //cell.className += " fc-state-highlight";
20595                 
20596             }
20597             // disabling
20598             if(t < min) {
20599                 cell.className = " fc-state-disabled";
20600                 cell.title = cal.minText;
20601                 return;
20602             }
20603             if(t > max) {
20604                 cell.className = " fc-state-disabled";
20605                 cell.title = cal.maxText;
20606                 return;
20607             }
20608             if(ddays){
20609                 if(ddays.indexOf(d.getDay()) != -1){
20610                     cell.title = ddaysText;
20611                     cell.className = " fc-state-disabled";
20612                 }
20613             }
20614             if(ddMatch && format){
20615                 var fvalue = d.dateFormat(format);
20616                 if(ddMatch.test(fvalue)){
20617                     cell.title = ddText.replace("%0", fvalue);
20618                     cell.className = " fc-state-disabled";
20619                 }
20620             }
20621             
20622             if (!cell.initialClassName) {
20623                 cell.initialClassName = cell.dom.className;
20624             }
20625             
20626             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20627         };
20628
20629         var i = 0;
20630         
20631         for(; i < startingPos; i++) {
20632             textEls[i].innerHTML = (++prevStart);
20633             d.setDate(d.getDate()+1);
20634             
20635             cells[i].className = "fc-past fc-other-month";
20636             setCellClass(this, cells[i]);
20637         }
20638         
20639         var intDay = 0;
20640         
20641         for(; i < days; i++){
20642             intDay = i - startingPos + 1;
20643             textEls[i].innerHTML = (intDay);
20644             d.setDate(d.getDate()+1);
20645             
20646             cells[i].className = ''; // "x-date-active";
20647             setCellClass(this, cells[i]);
20648         }
20649         var extraDays = 0;
20650         
20651         for(; i < 42; i++) {
20652             textEls[i].innerHTML = (++extraDays);
20653             d.setDate(d.getDate()+1);
20654             
20655             cells[i].className = "fc-future fc-other-month";
20656             setCellClass(this, cells[i]);
20657         }
20658         
20659         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20660         
20661         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20662         
20663         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20664         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20665         
20666         if(totalRows != 6){
20667             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20668             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20669         }
20670         
20671         this.fireEvent('monthchange', this, date);
20672         
20673         
20674         /*
20675         if(!this.internalRender){
20676             var main = this.el.dom.firstChild;
20677             var w = main.offsetWidth;
20678             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20679             Roo.fly(main).setWidth(w);
20680             this.internalRender = true;
20681             // opera does not respect the auto grow header center column
20682             // then, after it gets a width opera refuses to recalculate
20683             // without a second pass
20684             if(Roo.isOpera && !this.secondPass){
20685                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20686                 this.secondPass = true;
20687                 this.update.defer(10, this, [date]);
20688             }
20689         }
20690         */
20691         
20692     },
20693     
20694     findCell : function(dt) {
20695         dt = dt.clearTime().getTime();
20696         var ret = false;
20697         this.cells.each(function(c){
20698             //Roo.log("check " +c.dateValue + '?=' + dt);
20699             if(c.dateValue == dt){
20700                 ret = c;
20701                 return false;
20702             }
20703             return true;
20704         });
20705         
20706         return ret;
20707     },
20708     
20709     findCells : function(ev) {
20710         var s = ev.start.clone().clearTime().getTime();
20711        // Roo.log(s);
20712         var e= ev.end.clone().clearTime().getTime();
20713        // Roo.log(e);
20714         var ret = [];
20715         this.cells.each(function(c){
20716              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20717             
20718             if(c.dateValue > e){
20719                 return ;
20720             }
20721             if(c.dateValue < s){
20722                 return ;
20723             }
20724             ret.push(c);
20725         });
20726         
20727         return ret;    
20728     },
20729     
20730 //    findBestRow: function(cells)
20731 //    {
20732 //        var ret = 0;
20733 //        
20734 //        for (var i =0 ; i < cells.length;i++) {
20735 //            ret  = Math.max(cells[i].rows || 0,ret);
20736 //        }
20737 //        return ret;
20738 //        
20739 //    },
20740     
20741     
20742     addItem : function(ev)
20743     {
20744         // look for vertical location slot in
20745         var cells = this.findCells(ev);
20746         
20747 //        ev.row = this.findBestRow(cells);
20748         
20749         // work out the location.
20750         
20751         var crow = false;
20752         var rows = [];
20753         for(var i =0; i < cells.length; i++) {
20754             
20755             cells[i].row = cells[0].row;
20756             
20757             if(i == 0){
20758                 cells[i].row = cells[i].row + 1;
20759             }
20760             
20761             if (!crow) {
20762                 crow = {
20763                     start : cells[i],
20764                     end :  cells[i]
20765                 };
20766                 continue;
20767             }
20768             if (crow.start.getY() == cells[i].getY()) {
20769                 // on same row.
20770                 crow.end = cells[i];
20771                 continue;
20772             }
20773             // different row.
20774             rows.push(crow);
20775             crow = {
20776                 start: cells[i],
20777                 end : cells[i]
20778             };
20779             
20780         }
20781         
20782         rows.push(crow);
20783         ev.els = [];
20784         ev.rows = rows;
20785         ev.cells = cells;
20786         
20787         cells[0].events.push(ev);
20788         
20789         this.calevents.push(ev);
20790     },
20791     
20792     clearEvents: function() {
20793         
20794         if(!this.calevents){
20795             return;
20796         }
20797         
20798         Roo.each(this.cells.elements, function(c){
20799             c.row = 0;
20800             c.events = [];
20801             c.more = [];
20802         });
20803         
20804         Roo.each(this.calevents, function(e) {
20805             Roo.each(e.els, function(el) {
20806                 el.un('mouseenter' ,this.onEventEnter, this);
20807                 el.un('mouseleave' ,this.onEventLeave, this);
20808                 el.remove();
20809             },this);
20810         },this);
20811         
20812         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20813             e.remove();
20814         });
20815         
20816     },
20817     
20818     renderEvents: function()
20819     {   
20820         var _this = this;
20821         
20822         this.cells.each(function(c) {
20823             
20824             if(c.row < 5){
20825                 return;
20826             }
20827             
20828             var ev = c.events;
20829             
20830             var r = 4;
20831             if(c.row != c.events.length){
20832                 r = 4 - (4 - (c.row - c.events.length));
20833             }
20834             
20835             c.events = ev.slice(0, r);
20836             c.more = ev.slice(r);
20837             
20838             if(c.more.length && c.more.length == 1){
20839                 c.events.push(c.more.pop());
20840             }
20841             
20842             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20843             
20844         });
20845             
20846         this.cells.each(function(c) {
20847             
20848             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20849             
20850             
20851             for (var e = 0; e < c.events.length; e++){
20852                 var ev = c.events[e];
20853                 var rows = ev.rows;
20854                 
20855                 for(var i = 0; i < rows.length; i++) {
20856                 
20857                     // how many rows should it span..
20858
20859                     var  cfg = {
20860                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20861                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20862
20863                         unselectable : "on",
20864                         cn : [
20865                             {
20866                                 cls: 'fc-event-inner',
20867                                 cn : [
20868     //                                {
20869     //                                  tag:'span',
20870     //                                  cls: 'fc-event-time',
20871     //                                  html : cells.length > 1 ? '' : ev.time
20872     //                                },
20873                                     {
20874                                       tag:'span',
20875                                       cls: 'fc-event-title',
20876                                       html : String.format('{0}', ev.title)
20877                                     }
20878
20879
20880                                 ]
20881                             },
20882                             {
20883                                 cls: 'ui-resizable-handle ui-resizable-e',
20884                                 html : '&nbsp;&nbsp;&nbsp'
20885                             }
20886
20887                         ]
20888                     };
20889
20890                     if (i == 0) {
20891                         cfg.cls += ' fc-event-start';
20892                     }
20893                     if ((i+1) == rows.length) {
20894                         cfg.cls += ' fc-event-end';
20895                     }
20896
20897                     var ctr = _this.el.select('.fc-event-container',true).first();
20898                     var cg = ctr.createChild(cfg);
20899
20900                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20901                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20902
20903                     var r = (c.more.length) ? 1 : 0;
20904                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20905                     cg.setWidth(ebox.right - sbox.x -2);
20906
20907                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20908                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20909                     cg.on('click', _this.onEventClick, _this, ev);
20910
20911                     ev.els.push(cg);
20912                     
20913                 }
20914                 
20915             }
20916             
20917             
20918             if(c.more.length){
20919                 var  cfg = {
20920                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20921                     style : 'position: absolute',
20922                     unselectable : "on",
20923                     cn : [
20924                         {
20925                             cls: 'fc-event-inner',
20926                             cn : [
20927                                 {
20928                                   tag:'span',
20929                                   cls: 'fc-event-title',
20930                                   html : 'More'
20931                                 }
20932
20933
20934                             ]
20935                         },
20936                         {
20937                             cls: 'ui-resizable-handle ui-resizable-e',
20938                             html : '&nbsp;&nbsp;&nbsp'
20939                         }
20940
20941                     ]
20942                 };
20943
20944                 var ctr = _this.el.select('.fc-event-container',true).first();
20945                 var cg = ctr.createChild(cfg);
20946
20947                 var sbox = c.select('.fc-day-content',true).first().getBox();
20948                 var ebox = c.select('.fc-day-content',true).first().getBox();
20949                 //Roo.log(cg);
20950                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20951                 cg.setWidth(ebox.right - sbox.x -2);
20952
20953                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20954                 
20955             }
20956             
20957         });
20958         
20959         
20960         
20961     },
20962     
20963     onEventEnter: function (e, el,event,d) {
20964         this.fireEvent('evententer', this, el, event);
20965     },
20966     
20967     onEventLeave: function (e, el,event,d) {
20968         this.fireEvent('eventleave', this, el, event);
20969     },
20970     
20971     onEventClick: function (e, el,event,d) {
20972         this.fireEvent('eventclick', this, el, event);
20973     },
20974     
20975     onMonthChange: function () {
20976         this.store.load();
20977     },
20978     
20979     onMoreEventClick: function(e, el, more)
20980     {
20981         var _this = this;
20982         
20983         this.calpopover.placement = 'right';
20984         this.calpopover.setTitle('More');
20985         
20986         this.calpopover.setContent('');
20987         
20988         var ctr = this.calpopover.el.select('.popover-content', true).first();
20989         
20990         Roo.each(more, function(m){
20991             var cfg = {
20992                 cls : 'fc-event-hori fc-event-draggable',
20993                 html : m.title
20994             };
20995             var cg = ctr.createChild(cfg);
20996             
20997             cg.on('click', _this.onEventClick, _this, m);
20998         });
20999         
21000         this.calpopover.show(el);
21001         
21002         
21003     },
21004     
21005     onLoad: function () 
21006     {   
21007         this.calevents = [];
21008         var cal = this;
21009         
21010         if(this.store.getCount() > 0){
21011             this.store.data.each(function(d){
21012                cal.addItem({
21013                     id : d.data.id,
21014                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21015                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21016                     time : d.data.start_time,
21017                     title : d.data.title,
21018                     description : d.data.description,
21019                     venue : d.data.venue
21020                 });
21021             });
21022         }
21023         
21024         this.renderEvents();
21025         
21026         if(this.calevents.length && this.loadMask){
21027             this.maskEl.hide();
21028         }
21029     },
21030     
21031     onBeforeLoad: function()
21032     {
21033         this.clearEvents();
21034         if(this.loadMask){
21035             this.maskEl.show();
21036         }
21037     }
21038 });
21039
21040  
21041  /*
21042  * - LGPL
21043  *
21044  * element
21045  * 
21046  */
21047
21048 /**
21049  * @class Roo.bootstrap.Popover
21050  * @extends Roo.bootstrap.Component
21051  * Bootstrap Popover class
21052  * @cfg {String} html contents of the popover   (or false to use children..)
21053  * @cfg {String} title of popover (or false to hide)
21054  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21055  * @cfg {String} trigger click || hover (or false to trigger manually)
21056  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21057  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21058  *      - if false and it has a 'parent' then it will be automatically added to that element
21059  *      - if string - Roo.get  will be called 
21060  * @cfg {Number} delay - delay before showing
21061  
21062  * @constructor
21063  * Create a new Popover
21064  * @param {Object} config The config object
21065  */
21066
21067 Roo.bootstrap.Popover = function(config){
21068     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21069     
21070     this.addEvents({
21071         // raw events
21072          /**
21073          * @event show
21074          * After the popover show
21075          * 
21076          * @param {Roo.bootstrap.Popover} this
21077          */
21078         "show" : true,
21079         /**
21080          * @event hide
21081          * After the popover hide
21082          * 
21083          * @param {Roo.bootstrap.Popover} this
21084          */
21085         "hide" : true
21086     });
21087 };
21088
21089 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21090     
21091     title: false,
21092     html: false,
21093     
21094     placement : 'right',
21095     trigger : 'hover', // hover
21096     modal : false,
21097     delay : 0,
21098     
21099     over: false,
21100     
21101     can_build_overlaid : false,
21102     
21103     maskEl : false, // the mask element
21104     headerEl : false,
21105     contentEl : false,
21106     alignEl : false, // when show is called with an element - this get's stored.
21107     
21108     getChildContainer : function()
21109     {
21110         return this.contentEl;
21111         
21112     },
21113     getPopoverHeader : function()
21114     {
21115         this.title = true; // flag not to hide it..
21116         this.headerEl.addClass('p-0');
21117         return this.headerEl
21118     },
21119     
21120     
21121     getAutoCreate : function(){
21122          
21123         var cfg = {
21124            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21125            style: 'display:block',
21126            cn : [
21127                 {
21128                     cls : 'arrow'
21129                 },
21130                 {
21131                     cls : 'popover-inner ',
21132                     cn : [
21133                         {
21134                             tag: 'h3',
21135                             cls: 'popover-title popover-header',
21136                             html : this.title === false ? '' : this.title
21137                         },
21138                         {
21139                             cls : 'popover-content popover-body '  + (this.cls || ''),
21140                             html : this.html || ''
21141                         }
21142                     ]
21143                     
21144                 }
21145            ]
21146         };
21147         
21148         return cfg;
21149     },
21150     /**
21151      * @param {string} the title
21152      */
21153     setTitle: function(str)
21154     {
21155         this.title = str;
21156         if (this.el) {
21157             this.headerEl.dom.innerHTML = str;
21158         }
21159         
21160     },
21161     /**
21162      * @param {string} the body content
21163      */
21164     setContent: function(str)
21165     {
21166         this.html = str;
21167         if (this.contentEl) {
21168             this.contentEl.dom.innerHTML = str;
21169         }
21170         
21171     },
21172     // as it get's added to the bottom of the page.
21173     onRender : function(ct, position)
21174     {
21175         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21176         
21177         
21178         
21179         if(!this.el){
21180             var cfg = Roo.apply({},  this.getAutoCreate());
21181             cfg.id = Roo.id();
21182             
21183             if (this.cls) {
21184                 cfg.cls += ' ' + this.cls;
21185             }
21186             if (this.style) {
21187                 cfg.style = this.style;
21188             }
21189             //Roo.log("adding to ");
21190             this.el = Roo.get(document.body).createChild(cfg, position);
21191 //            Roo.log(this.el);
21192         }
21193         
21194         this.contentEl = this.el.select('.popover-content',true).first();
21195         this.headerEl =  this.el.select('.popover-title',true).first();
21196         
21197         var nitems = [];
21198         if(typeof(this.items) != 'undefined'){
21199             var items = this.items;
21200             delete this.items;
21201
21202             for(var i =0;i < items.length;i++) {
21203                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21204             }
21205         }
21206
21207         this.items = nitems;
21208         
21209         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21210         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21211         
21212         
21213         
21214         this.initEvents();
21215     },
21216     
21217     resizeMask : function()
21218     {
21219         this.maskEl.setSize(
21220             Roo.lib.Dom.getViewWidth(true),
21221             Roo.lib.Dom.getViewHeight(true)
21222         );
21223     },
21224     
21225     initEvents : function()
21226     {
21227         
21228         if (!this.modal) { 
21229             Roo.bootstrap.Popover.register(this);
21230         }
21231          
21232         this.arrowEl = this.el.select('.arrow',true).first();
21233         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21234         this.el.enableDisplayMode('block');
21235         this.el.hide();
21236  
21237         
21238         if (this.over === false && !this.parent()) {
21239             return; 
21240         }
21241         if (this.triggers === false) {
21242             return;
21243         }
21244          
21245         // support parent
21246         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21247         var triggers = this.trigger ? this.trigger.split(' ') : [];
21248         Roo.each(triggers, function(trigger) {
21249         
21250             if (trigger == 'click') {
21251                 on_el.on('click', this.toggle, this);
21252             } else if (trigger != 'manual') {
21253                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21254                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21255       
21256                 on_el.on(eventIn  ,this.enter, this);
21257                 on_el.on(eventOut, this.leave, this);
21258             }
21259         }, this);
21260     },
21261     
21262     
21263     // private
21264     timeout : null,
21265     hoverState : null,
21266     
21267     toggle : function () {
21268         this.hoverState == 'in' ? this.leave() : this.enter();
21269     },
21270     
21271     enter : function () {
21272         
21273         clearTimeout(this.timeout);
21274     
21275         this.hoverState = 'in';
21276     
21277         if (!this.delay || !this.delay.show) {
21278             this.show();
21279             return;
21280         }
21281         var _t = this;
21282         this.timeout = setTimeout(function () {
21283             if (_t.hoverState == 'in') {
21284                 _t.show();
21285             }
21286         }, this.delay.show)
21287     },
21288     
21289     leave : function() {
21290         clearTimeout(this.timeout);
21291     
21292         this.hoverState = 'out';
21293     
21294         if (!this.delay || !this.delay.hide) {
21295             this.hide();
21296             return;
21297         }
21298         var _t = this;
21299         this.timeout = setTimeout(function () {
21300             if (_t.hoverState == 'out') {
21301                 _t.hide();
21302             }
21303         }, this.delay.hide)
21304     },
21305     /**
21306      * Show the popover
21307      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21308      * @param {string} (left|right|top|bottom) position
21309      */
21310     show : function (on_el, placement)
21311     {
21312         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21313         on_el = on_el || false; // default to false
21314          
21315         if (!on_el) {
21316             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21317                 on_el = this.parent().el;
21318             } else if (this.over) {
21319                 on_el = Roo.get(this.over);
21320             }
21321             
21322         }
21323         
21324         this.alignEl = Roo.get( on_el );
21325
21326         if (!this.el) {
21327             this.render(document.body);
21328         }
21329         
21330         
21331          
21332         
21333         if (this.title === false) {
21334             this.headerEl.hide();
21335         }
21336         
21337        
21338         this.el.show();
21339         this.el.dom.style.display = 'block';
21340          
21341  
21342         if (this.alignEl) {
21343             this.updatePosition(this.placement, true);
21344              
21345         } else {
21346             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21347             var es = this.el.getSize();
21348             var x = Roo.lib.Dom.getViewWidth()/2;
21349             var y = Roo.lib.Dom.getViewHeight()/2;
21350             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21351             
21352         }
21353
21354         
21355         //var arrow = this.el.select('.arrow',true).first();
21356         //arrow.set(align[2], 
21357         
21358         this.el.addClass('in');
21359         
21360          
21361         
21362         this.hoverState = 'in';
21363         
21364         if (this.modal) {
21365             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21366             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21367             this.maskEl.dom.style.display = 'block';
21368             this.maskEl.addClass('show');
21369         }
21370         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21371  
21372         this.fireEvent('show', this);
21373         
21374     },
21375     /**
21376      * fire this manually after loading a grid in the table for example
21377      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21378      * @param {Boolean} try and move it if we cant get right position.
21379      */
21380     updatePosition : function(placement, try_move)
21381     {
21382         // allow for calling with no parameters
21383         placement = placement   ? placement :  this.placement;
21384         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21385         
21386         this.el.removeClass([
21387             'fade','top','bottom', 'left', 'right','in',
21388             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21389         ]);
21390         this.el.addClass(placement + ' bs-popover-' + placement);
21391         
21392         if (!this.alignEl ) {
21393             return false;
21394         }
21395         
21396         switch (placement) {
21397             case 'right':
21398                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21399                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21400                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21401                     //normal display... or moved up/down.
21402                     this.el.setXY(offset);
21403                     var xy = this.alignEl.getAnchorXY('tr', false);
21404                     xy[0]+=2;xy[1]+=5;
21405                     this.arrowEl.setXY(xy);
21406                     return true;
21407                 }
21408                 // continue through...
21409                 return this.updatePosition('left', false);
21410                 
21411             
21412             case 'left':
21413                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21414                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21415                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21416                     //normal display... or moved up/down.
21417                     this.el.setXY(offset);
21418                     var xy = this.alignEl.getAnchorXY('tl', false);
21419                     xy[0]-=10;xy[1]+=5; // << fix me
21420                     this.arrowEl.setXY(xy);
21421                     return true;
21422                 }
21423                 // call self...
21424                 return this.updatePosition('right', false);
21425             
21426             case 'top':
21427                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21428                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21429                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21430                     //normal display... or moved up/down.
21431                     this.el.setXY(offset);
21432                     var xy = this.alignEl.getAnchorXY('t', false);
21433                     xy[1]-=10; // << fix me
21434                     this.arrowEl.setXY(xy);
21435                     return true;
21436                 }
21437                 // fall through
21438                return this.updatePosition('bottom', false);
21439             
21440             case 'bottom':
21441                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21442                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21443                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21444                     //normal display... or moved up/down.
21445                     this.el.setXY(offset);
21446                     var xy = this.alignEl.getAnchorXY('b', false);
21447                      xy[1]+=2; // << fix me
21448                     this.arrowEl.setXY(xy);
21449                     return true;
21450                 }
21451                 // fall through
21452                 return this.updatePosition('top', false);
21453                 
21454             
21455         }
21456         
21457         
21458         return false;
21459     },
21460     
21461     hide : function()
21462     {
21463         this.el.setXY([0,0]);
21464         this.el.removeClass('in');
21465         this.el.hide();
21466         this.hoverState = null;
21467         this.maskEl.hide(); // always..
21468         this.fireEvent('hide', this);
21469     }
21470     
21471 });
21472
21473
21474 Roo.apply(Roo.bootstrap.Popover, {
21475
21476     alignment : {
21477         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21478         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21479         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21480         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21481     },
21482     
21483     zIndex : 20001,
21484
21485     clickHander : false,
21486     
21487     
21488
21489     onMouseDown : function(e)
21490     {
21491         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21492             /// what is nothing is showing..
21493             this.hideAll();
21494         }
21495          
21496     },
21497     
21498     
21499     popups : [],
21500     
21501     register : function(popup)
21502     {
21503         if (!Roo.bootstrap.Popover.clickHandler) {
21504             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21505         }
21506         // hide other popups.
21507         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21508         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21509         this.hideAll(); //<< why?
21510         //this.popups.push(popup);
21511     },
21512     hideAll : function()
21513     {
21514         this.popups.forEach(function(p) {
21515             p.hide();
21516         });
21517     },
21518     onShow : function() {
21519         Roo.bootstrap.Popover.popups.push(this);
21520     },
21521     onHide : function() {
21522         Roo.bootstrap.Popover.popups.remove(this);
21523     } 
21524
21525 });/*
21526  * - LGPL
21527  *
21528  * Card header - holder for the card header elements.
21529  * 
21530  */
21531
21532 /**
21533  * @class Roo.bootstrap.PopoverNav
21534  * @extends Roo.bootstrap.NavGroup
21535  * Bootstrap Popover header navigation class
21536  * @constructor
21537  * Create a new Popover Header Navigation 
21538  * @param {Object} config The config object
21539  */
21540
21541 Roo.bootstrap.PopoverNav = function(config){
21542     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21543 };
21544
21545 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21546     
21547     
21548     container_method : 'getPopoverHeader' 
21549     
21550      
21551     
21552     
21553    
21554 });
21555
21556  
21557
21558  /*
21559  * - LGPL
21560  *
21561  * Progress
21562  * 
21563  */
21564
21565 /**
21566  * @class Roo.bootstrap.Progress
21567  * @extends Roo.bootstrap.Component
21568  * Bootstrap Progress class
21569  * @cfg {Boolean} striped striped of the progress bar
21570  * @cfg {Boolean} active animated of the progress bar
21571  * 
21572  * 
21573  * @constructor
21574  * Create a new Progress
21575  * @param {Object} config The config object
21576  */
21577
21578 Roo.bootstrap.Progress = function(config){
21579     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21580 };
21581
21582 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21583     
21584     striped : false,
21585     active: false,
21586     
21587     getAutoCreate : function(){
21588         var cfg = {
21589             tag: 'div',
21590             cls: 'progress'
21591         };
21592         
21593         
21594         if(this.striped){
21595             cfg.cls += ' progress-striped';
21596         }
21597       
21598         if(this.active){
21599             cfg.cls += ' active';
21600         }
21601         
21602         
21603         return cfg;
21604     }
21605    
21606 });
21607
21608  
21609
21610  /*
21611  * - LGPL
21612  *
21613  * ProgressBar
21614  * 
21615  */
21616
21617 /**
21618  * @class Roo.bootstrap.ProgressBar
21619  * @extends Roo.bootstrap.Component
21620  * Bootstrap ProgressBar class
21621  * @cfg {Number} aria_valuenow aria-value now
21622  * @cfg {Number} aria_valuemin aria-value min
21623  * @cfg {Number} aria_valuemax aria-value max
21624  * @cfg {String} label label for the progress bar
21625  * @cfg {String} panel (success | info | warning | danger )
21626  * @cfg {String} role role of the progress bar
21627  * @cfg {String} sr_only text
21628  * 
21629  * 
21630  * @constructor
21631  * Create a new ProgressBar
21632  * @param {Object} config The config object
21633  */
21634
21635 Roo.bootstrap.ProgressBar = function(config){
21636     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21637 };
21638
21639 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21640     
21641     aria_valuenow : 0,
21642     aria_valuemin : 0,
21643     aria_valuemax : 100,
21644     label : false,
21645     panel : false,
21646     role : false,
21647     sr_only: false,
21648     
21649     getAutoCreate : function()
21650     {
21651         
21652         var cfg = {
21653             tag: 'div',
21654             cls: 'progress-bar',
21655             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21656         };
21657         
21658         if(this.sr_only){
21659             cfg.cn = {
21660                 tag: 'span',
21661                 cls: 'sr-only',
21662                 html: this.sr_only
21663             }
21664         }
21665         
21666         if(this.role){
21667             cfg.role = this.role;
21668         }
21669         
21670         if(this.aria_valuenow){
21671             cfg['aria-valuenow'] = this.aria_valuenow;
21672         }
21673         
21674         if(this.aria_valuemin){
21675             cfg['aria-valuemin'] = this.aria_valuemin;
21676         }
21677         
21678         if(this.aria_valuemax){
21679             cfg['aria-valuemax'] = this.aria_valuemax;
21680         }
21681         
21682         if(this.label && !this.sr_only){
21683             cfg.html = this.label;
21684         }
21685         
21686         if(this.panel){
21687             cfg.cls += ' progress-bar-' + this.panel;
21688         }
21689         
21690         return cfg;
21691     },
21692     
21693     update : function(aria_valuenow)
21694     {
21695         this.aria_valuenow = aria_valuenow;
21696         
21697         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21698     }
21699    
21700 });
21701
21702  
21703
21704  /*
21705  * - LGPL
21706  *
21707  * column
21708  * 
21709  */
21710
21711 /**
21712  * @class Roo.bootstrap.TabGroup
21713  * @extends Roo.bootstrap.Column
21714  * Bootstrap Column class
21715  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21716  * @cfg {Boolean} carousel true to make the group behave like a carousel
21717  * @cfg {Boolean} bullets show bullets for the panels
21718  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21719  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21720  * @cfg {Boolean} showarrow (true|false) show arrow default true
21721  * 
21722  * @constructor
21723  * Create a new TabGroup
21724  * @param {Object} config The config object
21725  */
21726
21727 Roo.bootstrap.TabGroup = function(config){
21728     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21729     if (!this.navId) {
21730         this.navId = Roo.id();
21731     }
21732     this.tabs = [];
21733     Roo.bootstrap.TabGroup.register(this);
21734     
21735 };
21736
21737 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21738     
21739     carousel : false,
21740     transition : false,
21741     bullets : 0,
21742     timer : 0,
21743     autoslide : false,
21744     slideFn : false,
21745     slideOnTouch : false,
21746     showarrow : true,
21747     
21748     getAutoCreate : function()
21749     {
21750         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21751         
21752         cfg.cls += ' tab-content';
21753         
21754         if (this.carousel) {
21755             cfg.cls += ' carousel slide';
21756             
21757             cfg.cn = [{
21758                cls : 'carousel-inner',
21759                cn : []
21760             }];
21761         
21762             if(this.bullets  && !Roo.isTouch){
21763                 
21764                 var bullets = {
21765                     cls : 'carousel-bullets',
21766                     cn : []
21767                 };
21768                
21769                 if(this.bullets_cls){
21770                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21771                 }
21772                 
21773                 bullets.cn.push({
21774                     cls : 'clear'
21775                 });
21776                 
21777                 cfg.cn[0].cn.push(bullets);
21778             }
21779             
21780             if(this.showarrow){
21781                 cfg.cn[0].cn.push({
21782                     tag : 'div',
21783                     class : 'carousel-arrow',
21784                     cn : [
21785                         {
21786                             tag : 'div',
21787                             class : 'carousel-prev',
21788                             cn : [
21789                                 {
21790                                     tag : 'i',
21791                                     class : 'fa fa-chevron-left'
21792                                 }
21793                             ]
21794                         },
21795                         {
21796                             tag : 'div',
21797                             class : 'carousel-next',
21798                             cn : [
21799                                 {
21800                                     tag : 'i',
21801                                     class : 'fa fa-chevron-right'
21802                                 }
21803                             ]
21804                         }
21805                     ]
21806                 });
21807             }
21808             
21809         }
21810         
21811         return cfg;
21812     },
21813     
21814     initEvents:  function()
21815     {
21816 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21817 //            this.el.on("touchstart", this.onTouchStart, this);
21818 //        }
21819         
21820         if(this.autoslide){
21821             var _this = this;
21822             
21823             this.slideFn = window.setInterval(function() {
21824                 _this.showPanelNext();
21825             }, this.timer);
21826         }
21827         
21828         if(this.showarrow){
21829             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21830             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21831         }
21832         
21833         
21834     },
21835     
21836 //    onTouchStart : function(e, el, o)
21837 //    {
21838 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21839 //            return;
21840 //        }
21841 //        
21842 //        this.showPanelNext();
21843 //    },
21844     
21845     
21846     getChildContainer : function()
21847     {
21848         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21849     },
21850     
21851     /**
21852     * register a Navigation item
21853     * @param {Roo.bootstrap.NavItem} the navitem to add
21854     */
21855     register : function(item)
21856     {
21857         this.tabs.push( item);
21858         item.navId = this.navId; // not really needed..
21859         this.addBullet();
21860     
21861     },
21862     
21863     getActivePanel : function()
21864     {
21865         var r = false;
21866         Roo.each(this.tabs, function(t) {
21867             if (t.active) {
21868                 r = t;
21869                 return false;
21870             }
21871             return null;
21872         });
21873         return r;
21874         
21875     },
21876     getPanelByName : function(n)
21877     {
21878         var r = false;
21879         Roo.each(this.tabs, function(t) {
21880             if (t.tabId == n) {
21881                 r = t;
21882                 return false;
21883             }
21884             return null;
21885         });
21886         return r;
21887     },
21888     indexOfPanel : function(p)
21889     {
21890         var r = false;
21891         Roo.each(this.tabs, function(t,i) {
21892             if (t.tabId == p.tabId) {
21893                 r = i;
21894                 return false;
21895             }
21896             return null;
21897         });
21898         return r;
21899     },
21900     /**
21901      * show a specific panel
21902      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21903      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21904      */
21905     showPanel : function (pan)
21906     {
21907         if(this.transition || typeof(pan) == 'undefined'){
21908             Roo.log("waiting for the transitionend");
21909             return false;
21910         }
21911         
21912         if (typeof(pan) == 'number') {
21913             pan = this.tabs[pan];
21914         }
21915         
21916         if (typeof(pan) == 'string') {
21917             pan = this.getPanelByName(pan);
21918         }
21919         
21920         var cur = this.getActivePanel();
21921         
21922         if(!pan || !cur){
21923             Roo.log('pan or acitve pan is undefined');
21924             return false;
21925         }
21926         
21927         if (pan.tabId == this.getActivePanel().tabId) {
21928             return true;
21929         }
21930         
21931         if (false === cur.fireEvent('beforedeactivate')) {
21932             return false;
21933         }
21934         
21935         if(this.bullets > 0 && !Roo.isTouch){
21936             this.setActiveBullet(this.indexOfPanel(pan));
21937         }
21938         
21939         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21940             
21941             //class="carousel-item carousel-item-next carousel-item-left"
21942             
21943             this.transition = true;
21944             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21945             var lr = dir == 'next' ? 'left' : 'right';
21946             pan.el.addClass(dir); // or prev
21947             pan.el.addClass('carousel-item-' + dir); // or prev
21948             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21949             cur.el.addClass(lr); // or right
21950             pan.el.addClass(lr);
21951             cur.el.addClass('carousel-item-' +lr); // or right
21952             pan.el.addClass('carousel-item-' +lr);
21953             
21954             
21955             var _this = this;
21956             cur.el.on('transitionend', function() {
21957                 Roo.log("trans end?");
21958                 
21959                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21960                 pan.setActive(true);
21961                 
21962                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21963                 cur.setActive(false);
21964                 
21965                 _this.transition = false;
21966                 
21967             }, this, { single:  true } );
21968             
21969             return true;
21970         }
21971         
21972         cur.setActive(false);
21973         pan.setActive(true);
21974         
21975         return true;
21976         
21977     },
21978     showPanelNext : function()
21979     {
21980         var i = this.indexOfPanel(this.getActivePanel());
21981         
21982         if (i >= this.tabs.length - 1 && !this.autoslide) {
21983             return;
21984         }
21985         
21986         if (i >= this.tabs.length - 1 && this.autoslide) {
21987             i = -1;
21988         }
21989         
21990         this.showPanel(this.tabs[i+1]);
21991     },
21992     
21993     showPanelPrev : function()
21994     {
21995         var i = this.indexOfPanel(this.getActivePanel());
21996         
21997         if (i  < 1 && !this.autoslide) {
21998             return;
21999         }
22000         
22001         if (i < 1 && this.autoslide) {
22002             i = this.tabs.length;
22003         }
22004         
22005         this.showPanel(this.tabs[i-1]);
22006     },
22007     
22008     
22009     addBullet: function()
22010     {
22011         if(!this.bullets || Roo.isTouch){
22012             return;
22013         }
22014         var ctr = this.el.select('.carousel-bullets',true).first();
22015         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22016         var bullet = ctr.createChild({
22017             cls : 'bullet bullet-' + i
22018         },ctr.dom.lastChild);
22019         
22020         
22021         var _this = this;
22022         
22023         bullet.on('click', (function(e, el, o, ii, t){
22024
22025             e.preventDefault();
22026
22027             this.showPanel(ii);
22028
22029             if(this.autoslide && this.slideFn){
22030                 clearInterval(this.slideFn);
22031                 this.slideFn = window.setInterval(function() {
22032                     _this.showPanelNext();
22033                 }, this.timer);
22034             }
22035
22036         }).createDelegate(this, [i, bullet], true));
22037                 
22038         
22039     },
22040      
22041     setActiveBullet : function(i)
22042     {
22043         if(Roo.isTouch){
22044             return;
22045         }
22046         
22047         Roo.each(this.el.select('.bullet', true).elements, function(el){
22048             el.removeClass('selected');
22049         });
22050
22051         var bullet = this.el.select('.bullet-' + i, true).first();
22052         
22053         if(!bullet){
22054             return;
22055         }
22056         
22057         bullet.addClass('selected');
22058     }
22059     
22060     
22061   
22062 });
22063
22064  
22065
22066  
22067  
22068 Roo.apply(Roo.bootstrap.TabGroup, {
22069     
22070     groups: {},
22071      /**
22072     * register a Navigation Group
22073     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22074     */
22075     register : function(navgrp)
22076     {
22077         this.groups[navgrp.navId] = navgrp;
22078         
22079     },
22080     /**
22081     * fetch a Navigation Group based on the navigation ID
22082     * if one does not exist , it will get created.
22083     * @param {string} the navgroup to add
22084     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22085     */
22086     get: function(navId) {
22087         if (typeof(this.groups[navId]) == 'undefined') {
22088             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22089         }
22090         return this.groups[navId] ;
22091     }
22092     
22093     
22094     
22095 });
22096
22097  /*
22098  * - LGPL
22099  *
22100  * TabPanel
22101  * 
22102  */
22103
22104 /**
22105  * @class Roo.bootstrap.TabPanel
22106  * @extends Roo.bootstrap.Component
22107  * Bootstrap TabPanel class
22108  * @cfg {Boolean} active panel active
22109  * @cfg {String} html panel content
22110  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22111  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22112  * @cfg {String} href click to link..
22113  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22114  * 
22115  * 
22116  * @constructor
22117  * Create a new TabPanel
22118  * @param {Object} config The config object
22119  */
22120
22121 Roo.bootstrap.TabPanel = function(config){
22122     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22123     this.addEvents({
22124         /**
22125              * @event changed
22126              * Fires when the active status changes
22127              * @param {Roo.bootstrap.TabPanel} this
22128              * @param {Boolean} state the new state
22129             
22130          */
22131         'changed': true,
22132         /**
22133              * @event beforedeactivate
22134              * Fires before a tab is de-activated - can be used to do validation on a form.
22135              * @param {Roo.bootstrap.TabPanel} this
22136              * @return {Boolean} false if there is an error
22137             
22138          */
22139         'beforedeactivate': true
22140      });
22141     
22142     this.tabId = this.tabId || Roo.id();
22143   
22144 };
22145
22146 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22147     
22148     active: false,
22149     html: false,
22150     tabId: false,
22151     navId : false,
22152     href : '',
22153     touchSlide : false,
22154     getAutoCreate : function(){
22155         
22156         
22157         var cfg = {
22158             tag: 'div',
22159             // item is needed for carousel - not sure if it has any effect otherwise
22160             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22161             html: this.html || ''
22162         };
22163         
22164         if(this.active){
22165             cfg.cls += ' active';
22166         }
22167         
22168         if(this.tabId){
22169             cfg.tabId = this.tabId;
22170         }
22171         
22172         
22173         
22174         return cfg;
22175     },
22176     
22177     initEvents:  function()
22178     {
22179         var p = this.parent();
22180         
22181         this.navId = this.navId || p.navId;
22182         
22183         if (typeof(this.navId) != 'undefined') {
22184             // not really needed.. but just in case.. parent should be a NavGroup.
22185             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22186             
22187             tg.register(this);
22188             
22189             var i = tg.tabs.length - 1;
22190             
22191             if(this.active && tg.bullets > 0 && i < tg.bullets){
22192                 tg.setActiveBullet(i);
22193             }
22194         }
22195         
22196         this.el.on('click', this.onClick, this);
22197         
22198         if(Roo.isTouch && this.touchSlide){
22199             this.el.on("touchstart", this.onTouchStart, this);
22200             this.el.on("touchmove", this.onTouchMove, this);
22201             this.el.on("touchend", this.onTouchEnd, this);
22202         }
22203         
22204     },
22205     
22206     onRender : function(ct, position)
22207     {
22208         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22209     },
22210     
22211     setActive : function(state)
22212     {
22213         Roo.log("panel - set active " + this.tabId + "=" + state);
22214         
22215         this.active = state;
22216         if (!state) {
22217             this.el.removeClass('active');
22218             
22219         } else  if (!this.el.hasClass('active')) {
22220             this.el.addClass('active');
22221         }
22222         
22223         this.fireEvent('changed', this, state);
22224     },
22225     
22226     onClick : function(e)
22227     {
22228         e.preventDefault();
22229         
22230         if(!this.href.length){
22231             return;
22232         }
22233         
22234         window.location.href = this.href;
22235     },
22236     
22237     startX : 0,
22238     startY : 0,
22239     endX : 0,
22240     endY : 0,
22241     swiping : false,
22242     
22243     onTouchStart : function(e)
22244     {
22245         this.swiping = false;
22246         
22247         this.startX = e.browserEvent.touches[0].clientX;
22248         this.startY = e.browserEvent.touches[0].clientY;
22249     },
22250     
22251     onTouchMove : function(e)
22252     {
22253         this.swiping = true;
22254         
22255         this.endX = e.browserEvent.touches[0].clientX;
22256         this.endY = e.browserEvent.touches[0].clientY;
22257     },
22258     
22259     onTouchEnd : function(e)
22260     {
22261         if(!this.swiping){
22262             this.onClick(e);
22263             return;
22264         }
22265         
22266         var tabGroup = this.parent();
22267         
22268         if(this.endX > this.startX){ // swiping right
22269             tabGroup.showPanelPrev();
22270             return;
22271         }
22272         
22273         if(this.startX > this.endX){ // swiping left
22274             tabGroup.showPanelNext();
22275             return;
22276         }
22277     }
22278     
22279     
22280 });
22281  
22282
22283  
22284
22285  /*
22286  * - LGPL
22287  *
22288  * DateField
22289  * 
22290  */
22291
22292 /**
22293  * @class Roo.bootstrap.DateField
22294  * @extends Roo.bootstrap.Input
22295  * Bootstrap DateField class
22296  * @cfg {Number} weekStart default 0
22297  * @cfg {String} viewMode default empty, (months|years)
22298  * @cfg {String} minViewMode default empty, (months|years)
22299  * @cfg {Number} startDate default -Infinity
22300  * @cfg {Number} endDate default Infinity
22301  * @cfg {Boolean} todayHighlight default false
22302  * @cfg {Boolean} todayBtn default false
22303  * @cfg {Boolean} calendarWeeks default false
22304  * @cfg {Object} daysOfWeekDisabled default empty
22305  * @cfg {Boolean} singleMode default false (true | false)
22306  * 
22307  * @cfg {Boolean} keyboardNavigation default true
22308  * @cfg {String} language default en
22309  * 
22310  * @constructor
22311  * Create a new DateField
22312  * @param {Object} config The config object
22313  */
22314
22315 Roo.bootstrap.DateField = function(config){
22316     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22317      this.addEvents({
22318             /**
22319              * @event show
22320              * Fires when this field show.
22321              * @param {Roo.bootstrap.DateField} this
22322              * @param {Mixed} date The date value
22323              */
22324             show : true,
22325             /**
22326              * @event show
22327              * Fires when this field hide.
22328              * @param {Roo.bootstrap.DateField} this
22329              * @param {Mixed} date The date value
22330              */
22331             hide : true,
22332             /**
22333              * @event select
22334              * Fires when select a date.
22335              * @param {Roo.bootstrap.DateField} this
22336              * @param {Mixed} date The date value
22337              */
22338             select : true,
22339             /**
22340              * @event beforeselect
22341              * Fires when before select a date.
22342              * @param {Roo.bootstrap.DateField} this
22343              * @param {Mixed} date The date value
22344              */
22345             beforeselect : true
22346         });
22347 };
22348
22349 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22350     
22351     /**
22352      * @cfg {String} format
22353      * The default date format string which can be overriden for localization support.  The format must be
22354      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22355      */
22356     format : "m/d/y",
22357     /**
22358      * @cfg {String} altFormats
22359      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22360      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22361      */
22362     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22363     
22364     weekStart : 0,
22365     
22366     viewMode : '',
22367     
22368     minViewMode : '',
22369     
22370     todayHighlight : false,
22371     
22372     todayBtn: false,
22373     
22374     language: 'en',
22375     
22376     keyboardNavigation: true,
22377     
22378     calendarWeeks: false,
22379     
22380     startDate: -Infinity,
22381     
22382     endDate: Infinity,
22383     
22384     daysOfWeekDisabled: [],
22385     
22386     _events: [],
22387     
22388     singleMode : false,
22389     
22390     UTCDate: function()
22391     {
22392         return new Date(Date.UTC.apply(Date, arguments));
22393     },
22394     
22395     UTCToday: function()
22396     {
22397         var today = new Date();
22398         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22399     },
22400     
22401     getDate: function() {
22402             var d = this.getUTCDate();
22403             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22404     },
22405     
22406     getUTCDate: function() {
22407             return this.date;
22408     },
22409     
22410     setDate: function(d) {
22411             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22412     },
22413     
22414     setUTCDate: function(d) {
22415             this.date = d;
22416             this.setValue(this.formatDate(this.date));
22417     },
22418         
22419     onRender: function(ct, position)
22420     {
22421         
22422         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22423         
22424         this.language = this.language || 'en';
22425         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22426         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22427         
22428         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22429         this.format = this.format || 'm/d/y';
22430         this.isInline = false;
22431         this.isInput = true;
22432         this.component = this.el.select('.add-on', true).first() || false;
22433         this.component = (this.component && this.component.length === 0) ? false : this.component;
22434         this.hasInput = this.component && this.inputEl().length;
22435         
22436         if (typeof(this.minViewMode === 'string')) {
22437             switch (this.minViewMode) {
22438                 case 'months':
22439                     this.minViewMode = 1;
22440                     break;
22441                 case 'years':
22442                     this.minViewMode = 2;
22443                     break;
22444                 default:
22445                     this.minViewMode = 0;
22446                     break;
22447             }
22448         }
22449         
22450         if (typeof(this.viewMode === 'string')) {
22451             switch (this.viewMode) {
22452                 case 'months':
22453                     this.viewMode = 1;
22454                     break;
22455                 case 'years':
22456                     this.viewMode = 2;
22457                     break;
22458                 default:
22459                     this.viewMode = 0;
22460                     break;
22461             }
22462         }
22463                 
22464         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22465         
22466 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22467         
22468         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22469         
22470         this.picker().on('mousedown', this.onMousedown, this);
22471         this.picker().on('click', this.onClick, this);
22472         
22473         this.picker().addClass('datepicker-dropdown');
22474         
22475         this.startViewMode = this.viewMode;
22476         
22477         if(this.singleMode){
22478             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22479                 v.setVisibilityMode(Roo.Element.DISPLAY);
22480                 v.hide();
22481             });
22482             
22483             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22484                 v.setStyle('width', '189px');
22485             });
22486         }
22487         
22488         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22489             if(!this.calendarWeeks){
22490                 v.remove();
22491                 return;
22492             }
22493             
22494             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22495             v.attr('colspan', function(i, val){
22496                 return parseInt(val) + 1;
22497             });
22498         });
22499                         
22500         
22501         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22502         
22503         this.setStartDate(this.startDate);
22504         this.setEndDate(this.endDate);
22505         
22506         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22507         
22508         this.fillDow();
22509         this.fillMonths();
22510         this.update();
22511         this.showMode();
22512         
22513         if(this.isInline) {
22514             this.showPopup();
22515         }
22516     },
22517     
22518     picker : function()
22519     {
22520         return this.pickerEl;
22521 //        return this.el.select('.datepicker', true).first();
22522     },
22523     
22524     fillDow: function()
22525     {
22526         var dowCnt = this.weekStart;
22527         
22528         var dow = {
22529             tag: 'tr',
22530             cn: [
22531                 
22532             ]
22533         };
22534         
22535         if(this.calendarWeeks){
22536             dow.cn.push({
22537                 tag: 'th',
22538                 cls: 'cw',
22539                 html: '&nbsp;'
22540             })
22541         }
22542         
22543         while (dowCnt < this.weekStart + 7) {
22544             dow.cn.push({
22545                 tag: 'th',
22546                 cls: 'dow',
22547                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22548             });
22549         }
22550         
22551         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22552     },
22553     
22554     fillMonths: function()
22555     {    
22556         var i = 0;
22557         var months = this.picker().select('>.datepicker-months td', true).first();
22558         
22559         months.dom.innerHTML = '';
22560         
22561         while (i < 12) {
22562             var month = {
22563                 tag: 'span',
22564                 cls: 'month',
22565                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22566             };
22567             
22568             months.createChild(month);
22569         }
22570         
22571     },
22572     
22573     update: function()
22574     {
22575         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
22576         
22577         if (this.date < this.startDate) {
22578             this.viewDate = new Date(this.startDate);
22579         } else if (this.date > this.endDate) {
22580             this.viewDate = new Date(this.endDate);
22581         } else {
22582             this.viewDate = new Date(this.date);
22583         }
22584         
22585         this.fill();
22586     },
22587     
22588     fill: function() 
22589     {
22590         var d = new Date(this.viewDate),
22591                 year = d.getUTCFullYear(),
22592                 month = d.getUTCMonth(),
22593                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22594                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22595                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22596                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22597                 currentDate = this.date && this.date.valueOf(),
22598                 today = this.UTCToday();
22599         
22600         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22601         
22602 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22603         
22604 //        this.picker.select('>tfoot th.today').
22605 //                                              .text(dates[this.language].today)
22606 //                                              .toggle(this.todayBtn !== false);
22607     
22608         this.updateNavArrows();
22609         this.fillMonths();
22610                                                 
22611         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22612         
22613         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22614          
22615         prevMonth.setUTCDate(day);
22616         
22617         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22618         
22619         var nextMonth = new Date(prevMonth);
22620         
22621         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22622         
22623         nextMonth = nextMonth.valueOf();
22624         
22625         var fillMonths = false;
22626         
22627         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22628         
22629         while(prevMonth.valueOf() <= nextMonth) {
22630             var clsName = '';
22631             
22632             if (prevMonth.getUTCDay() === this.weekStart) {
22633                 if(fillMonths){
22634                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22635                 }
22636                     
22637                 fillMonths = {
22638                     tag: 'tr',
22639                     cn: []
22640                 };
22641                 
22642                 if(this.calendarWeeks){
22643                     // ISO 8601: First week contains first thursday.
22644                     // ISO also states week starts on Monday, but we can be more abstract here.
22645                     var
22646                     // Start of current week: based on weekstart/current date
22647                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22648                     // Thursday of this week
22649                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22650                     // First Thursday of year, year from thursday
22651                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22652                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22653                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22654                     
22655                     fillMonths.cn.push({
22656                         tag: 'td',
22657                         cls: 'cw',
22658                         html: calWeek
22659                     });
22660                 }
22661             }
22662             
22663             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22664                 clsName += ' old';
22665             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22666                 clsName += ' new';
22667             }
22668             if (this.todayHighlight &&
22669                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22670                 prevMonth.getUTCMonth() == today.getMonth() &&
22671                 prevMonth.getUTCDate() == today.getDate()) {
22672                 clsName += ' today';
22673             }
22674             
22675             if (currentDate && prevMonth.valueOf() === currentDate) {
22676                 clsName += ' active';
22677             }
22678             
22679             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22680                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22681                     clsName += ' disabled';
22682             }
22683             
22684             fillMonths.cn.push({
22685                 tag: 'td',
22686                 cls: 'day ' + clsName,
22687                 html: prevMonth.getDate()
22688             });
22689             
22690             prevMonth.setDate(prevMonth.getDate()+1);
22691         }
22692           
22693         var currentYear = this.date && this.date.getUTCFullYear();
22694         var currentMonth = this.date && this.date.getUTCMonth();
22695         
22696         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22697         
22698         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22699             v.removeClass('active');
22700             
22701             if(currentYear === year && k === currentMonth){
22702                 v.addClass('active');
22703             }
22704             
22705             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22706                 v.addClass('disabled');
22707             }
22708             
22709         });
22710         
22711         
22712         year = parseInt(year/10, 10) * 10;
22713         
22714         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22715         
22716         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22717         
22718         year -= 1;
22719         for (var i = -1; i < 11; i++) {
22720             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22721                 tag: 'span',
22722                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22723                 html: year
22724             });
22725             
22726             year += 1;
22727         }
22728     },
22729     
22730     showMode: function(dir) 
22731     {
22732         if (dir) {
22733             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22734         }
22735         
22736         Roo.each(this.picker().select('>div',true).elements, function(v){
22737             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22738             v.hide();
22739         });
22740         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22741     },
22742     
22743     place: function()
22744     {
22745         if(this.isInline) {
22746             return;
22747         }
22748         
22749         this.picker().removeClass(['bottom', 'top']);
22750         
22751         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22752             /*
22753              * place to the top of element!
22754              *
22755              */
22756             
22757             this.picker().addClass('top');
22758             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22759             
22760             return;
22761         }
22762         
22763         this.picker().addClass('bottom');
22764         
22765         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22766     },
22767     
22768     parseDate : function(value)
22769     {
22770         if(!value || value instanceof Date){
22771             return value;
22772         }
22773         var v = Date.parseDate(value, this.format);
22774         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22775             v = Date.parseDate(value, 'Y-m-d');
22776         }
22777         if(!v && this.altFormats){
22778             if(!this.altFormatsArray){
22779                 this.altFormatsArray = this.altFormats.split("|");
22780             }
22781             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22782                 v = Date.parseDate(value, this.altFormatsArray[i]);
22783             }
22784         }
22785         return v;
22786     },
22787     
22788     formatDate : function(date, fmt)
22789     {   
22790         return (!date || !(date instanceof Date)) ?
22791         date : date.dateFormat(fmt || this.format);
22792     },
22793     
22794     onFocus : function()
22795     {
22796         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22797         this.showPopup();
22798     },
22799     
22800     onBlur : function()
22801     {
22802         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22803         
22804         var d = this.inputEl().getValue();
22805         
22806         this.setValue(d);
22807                 
22808         this.hidePopup();
22809     },
22810     
22811     showPopup : function()
22812     {
22813         this.picker().show();
22814         this.update();
22815         this.place();
22816         
22817         this.fireEvent('showpopup', this, this.date);
22818     },
22819     
22820     hidePopup : function()
22821     {
22822         if(this.isInline) {
22823             return;
22824         }
22825         this.picker().hide();
22826         this.viewMode = this.startViewMode;
22827         this.showMode();
22828         
22829         this.fireEvent('hidepopup', this, this.date);
22830         
22831     },
22832     
22833     onMousedown: function(e)
22834     {
22835         e.stopPropagation();
22836         e.preventDefault();
22837     },
22838     
22839     keyup: function(e)
22840     {
22841         Roo.bootstrap.DateField.superclass.keyup.call(this);
22842         this.update();
22843     },
22844
22845     setValue: function(v)
22846     {
22847         if(this.fireEvent('beforeselect', this, v) !== false){
22848             var d = new Date(this.parseDate(v) ).clearTime();
22849         
22850             if(isNaN(d.getTime())){
22851                 this.date = this.viewDate = '';
22852                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22853                 return;
22854             }
22855
22856             v = this.formatDate(d);
22857
22858             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22859
22860             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22861
22862             this.update();
22863
22864             this.fireEvent('select', this, this.date);
22865         }
22866     },
22867     
22868     getValue: function()
22869     {
22870         return this.formatDate(this.date);
22871     },
22872     
22873     fireKey: function(e)
22874     {
22875         if (!this.picker().isVisible()){
22876             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22877                 this.showPopup();
22878             }
22879             return;
22880         }
22881         
22882         var dateChanged = false,
22883         dir, day, month,
22884         newDate, newViewDate;
22885         
22886         switch(e.keyCode){
22887             case 27: // escape
22888                 this.hidePopup();
22889                 e.preventDefault();
22890                 break;
22891             case 37: // left
22892             case 39: // right
22893                 if (!this.keyboardNavigation) {
22894                     break;
22895                 }
22896                 dir = e.keyCode == 37 ? -1 : 1;
22897                 
22898                 if (e.ctrlKey){
22899                     newDate = this.moveYear(this.date, dir);
22900                     newViewDate = this.moveYear(this.viewDate, dir);
22901                 } else if (e.shiftKey){
22902                     newDate = this.moveMonth(this.date, dir);
22903                     newViewDate = this.moveMonth(this.viewDate, dir);
22904                 } else {
22905                     newDate = new Date(this.date);
22906                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22907                     newViewDate = new Date(this.viewDate);
22908                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22909                 }
22910                 if (this.dateWithinRange(newDate)){
22911                     this.date = newDate;
22912                     this.viewDate = newViewDate;
22913                     this.setValue(this.formatDate(this.date));
22914 //                    this.update();
22915                     e.preventDefault();
22916                     dateChanged = true;
22917                 }
22918                 break;
22919             case 38: // up
22920             case 40: // down
22921                 if (!this.keyboardNavigation) {
22922                     break;
22923                 }
22924                 dir = e.keyCode == 38 ? -1 : 1;
22925                 if (e.ctrlKey){
22926                     newDate = this.moveYear(this.date, dir);
22927                     newViewDate = this.moveYear(this.viewDate, dir);
22928                 } else if (e.shiftKey){
22929                     newDate = this.moveMonth(this.date, dir);
22930                     newViewDate = this.moveMonth(this.viewDate, dir);
22931                 } else {
22932                     newDate = new Date(this.date);
22933                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22934                     newViewDate = new Date(this.viewDate);
22935                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22936                 }
22937                 if (this.dateWithinRange(newDate)){
22938                     this.date = newDate;
22939                     this.viewDate = newViewDate;
22940                     this.setValue(this.formatDate(this.date));
22941 //                    this.update();
22942                     e.preventDefault();
22943                     dateChanged = true;
22944                 }
22945                 break;
22946             case 13: // enter
22947                 this.setValue(this.formatDate(this.date));
22948                 this.hidePopup();
22949                 e.preventDefault();
22950                 break;
22951             case 9: // tab
22952                 this.setValue(this.formatDate(this.date));
22953                 this.hidePopup();
22954                 break;
22955             case 16: // shift
22956             case 17: // ctrl
22957             case 18: // alt
22958                 break;
22959             default :
22960                 this.hidePopup();
22961                 
22962         }
22963     },
22964     
22965     
22966     onClick: function(e) 
22967     {
22968         e.stopPropagation();
22969         e.preventDefault();
22970         
22971         var target = e.getTarget();
22972         
22973         if(target.nodeName.toLowerCase() === 'i'){
22974             target = Roo.get(target).dom.parentNode;
22975         }
22976         
22977         var nodeName = target.nodeName;
22978         var className = target.className;
22979         var html = target.innerHTML;
22980         //Roo.log(nodeName);
22981         
22982         switch(nodeName.toLowerCase()) {
22983             case 'th':
22984                 switch(className) {
22985                     case 'switch':
22986                         this.showMode(1);
22987                         break;
22988                     case 'prev':
22989                     case 'next':
22990                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22991                         switch(this.viewMode){
22992                                 case 0:
22993                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22994                                         break;
22995                                 case 1:
22996                                 case 2:
22997                                         this.viewDate = this.moveYear(this.viewDate, dir);
22998                                         break;
22999                         }
23000                         this.fill();
23001                         break;
23002                     case 'today':
23003                         var date = new Date();
23004                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23005 //                        this.fill()
23006                         this.setValue(this.formatDate(this.date));
23007                         
23008                         this.hidePopup();
23009                         break;
23010                 }
23011                 break;
23012             case 'span':
23013                 if (className.indexOf('disabled') < 0) {
23014                 if (!this.viewDate) {
23015                     this.viewDate = new Date();
23016                 }
23017                 this.viewDate.setUTCDate(1);
23018                     if (className.indexOf('month') > -1) {
23019                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23020                     } else {
23021                         var year = parseInt(html, 10) || 0;
23022                         this.viewDate.setUTCFullYear(year);
23023                         
23024                     }
23025                     
23026                     if(this.singleMode){
23027                         this.setValue(this.formatDate(this.viewDate));
23028                         this.hidePopup();
23029                         return;
23030                     }
23031                     
23032                     this.showMode(-1);
23033                     this.fill();
23034                 }
23035                 break;
23036                 
23037             case 'td':
23038                 //Roo.log(className);
23039                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23040                     var day = parseInt(html, 10) || 1;
23041                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23042                         month = (this.viewDate || new Date()).getUTCMonth();
23043
23044                     if (className.indexOf('old') > -1) {
23045                         if(month === 0 ){
23046                             month = 11;
23047                             year -= 1;
23048                         }else{
23049                             month -= 1;
23050                         }
23051                     } else if (className.indexOf('new') > -1) {
23052                         if (month == 11) {
23053                             month = 0;
23054                             year += 1;
23055                         } else {
23056                             month += 1;
23057                         }
23058                     }
23059                     //Roo.log([year,month,day]);
23060                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23061                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23062 //                    this.fill();
23063                     //Roo.log(this.formatDate(this.date));
23064                     this.setValue(this.formatDate(this.date));
23065                     this.hidePopup();
23066                 }
23067                 break;
23068         }
23069     },
23070     
23071     setStartDate: function(startDate)
23072     {
23073         this.startDate = startDate || -Infinity;
23074         if (this.startDate !== -Infinity) {
23075             this.startDate = this.parseDate(this.startDate);
23076         }
23077         this.update();
23078         this.updateNavArrows();
23079     },
23080
23081     setEndDate: function(endDate)
23082     {
23083         this.endDate = endDate || Infinity;
23084         if (this.endDate !== Infinity) {
23085             this.endDate = this.parseDate(this.endDate);
23086         }
23087         this.update();
23088         this.updateNavArrows();
23089     },
23090     
23091     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23092     {
23093         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23094         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23095             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23096         }
23097         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23098             return parseInt(d, 10);
23099         });
23100         this.update();
23101         this.updateNavArrows();
23102     },
23103     
23104     updateNavArrows: function() 
23105     {
23106         if(this.singleMode){
23107             return;
23108         }
23109         
23110         var d = new Date(this.viewDate),
23111         year = d.getUTCFullYear(),
23112         month = d.getUTCMonth();
23113         
23114         Roo.each(this.picker().select('.prev', true).elements, function(v){
23115             v.show();
23116             switch (this.viewMode) {
23117                 case 0:
23118
23119                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23120                         v.hide();
23121                     }
23122                     break;
23123                 case 1:
23124                 case 2:
23125                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23126                         v.hide();
23127                     }
23128                     break;
23129             }
23130         });
23131         
23132         Roo.each(this.picker().select('.next', true).elements, function(v){
23133             v.show();
23134             switch (this.viewMode) {
23135                 case 0:
23136
23137                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23138                         v.hide();
23139                     }
23140                     break;
23141                 case 1:
23142                 case 2:
23143                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23144                         v.hide();
23145                     }
23146                     break;
23147             }
23148         })
23149     },
23150     
23151     moveMonth: function(date, dir)
23152     {
23153         if (!dir) {
23154             return date;
23155         }
23156         var new_date = new Date(date.valueOf()),
23157         day = new_date.getUTCDate(),
23158         month = new_date.getUTCMonth(),
23159         mag = Math.abs(dir),
23160         new_month, test;
23161         dir = dir > 0 ? 1 : -1;
23162         if (mag == 1){
23163             test = dir == -1
23164             // If going back one month, make sure month is not current month
23165             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23166             ? function(){
23167                 return new_date.getUTCMonth() == month;
23168             }
23169             // If going forward one month, make sure month is as expected
23170             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23171             : function(){
23172                 return new_date.getUTCMonth() != new_month;
23173             };
23174             new_month = month + dir;
23175             new_date.setUTCMonth(new_month);
23176             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23177             if (new_month < 0 || new_month > 11) {
23178                 new_month = (new_month + 12) % 12;
23179             }
23180         } else {
23181             // For magnitudes >1, move one month at a time...
23182             for (var i=0; i<mag; i++) {
23183                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23184                 new_date = this.moveMonth(new_date, dir);
23185             }
23186             // ...then reset the day, keeping it in the new month
23187             new_month = new_date.getUTCMonth();
23188             new_date.setUTCDate(day);
23189             test = function(){
23190                 return new_month != new_date.getUTCMonth();
23191             };
23192         }
23193         // Common date-resetting loop -- if date is beyond end of month, make it
23194         // end of month
23195         while (test()){
23196             new_date.setUTCDate(--day);
23197             new_date.setUTCMonth(new_month);
23198         }
23199         return new_date;
23200     },
23201
23202     moveYear: function(date, dir)
23203     {
23204         return this.moveMonth(date, dir*12);
23205     },
23206
23207     dateWithinRange: function(date)
23208     {
23209         return date >= this.startDate && date <= this.endDate;
23210     },
23211
23212     
23213     remove: function() 
23214     {
23215         this.picker().remove();
23216     },
23217     
23218     validateValue : function(value)
23219     {
23220         if(this.getVisibilityEl().hasClass('hidden')){
23221             return true;
23222         }
23223         
23224         if(value.length < 1)  {
23225             if(this.allowBlank){
23226                 return true;
23227             }
23228             return false;
23229         }
23230         
23231         if(value.length < this.minLength){
23232             return false;
23233         }
23234         if(value.length > this.maxLength){
23235             return false;
23236         }
23237         if(this.vtype){
23238             var vt = Roo.form.VTypes;
23239             if(!vt[this.vtype](value, this)){
23240                 return false;
23241             }
23242         }
23243         if(typeof this.validator == "function"){
23244             var msg = this.validator(value);
23245             if(msg !== true){
23246                 return false;
23247             }
23248         }
23249         
23250         if(this.regex && !this.regex.test(value)){
23251             return false;
23252         }
23253         
23254         if(typeof(this.parseDate(value)) == 'undefined'){
23255             return false;
23256         }
23257         
23258         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23259             return false;
23260         }      
23261         
23262         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23263             return false;
23264         } 
23265         
23266         
23267         return true;
23268     },
23269     
23270     reset : function()
23271     {
23272         this.date = this.viewDate = '';
23273         
23274         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23275     }
23276    
23277 });
23278
23279 Roo.apply(Roo.bootstrap.DateField,  {
23280     
23281     head : {
23282         tag: 'thead',
23283         cn: [
23284         {
23285             tag: 'tr',
23286             cn: [
23287             {
23288                 tag: 'th',
23289                 cls: 'prev',
23290                 html: '<i class="fa fa-arrow-left"/>'
23291             },
23292             {
23293                 tag: 'th',
23294                 cls: 'switch',
23295                 colspan: '5'
23296             },
23297             {
23298                 tag: 'th',
23299                 cls: 'next',
23300                 html: '<i class="fa fa-arrow-right"/>'
23301             }
23302
23303             ]
23304         }
23305         ]
23306     },
23307     
23308     content : {
23309         tag: 'tbody',
23310         cn: [
23311         {
23312             tag: 'tr',
23313             cn: [
23314             {
23315                 tag: 'td',
23316                 colspan: '7'
23317             }
23318             ]
23319         }
23320         ]
23321     },
23322     
23323     footer : {
23324         tag: 'tfoot',
23325         cn: [
23326         {
23327             tag: 'tr',
23328             cn: [
23329             {
23330                 tag: 'th',
23331                 colspan: '7',
23332                 cls: 'today'
23333             }
23334                     
23335             ]
23336         }
23337         ]
23338     },
23339     
23340     dates:{
23341         en: {
23342             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23343             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23344             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23345             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23346             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23347             today: "Today"
23348         }
23349     },
23350     
23351     modes: [
23352     {
23353         clsName: 'days',
23354         navFnc: 'Month',
23355         navStep: 1
23356     },
23357     {
23358         clsName: 'months',
23359         navFnc: 'FullYear',
23360         navStep: 1
23361     },
23362     {
23363         clsName: 'years',
23364         navFnc: 'FullYear',
23365         navStep: 10
23366     }]
23367 });
23368
23369 Roo.apply(Roo.bootstrap.DateField,  {
23370   
23371     template : {
23372         tag: 'div',
23373         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23374         cn: [
23375         {
23376             tag: 'div',
23377             cls: 'datepicker-days',
23378             cn: [
23379             {
23380                 tag: 'table',
23381                 cls: 'table-condensed',
23382                 cn:[
23383                 Roo.bootstrap.DateField.head,
23384                 {
23385                     tag: 'tbody'
23386                 },
23387                 Roo.bootstrap.DateField.footer
23388                 ]
23389             }
23390             ]
23391         },
23392         {
23393             tag: 'div',
23394             cls: 'datepicker-months',
23395             cn: [
23396             {
23397                 tag: 'table',
23398                 cls: 'table-condensed',
23399                 cn:[
23400                 Roo.bootstrap.DateField.head,
23401                 Roo.bootstrap.DateField.content,
23402                 Roo.bootstrap.DateField.footer
23403                 ]
23404             }
23405             ]
23406         },
23407         {
23408             tag: 'div',
23409             cls: 'datepicker-years',
23410             cn: [
23411             {
23412                 tag: 'table',
23413                 cls: 'table-condensed',
23414                 cn:[
23415                 Roo.bootstrap.DateField.head,
23416                 Roo.bootstrap.DateField.content,
23417                 Roo.bootstrap.DateField.footer
23418                 ]
23419             }
23420             ]
23421         }
23422         ]
23423     }
23424 });
23425
23426  
23427
23428  /*
23429  * - LGPL
23430  *
23431  * TimeField
23432  * 
23433  */
23434
23435 /**
23436  * @class Roo.bootstrap.TimeField
23437  * @extends Roo.bootstrap.Input
23438  * Bootstrap DateField class
23439  * 
23440  * 
23441  * @constructor
23442  * Create a new TimeField
23443  * @param {Object} config The config object
23444  */
23445
23446 Roo.bootstrap.TimeField = function(config){
23447     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23448     this.addEvents({
23449             /**
23450              * @event show
23451              * Fires when this field show.
23452              * @param {Roo.bootstrap.DateField} thisthis
23453              * @param {Mixed} date The date value
23454              */
23455             show : true,
23456             /**
23457              * @event show
23458              * Fires when this field hide.
23459              * @param {Roo.bootstrap.DateField} this
23460              * @param {Mixed} date The date value
23461              */
23462             hide : true,
23463             /**
23464              * @event select
23465              * Fires when select a date.
23466              * @param {Roo.bootstrap.DateField} this
23467              * @param {Mixed} date The date value
23468              */
23469             select : true
23470         });
23471 };
23472
23473 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23474     
23475     /**
23476      * @cfg {String} format
23477      * The default time format string which can be overriden for localization support.  The format must be
23478      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23479      */
23480     format : "H:i",
23481
23482     getAutoCreate : function()
23483     {
23484         this.after = '<i class="fa far fa-clock"></i>';
23485         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23486         
23487          
23488     },
23489     onRender: function(ct, position)
23490     {
23491         
23492         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23493                 
23494         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23495         
23496         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23497         
23498         this.pop = this.picker().select('>.datepicker-time',true).first();
23499         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23500         
23501         this.picker().on('mousedown', this.onMousedown, this);
23502         this.picker().on('click', this.onClick, this);
23503         
23504         this.picker().addClass('datepicker-dropdown');
23505     
23506         this.fillTime();
23507         this.update();
23508             
23509         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23510         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23511         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23512         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23513         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23514         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23515
23516     },
23517     
23518     fireKey: function(e){
23519         if (!this.picker().isVisible()){
23520             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23521                 this.show();
23522             }
23523             return;
23524         }
23525
23526         e.preventDefault();
23527         
23528         switch(e.keyCode){
23529             case 27: // escape
23530                 this.hide();
23531                 break;
23532             case 37: // left
23533             case 39: // right
23534                 this.onTogglePeriod();
23535                 break;
23536             case 38: // up
23537                 this.onIncrementMinutes();
23538                 break;
23539             case 40: // down
23540                 this.onDecrementMinutes();
23541                 break;
23542             case 13: // enter
23543             case 9: // tab
23544                 this.setTime();
23545                 break;
23546         }
23547     },
23548     
23549     onClick: function(e) {
23550         e.stopPropagation();
23551         e.preventDefault();
23552     },
23553     
23554     picker : function()
23555     {
23556         return this.pickerEl;
23557     },
23558     
23559     fillTime: function()
23560     {    
23561         var time = this.pop.select('tbody', true).first();
23562         
23563         time.dom.innerHTML = '';
23564         
23565         time.createChild({
23566             tag: 'tr',
23567             cn: [
23568                 {
23569                     tag: 'td',
23570                     cn: [
23571                         {
23572                             tag: 'a',
23573                             href: '#',
23574                             cls: 'btn',
23575                             cn: [
23576                                 {
23577                                     tag: 'i',
23578                                     cls: 'hours-up fa fas fa-chevron-up'
23579                                 }
23580                             ]
23581                         } 
23582                     ]
23583                 },
23584                 {
23585                     tag: 'td',
23586                     cls: 'separator'
23587                 },
23588                 {
23589                     tag: 'td',
23590                     cn: [
23591                         {
23592                             tag: 'a',
23593                             href: '#',
23594                             cls: 'btn',
23595                             cn: [
23596                                 {
23597                                     tag: 'i',
23598                                     cls: 'minutes-up fa fas fa-chevron-up'
23599                                 }
23600                             ]
23601                         }
23602                     ]
23603                 },
23604                 {
23605                     tag: 'td',
23606                     cls: 'separator'
23607                 }
23608             ]
23609         });
23610         
23611         time.createChild({
23612             tag: 'tr',
23613             cn: [
23614                 {
23615                     tag: 'td',
23616                     cn: [
23617                         {
23618                             tag: 'span',
23619                             cls: 'timepicker-hour',
23620                             html: '00'
23621                         }  
23622                     ]
23623                 },
23624                 {
23625                     tag: 'td',
23626                     cls: 'separator',
23627                     html: ':'
23628                 },
23629                 {
23630                     tag: 'td',
23631                     cn: [
23632                         {
23633                             tag: 'span',
23634                             cls: 'timepicker-minute',
23635                             html: '00'
23636                         }  
23637                     ]
23638                 },
23639                 {
23640                     tag: 'td',
23641                     cls: 'separator'
23642                 },
23643                 {
23644                     tag: 'td',
23645                     cn: [
23646                         {
23647                             tag: 'button',
23648                             type: 'button',
23649                             cls: 'btn btn-primary period',
23650                             html: 'AM'
23651                             
23652                         }
23653                     ]
23654                 }
23655             ]
23656         });
23657         
23658         time.createChild({
23659             tag: 'tr',
23660             cn: [
23661                 {
23662                     tag: 'td',
23663                     cn: [
23664                         {
23665                             tag: 'a',
23666                             href: '#',
23667                             cls: 'btn',
23668                             cn: [
23669                                 {
23670                                     tag: 'span',
23671                                     cls: 'hours-down fa fas fa-chevron-down'
23672                                 }
23673                             ]
23674                         }
23675                     ]
23676                 },
23677                 {
23678                     tag: 'td',
23679                     cls: 'separator'
23680                 },
23681                 {
23682                     tag: 'td',
23683                     cn: [
23684                         {
23685                             tag: 'a',
23686                             href: '#',
23687                             cls: 'btn',
23688                             cn: [
23689                                 {
23690                                     tag: 'span',
23691                                     cls: 'minutes-down fa fas fa-chevron-down'
23692                                 }
23693                             ]
23694                         }
23695                     ]
23696                 },
23697                 {
23698                     tag: 'td',
23699                     cls: 'separator'
23700                 }
23701             ]
23702         });
23703         
23704     },
23705     
23706     update: function()
23707     {
23708         
23709         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23710         
23711         this.fill();
23712     },
23713     
23714     fill: function() 
23715     {
23716         var hours = this.time.getHours();
23717         var minutes = this.time.getMinutes();
23718         var period = 'AM';
23719         
23720         if(hours > 11){
23721             period = 'PM';
23722         }
23723         
23724         if(hours == 0){
23725             hours = 12;
23726         }
23727         
23728         
23729         if(hours > 12){
23730             hours = hours - 12;
23731         }
23732         
23733         if(hours < 10){
23734             hours = '0' + hours;
23735         }
23736         
23737         if(minutes < 10){
23738             minutes = '0' + minutes;
23739         }
23740         
23741         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23742         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23743         this.pop.select('button', true).first().dom.innerHTML = period;
23744         
23745     },
23746     
23747     place: function()
23748     {   
23749         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23750         
23751         var cls = ['bottom'];
23752         
23753         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23754             cls.pop();
23755             cls.push('top');
23756         }
23757         
23758         cls.push('right');
23759         
23760         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23761             cls.pop();
23762             cls.push('left');
23763         }
23764         //this.picker().setXY(20000,20000);
23765         this.picker().addClass(cls.join('-'));
23766         
23767         var _this = this;
23768         
23769         Roo.each(cls, function(c){
23770             if(c == 'bottom'){
23771                 (function() {
23772                  //  
23773                 }).defer(200);
23774                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23775                 //_this.picker().setTop(_this.inputEl().getHeight());
23776                 return;
23777             }
23778             if(c == 'top'){
23779                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23780                 
23781                 //_this.picker().setTop(0 - _this.picker().getHeight());
23782                 return;
23783             }
23784             /*
23785             if(c == 'left'){
23786                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23787                 return;
23788             }
23789             if(c == 'right'){
23790                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23791                 return;
23792             }
23793             */
23794         });
23795         
23796     },
23797   
23798     onFocus : function()
23799     {
23800         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23801         this.show();
23802     },
23803     
23804     onBlur : function()
23805     {
23806         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23807         this.hide();
23808     },
23809     
23810     show : function()
23811     {
23812         this.picker().show();
23813         this.pop.show();
23814         this.update();
23815         this.place();
23816         
23817         this.fireEvent('show', this, this.date);
23818     },
23819     
23820     hide : function()
23821     {
23822         this.picker().hide();
23823         this.pop.hide();
23824         
23825         this.fireEvent('hide', this, this.date);
23826     },
23827     
23828     setTime : function()
23829     {
23830         this.hide();
23831         this.setValue(this.time.format(this.format));
23832         
23833         this.fireEvent('select', this, this.date);
23834         
23835         
23836     },
23837     
23838     onMousedown: function(e){
23839         e.stopPropagation();
23840         e.preventDefault();
23841     },
23842     
23843     onIncrementHours: function()
23844     {
23845         Roo.log('onIncrementHours');
23846         this.time = this.time.add(Date.HOUR, 1);
23847         this.update();
23848         
23849     },
23850     
23851     onDecrementHours: function()
23852     {
23853         Roo.log('onDecrementHours');
23854         this.time = this.time.add(Date.HOUR, -1);
23855         this.update();
23856     },
23857     
23858     onIncrementMinutes: function()
23859     {
23860         Roo.log('onIncrementMinutes');
23861         this.time = this.time.add(Date.MINUTE, 1);
23862         this.update();
23863     },
23864     
23865     onDecrementMinutes: function()
23866     {
23867         Roo.log('onDecrementMinutes');
23868         this.time = this.time.add(Date.MINUTE, -1);
23869         this.update();
23870     },
23871     
23872     onTogglePeriod: function()
23873     {
23874         Roo.log('onTogglePeriod');
23875         this.time = this.time.add(Date.HOUR, 12);
23876         this.update();
23877     }
23878     
23879    
23880 });
23881  
23882
23883 Roo.apply(Roo.bootstrap.TimeField,  {
23884   
23885     template : {
23886         tag: 'div',
23887         cls: 'datepicker dropdown-menu',
23888         cn: [
23889             {
23890                 tag: 'div',
23891                 cls: 'datepicker-time',
23892                 cn: [
23893                 {
23894                     tag: 'table',
23895                     cls: 'table-condensed',
23896                     cn:[
23897                         {
23898                             tag: 'tbody',
23899                             cn: [
23900                                 {
23901                                     tag: 'tr',
23902                                     cn: [
23903                                     {
23904                                         tag: 'td',
23905                                         colspan: '7'
23906                                     }
23907                                     ]
23908                                 }
23909                             ]
23910                         },
23911                         {
23912                             tag: 'tfoot',
23913                             cn: [
23914                                 {
23915                                     tag: 'tr',
23916                                     cn: [
23917                                     {
23918                                         tag: 'th',
23919                                         colspan: '7',
23920                                         cls: '',
23921                                         cn: [
23922                                             {
23923                                                 tag: 'button',
23924                                                 cls: 'btn btn-info ok',
23925                                                 html: 'OK'
23926                                             }
23927                                         ]
23928                                     }
23929                     
23930                                     ]
23931                                 }
23932                             ]
23933                         }
23934                     ]
23935                 }
23936                 ]
23937             }
23938         ]
23939     }
23940 });
23941
23942  
23943
23944  /*
23945  * - LGPL
23946  *
23947  * MonthField
23948  * 
23949  */
23950
23951 /**
23952  * @class Roo.bootstrap.MonthField
23953  * @extends Roo.bootstrap.Input
23954  * Bootstrap MonthField class
23955  * 
23956  * @cfg {String} language default en
23957  * 
23958  * @constructor
23959  * Create a new MonthField
23960  * @param {Object} config The config object
23961  */
23962
23963 Roo.bootstrap.MonthField = function(config){
23964     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23965     
23966     this.addEvents({
23967         /**
23968          * @event show
23969          * Fires when this field show.
23970          * @param {Roo.bootstrap.MonthField} this
23971          * @param {Mixed} date The date value
23972          */
23973         show : true,
23974         /**
23975          * @event show
23976          * Fires when this field hide.
23977          * @param {Roo.bootstrap.MonthField} this
23978          * @param {Mixed} date The date value
23979          */
23980         hide : true,
23981         /**
23982          * @event select
23983          * Fires when select a date.
23984          * @param {Roo.bootstrap.MonthField} this
23985          * @param {String} oldvalue The old value
23986          * @param {String} newvalue The new value
23987          */
23988         select : true
23989     });
23990 };
23991
23992 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23993     
23994     onRender: function(ct, position)
23995     {
23996         
23997         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23998         
23999         this.language = this.language || 'en';
24000         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24001         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24002         
24003         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24004         this.isInline = false;
24005         this.isInput = true;
24006         this.component = this.el.select('.add-on', true).first() || false;
24007         this.component = (this.component && this.component.length === 0) ? false : this.component;
24008         this.hasInput = this.component && this.inputEL().length;
24009         
24010         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24011         
24012         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24013         
24014         this.picker().on('mousedown', this.onMousedown, this);
24015         this.picker().on('click', this.onClick, this);
24016         
24017         this.picker().addClass('datepicker-dropdown');
24018         
24019         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24020             v.setStyle('width', '189px');
24021         });
24022         
24023         this.fillMonths();
24024         
24025         this.update();
24026         
24027         if(this.isInline) {
24028             this.show();
24029         }
24030         
24031     },
24032     
24033     setValue: function(v, suppressEvent)
24034     {   
24035         var o = this.getValue();
24036         
24037         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24038         
24039         this.update();
24040
24041         if(suppressEvent !== true){
24042             this.fireEvent('select', this, o, v);
24043         }
24044         
24045     },
24046     
24047     getValue: function()
24048     {
24049         return this.value;
24050     },
24051     
24052     onClick: function(e) 
24053     {
24054         e.stopPropagation();
24055         e.preventDefault();
24056         
24057         var target = e.getTarget();
24058         
24059         if(target.nodeName.toLowerCase() === 'i'){
24060             target = Roo.get(target).dom.parentNode;
24061         }
24062         
24063         var nodeName = target.nodeName;
24064         var className = target.className;
24065         var html = target.innerHTML;
24066         
24067         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24068             return;
24069         }
24070         
24071         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24072         
24073         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24074         
24075         this.hide();
24076                         
24077     },
24078     
24079     picker : function()
24080     {
24081         return this.pickerEl;
24082     },
24083     
24084     fillMonths: function()
24085     {    
24086         var i = 0;
24087         var months = this.picker().select('>.datepicker-months td', true).first();
24088         
24089         months.dom.innerHTML = '';
24090         
24091         while (i < 12) {
24092             var month = {
24093                 tag: 'span',
24094                 cls: 'month',
24095                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24096             };
24097             
24098             months.createChild(month);
24099         }
24100         
24101     },
24102     
24103     update: function()
24104     {
24105         var _this = this;
24106         
24107         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24108             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24109         }
24110         
24111         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24112             e.removeClass('active');
24113             
24114             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24115                 e.addClass('active');
24116             }
24117         })
24118     },
24119     
24120     place: function()
24121     {
24122         if(this.isInline) {
24123             return;
24124         }
24125         
24126         this.picker().removeClass(['bottom', 'top']);
24127         
24128         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24129             /*
24130              * place to the top of element!
24131              *
24132              */
24133             
24134             this.picker().addClass('top');
24135             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24136             
24137             return;
24138         }
24139         
24140         this.picker().addClass('bottom');
24141         
24142         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24143     },
24144     
24145     onFocus : function()
24146     {
24147         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24148         this.show();
24149     },
24150     
24151     onBlur : function()
24152     {
24153         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24154         
24155         var d = this.inputEl().getValue();
24156         
24157         this.setValue(d);
24158                 
24159         this.hide();
24160     },
24161     
24162     show : function()
24163     {
24164         this.picker().show();
24165         this.picker().select('>.datepicker-months', true).first().show();
24166         this.update();
24167         this.place();
24168         
24169         this.fireEvent('show', this, this.date);
24170     },
24171     
24172     hide : function()
24173     {
24174         if(this.isInline) {
24175             return;
24176         }
24177         this.picker().hide();
24178         this.fireEvent('hide', this, this.date);
24179         
24180     },
24181     
24182     onMousedown: function(e)
24183     {
24184         e.stopPropagation();
24185         e.preventDefault();
24186     },
24187     
24188     keyup: function(e)
24189     {
24190         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24191         this.update();
24192     },
24193
24194     fireKey: function(e)
24195     {
24196         if (!this.picker().isVisible()){
24197             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24198                 this.show();
24199             }
24200             return;
24201         }
24202         
24203         var dir;
24204         
24205         switch(e.keyCode){
24206             case 27: // escape
24207                 this.hide();
24208                 e.preventDefault();
24209                 break;
24210             case 37: // left
24211             case 39: // right
24212                 dir = e.keyCode == 37 ? -1 : 1;
24213                 
24214                 this.vIndex = this.vIndex + dir;
24215                 
24216                 if(this.vIndex < 0){
24217                     this.vIndex = 0;
24218                 }
24219                 
24220                 if(this.vIndex > 11){
24221                     this.vIndex = 11;
24222                 }
24223                 
24224                 if(isNaN(this.vIndex)){
24225                     this.vIndex = 0;
24226                 }
24227                 
24228                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24229                 
24230                 break;
24231             case 38: // up
24232             case 40: // down
24233                 
24234                 dir = e.keyCode == 38 ? -1 : 1;
24235                 
24236                 this.vIndex = this.vIndex + dir * 4;
24237                 
24238                 if(this.vIndex < 0){
24239                     this.vIndex = 0;
24240                 }
24241                 
24242                 if(this.vIndex > 11){
24243                     this.vIndex = 11;
24244                 }
24245                 
24246                 if(isNaN(this.vIndex)){
24247                     this.vIndex = 0;
24248                 }
24249                 
24250                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24251                 break;
24252                 
24253             case 13: // enter
24254                 
24255                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24256                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24257                 }
24258                 
24259                 this.hide();
24260                 e.preventDefault();
24261                 break;
24262             case 9: // tab
24263                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24264                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24265                 }
24266                 this.hide();
24267                 break;
24268             case 16: // shift
24269             case 17: // ctrl
24270             case 18: // alt
24271                 break;
24272             default :
24273                 this.hide();
24274                 
24275         }
24276     },
24277     
24278     remove: function() 
24279     {
24280         this.picker().remove();
24281     }
24282    
24283 });
24284
24285 Roo.apply(Roo.bootstrap.MonthField,  {
24286     
24287     content : {
24288         tag: 'tbody',
24289         cn: [
24290         {
24291             tag: 'tr',
24292             cn: [
24293             {
24294                 tag: 'td',
24295                 colspan: '7'
24296             }
24297             ]
24298         }
24299         ]
24300     },
24301     
24302     dates:{
24303         en: {
24304             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24305             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24306         }
24307     }
24308 });
24309
24310 Roo.apply(Roo.bootstrap.MonthField,  {
24311   
24312     template : {
24313         tag: 'div',
24314         cls: 'datepicker dropdown-menu roo-dynamic',
24315         cn: [
24316             {
24317                 tag: 'div',
24318                 cls: 'datepicker-months',
24319                 cn: [
24320                 {
24321                     tag: 'table',
24322                     cls: 'table-condensed',
24323                     cn:[
24324                         Roo.bootstrap.DateField.content
24325                     ]
24326                 }
24327                 ]
24328             }
24329         ]
24330     }
24331 });
24332
24333  
24334
24335  
24336  /*
24337  * - LGPL
24338  *
24339  * CheckBox
24340  * 
24341  */
24342
24343 /**
24344  * @class Roo.bootstrap.CheckBox
24345  * @extends Roo.bootstrap.Input
24346  * Bootstrap CheckBox class
24347  * 
24348  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24349  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24350  * @cfg {String} boxLabel The text that appears beside the checkbox
24351  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24352  * @cfg {Boolean} checked initnal the element
24353  * @cfg {Boolean} inline inline the element (default false)
24354  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24355  * @cfg {String} tooltip label tooltip
24356  * 
24357  * @constructor
24358  * Create a new CheckBox
24359  * @param {Object} config The config object
24360  */
24361
24362 Roo.bootstrap.CheckBox = function(config){
24363     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24364    
24365     this.addEvents({
24366         /**
24367         * @event check
24368         * Fires when the element is checked or unchecked.
24369         * @param {Roo.bootstrap.CheckBox} this This input
24370         * @param {Boolean} checked The new checked value
24371         */
24372        check : true,
24373        /**
24374         * @event click
24375         * Fires when the element is click.
24376         * @param {Roo.bootstrap.CheckBox} this This input
24377         */
24378        click : true
24379     });
24380     
24381 };
24382
24383 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24384   
24385     inputType: 'checkbox',
24386     inputValue: 1,
24387     valueOff: 0,
24388     boxLabel: false,
24389     checked: false,
24390     weight : false,
24391     inline: false,
24392     tooltip : '',
24393     
24394     // checkbox success does not make any sense really.. 
24395     invalidClass : "",
24396     validClass : "",
24397     
24398     
24399     getAutoCreate : function()
24400     {
24401         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24402         
24403         var id = Roo.id();
24404         
24405         var cfg = {};
24406         
24407         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24408         
24409         if(this.inline){
24410             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24411         }
24412         
24413         var input =  {
24414             tag: 'input',
24415             id : id,
24416             type : this.inputType,
24417             value : this.inputValue,
24418             cls : 'roo-' + this.inputType, //'form-box',
24419             placeholder : this.placeholder || ''
24420             
24421         };
24422         
24423         if(this.inputType != 'radio'){
24424             var hidden =  {
24425                 tag: 'input',
24426                 type : 'hidden',
24427                 cls : 'roo-hidden-value',
24428                 value : this.checked ? this.inputValue : this.valueOff
24429             };
24430         }
24431         
24432             
24433         if (this.weight) { // Validity check?
24434             cfg.cls += " " + this.inputType + "-" + this.weight;
24435         }
24436         
24437         if (this.disabled) {
24438             input.disabled=true;
24439         }
24440         
24441         if(this.checked){
24442             input.checked = this.checked;
24443         }
24444         
24445         if (this.name) {
24446             
24447             input.name = this.name;
24448             
24449             if(this.inputType != 'radio'){
24450                 hidden.name = this.name;
24451                 input.name = '_hidden_' + this.name;
24452             }
24453         }
24454         
24455         if (this.size) {
24456             input.cls += ' input-' + this.size;
24457         }
24458         
24459         var settings=this;
24460         
24461         ['xs','sm','md','lg'].map(function(size){
24462             if (settings[size]) {
24463                 cfg.cls += ' col-' + size + '-' + settings[size];
24464             }
24465         });
24466         
24467         var inputblock = input;
24468          
24469         if (this.before || this.after) {
24470             
24471             inputblock = {
24472                 cls : 'input-group',
24473                 cn :  [] 
24474             };
24475             
24476             if (this.before) {
24477                 inputblock.cn.push({
24478                     tag :'span',
24479                     cls : 'input-group-addon',
24480                     html : this.before
24481                 });
24482             }
24483             
24484             inputblock.cn.push(input);
24485             
24486             if(this.inputType != 'radio'){
24487                 inputblock.cn.push(hidden);
24488             }
24489             
24490             if (this.after) {
24491                 inputblock.cn.push({
24492                     tag :'span',
24493                     cls : 'input-group-addon',
24494                     html : this.after
24495                 });
24496             }
24497             
24498         }
24499         var boxLabelCfg = false;
24500         
24501         if(this.boxLabel){
24502            
24503             boxLabelCfg = {
24504                 tag: 'label',
24505                 //'for': id, // box label is handled by onclick - so no for...
24506                 cls: 'box-label',
24507                 html: this.boxLabel
24508             };
24509             if(this.tooltip){
24510                 boxLabelCfg.tooltip = this.tooltip;
24511             }
24512              
24513         }
24514         
24515         
24516         if (align ==='left' && this.fieldLabel.length) {
24517 //                Roo.log("left and has label");
24518             cfg.cn = [
24519                 {
24520                     tag: 'label',
24521                     'for' :  id,
24522                     cls : 'control-label',
24523                     html : this.fieldLabel
24524                 },
24525                 {
24526                     cls : "", 
24527                     cn: [
24528                         inputblock
24529                     ]
24530                 }
24531             ];
24532             
24533             if (boxLabelCfg) {
24534                 cfg.cn[1].cn.push(boxLabelCfg);
24535             }
24536             
24537             if(this.labelWidth > 12){
24538                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24539             }
24540             
24541             if(this.labelWidth < 13 && this.labelmd == 0){
24542                 this.labelmd = this.labelWidth;
24543             }
24544             
24545             if(this.labellg > 0){
24546                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24547                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24548             }
24549             
24550             if(this.labelmd > 0){
24551                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24552                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24553             }
24554             
24555             if(this.labelsm > 0){
24556                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24557                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24558             }
24559             
24560             if(this.labelxs > 0){
24561                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24562                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24563             }
24564             
24565         } else if ( this.fieldLabel.length) {
24566 //                Roo.log(" label");
24567                 cfg.cn = [
24568                    
24569                     {
24570                         tag: this.boxLabel ? 'span' : 'label',
24571                         'for': id,
24572                         cls: 'control-label box-input-label',
24573                         //cls : 'input-group-addon',
24574                         html : this.fieldLabel
24575                     },
24576                     
24577                     inputblock
24578                     
24579                 ];
24580                 if (boxLabelCfg) {
24581                     cfg.cn.push(boxLabelCfg);
24582                 }
24583
24584         } else {
24585             
24586 //                Roo.log(" no label && no align");
24587                 cfg.cn = [  inputblock ] ;
24588                 if (boxLabelCfg) {
24589                     cfg.cn.push(boxLabelCfg);
24590                 }
24591
24592                 
24593         }
24594         
24595        
24596         
24597         if(this.inputType != 'radio'){
24598             cfg.cn.push(hidden);
24599         }
24600         
24601         return cfg;
24602         
24603     },
24604     
24605     /**
24606      * return the real input element.
24607      */
24608     inputEl: function ()
24609     {
24610         return this.el.select('input.roo-' + this.inputType,true).first();
24611     },
24612     hiddenEl: function ()
24613     {
24614         return this.el.select('input.roo-hidden-value',true).first();
24615     },
24616     
24617     labelEl: function()
24618     {
24619         return this.el.select('label.control-label',true).first();
24620     },
24621     /* depricated... */
24622     
24623     label: function()
24624     {
24625         return this.labelEl();
24626     },
24627     
24628     boxLabelEl: function()
24629     {
24630         return this.el.select('label.box-label',true).first();
24631     },
24632     
24633     initEvents : function()
24634     {
24635 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24636         
24637         this.inputEl().on('click', this.onClick,  this);
24638         
24639         if (this.boxLabel) { 
24640             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24641         }
24642         
24643         this.startValue = this.getValue();
24644         
24645         if(this.groupId){
24646             Roo.bootstrap.CheckBox.register(this);
24647         }
24648     },
24649     
24650     onClick : function(e)
24651     {   
24652         if(this.fireEvent('click', this, e) !== false){
24653             this.setChecked(!this.checked);
24654         }
24655         
24656     },
24657     
24658     setChecked : function(state,suppressEvent)
24659     {
24660         this.startValue = this.getValue();
24661
24662         if(this.inputType == 'radio'){
24663             
24664             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24665                 e.dom.checked = false;
24666             });
24667             
24668             this.inputEl().dom.checked = true;
24669             
24670             this.inputEl().dom.value = this.inputValue;
24671             
24672             if(suppressEvent !== true){
24673                 this.fireEvent('check', this, true);
24674             }
24675             
24676             this.validate();
24677             
24678             return;
24679         }
24680         
24681         this.checked = state;
24682         
24683         this.inputEl().dom.checked = state;
24684         
24685         
24686         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24687         
24688         if(suppressEvent !== true){
24689             this.fireEvent('check', this, state);
24690         }
24691         
24692         this.validate();
24693     },
24694     
24695     getValue : function()
24696     {
24697         if(this.inputType == 'radio'){
24698             return this.getGroupValue();
24699         }
24700         
24701         return this.hiddenEl().dom.value;
24702         
24703     },
24704     
24705     getGroupValue : function()
24706     {
24707         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24708             return '';
24709         }
24710         
24711         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24712     },
24713     
24714     setValue : function(v,suppressEvent)
24715     {
24716         if(this.inputType == 'radio'){
24717             this.setGroupValue(v, suppressEvent);
24718             return;
24719         }
24720         
24721         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24722         
24723         this.validate();
24724     },
24725     
24726     setGroupValue : function(v, suppressEvent)
24727     {
24728         this.startValue = this.getValue();
24729         
24730         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24731             e.dom.checked = false;
24732             
24733             if(e.dom.value == v){
24734                 e.dom.checked = true;
24735             }
24736         });
24737         
24738         if(suppressEvent !== true){
24739             this.fireEvent('check', this, true);
24740         }
24741
24742         this.validate();
24743         
24744         return;
24745     },
24746     
24747     validate : function()
24748     {
24749         if(this.getVisibilityEl().hasClass('hidden')){
24750             return true;
24751         }
24752         
24753         if(
24754                 this.disabled || 
24755                 (this.inputType == 'radio' && this.validateRadio()) ||
24756                 (this.inputType == 'checkbox' && this.validateCheckbox())
24757         ){
24758             this.markValid();
24759             return true;
24760         }
24761         
24762         this.markInvalid();
24763         return false;
24764     },
24765     
24766     validateRadio : function()
24767     {
24768         if(this.getVisibilityEl().hasClass('hidden')){
24769             return true;
24770         }
24771         
24772         if(this.allowBlank){
24773             return true;
24774         }
24775         
24776         var valid = false;
24777         
24778         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24779             if(!e.dom.checked){
24780                 return;
24781             }
24782             
24783             valid = true;
24784             
24785             return false;
24786         });
24787         
24788         return valid;
24789     },
24790     
24791     validateCheckbox : function()
24792     {
24793         if(!this.groupId){
24794             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24795             //return (this.getValue() == this.inputValue) ? true : false;
24796         }
24797         
24798         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24799         
24800         if(!group){
24801             return false;
24802         }
24803         
24804         var r = false;
24805         
24806         for(var i in group){
24807             if(group[i].el.isVisible(true)){
24808                 r = false;
24809                 break;
24810             }
24811             
24812             r = true;
24813         }
24814         
24815         for(var i in group){
24816             if(r){
24817                 break;
24818             }
24819             
24820             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24821         }
24822         
24823         return r;
24824     },
24825     
24826     /**
24827      * Mark this field as valid
24828      */
24829     markValid : function()
24830     {
24831         var _this = this;
24832         
24833         this.fireEvent('valid', this);
24834         
24835         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24836         
24837         if(this.groupId){
24838             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24839         }
24840         
24841         if(label){
24842             label.markValid();
24843         }
24844
24845         if(this.inputType == 'radio'){
24846             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24847                 var fg = e.findParent('.form-group', false, true);
24848                 if (Roo.bootstrap.version == 3) {
24849                     fg.removeClass([_this.invalidClass, _this.validClass]);
24850                     fg.addClass(_this.validClass);
24851                 } else {
24852                     fg.removeClass(['is-valid', 'is-invalid']);
24853                     fg.addClass('is-valid');
24854                 }
24855             });
24856             
24857             return;
24858         }
24859
24860         if(!this.groupId){
24861             var fg = this.el.findParent('.form-group', false, true);
24862             if (Roo.bootstrap.version == 3) {
24863                 fg.removeClass([this.invalidClass, this.validClass]);
24864                 fg.addClass(this.validClass);
24865             } else {
24866                 fg.removeClass(['is-valid', 'is-invalid']);
24867                 fg.addClass('is-valid');
24868             }
24869             return;
24870         }
24871         
24872         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24873         
24874         if(!group){
24875             return;
24876         }
24877         
24878         for(var i in group){
24879             var fg = group[i].el.findParent('.form-group', false, true);
24880             if (Roo.bootstrap.version == 3) {
24881                 fg.removeClass([this.invalidClass, this.validClass]);
24882                 fg.addClass(this.validClass);
24883             } else {
24884                 fg.removeClass(['is-valid', 'is-invalid']);
24885                 fg.addClass('is-valid');
24886             }
24887         }
24888     },
24889     
24890      /**
24891      * Mark this field as invalid
24892      * @param {String} msg The validation message
24893      */
24894     markInvalid : function(msg)
24895     {
24896         if(this.allowBlank){
24897             return;
24898         }
24899         
24900         var _this = this;
24901         
24902         this.fireEvent('invalid', this, msg);
24903         
24904         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24905         
24906         if(this.groupId){
24907             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24908         }
24909         
24910         if(label){
24911             label.markInvalid();
24912         }
24913             
24914         if(this.inputType == 'radio'){
24915             
24916             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24917                 var fg = e.findParent('.form-group', false, true);
24918                 if (Roo.bootstrap.version == 3) {
24919                     fg.removeClass([_this.invalidClass, _this.validClass]);
24920                     fg.addClass(_this.invalidClass);
24921                 } else {
24922                     fg.removeClass(['is-invalid', 'is-valid']);
24923                     fg.addClass('is-invalid');
24924                 }
24925             });
24926             
24927             return;
24928         }
24929         
24930         if(!this.groupId){
24931             var fg = this.el.findParent('.form-group', false, true);
24932             if (Roo.bootstrap.version == 3) {
24933                 fg.removeClass([_this.invalidClass, _this.validClass]);
24934                 fg.addClass(_this.invalidClass);
24935             } else {
24936                 fg.removeClass(['is-invalid', 'is-valid']);
24937                 fg.addClass('is-invalid');
24938             }
24939             return;
24940         }
24941         
24942         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24943         
24944         if(!group){
24945             return;
24946         }
24947         
24948         for(var i in group){
24949             var fg = group[i].el.findParent('.form-group', false, true);
24950             if (Roo.bootstrap.version == 3) {
24951                 fg.removeClass([_this.invalidClass, _this.validClass]);
24952                 fg.addClass(_this.invalidClass);
24953             } else {
24954                 fg.removeClass(['is-invalid', 'is-valid']);
24955                 fg.addClass('is-invalid');
24956             }
24957         }
24958         
24959     },
24960     
24961     clearInvalid : function()
24962     {
24963         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24964         
24965         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24966         
24967         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24968         
24969         if (label && label.iconEl) {
24970             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24971             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24972         }
24973     },
24974     
24975     disable : function()
24976     {
24977         if(this.inputType != 'radio'){
24978             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24979             return;
24980         }
24981         
24982         var _this = this;
24983         
24984         if(this.rendered){
24985             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24986                 _this.getActionEl().addClass(this.disabledClass);
24987                 e.dom.disabled = true;
24988             });
24989         }
24990         
24991         this.disabled = true;
24992         this.fireEvent("disable", this);
24993         return this;
24994     },
24995
24996     enable : function()
24997     {
24998         if(this.inputType != 'radio'){
24999             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25000             return;
25001         }
25002         
25003         var _this = this;
25004         
25005         if(this.rendered){
25006             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25007                 _this.getActionEl().removeClass(this.disabledClass);
25008                 e.dom.disabled = false;
25009             });
25010         }
25011         
25012         this.disabled = false;
25013         this.fireEvent("enable", this);
25014         return this;
25015     },
25016     
25017     setBoxLabel : function(v)
25018     {
25019         this.boxLabel = v;
25020         
25021         if(this.rendered){
25022             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25023         }
25024     }
25025
25026 });
25027
25028 Roo.apply(Roo.bootstrap.CheckBox, {
25029     
25030     groups: {},
25031     
25032      /**
25033     * register a CheckBox Group
25034     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25035     */
25036     register : function(checkbox)
25037     {
25038         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25039             this.groups[checkbox.groupId] = {};
25040         }
25041         
25042         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25043             return;
25044         }
25045         
25046         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25047         
25048     },
25049     /**
25050     * fetch a CheckBox Group based on the group ID
25051     * @param {string} the group ID
25052     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25053     */
25054     get: function(groupId) {
25055         if (typeof(this.groups[groupId]) == 'undefined') {
25056             return false;
25057         }
25058         
25059         return this.groups[groupId] ;
25060     }
25061     
25062     
25063 });
25064 /*
25065  * - LGPL
25066  *
25067  * RadioItem
25068  * 
25069  */
25070
25071 /**
25072  * @class Roo.bootstrap.Radio
25073  * @extends Roo.bootstrap.Component
25074  * Bootstrap Radio class
25075  * @cfg {String} boxLabel - the label associated
25076  * @cfg {String} value - the value of radio
25077  * 
25078  * @constructor
25079  * Create a new Radio
25080  * @param {Object} config The config object
25081  */
25082 Roo.bootstrap.Radio = function(config){
25083     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25084     
25085 };
25086
25087 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25088     
25089     boxLabel : '',
25090     
25091     value : '',
25092     
25093     getAutoCreate : function()
25094     {
25095         var cfg = {
25096             tag : 'div',
25097             cls : 'form-group radio',
25098             cn : [
25099                 {
25100                     tag : 'label',
25101                     cls : 'box-label',
25102                     html : this.boxLabel
25103                 }
25104             ]
25105         };
25106         
25107         return cfg;
25108     },
25109     
25110     initEvents : function() 
25111     {
25112         this.parent().register(this);
25113         
25114         this.el.on('click', this.onClick, this);
25115         
25116     },
25117     
25118     onClick : function(e)
25119     {
25120         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25121             this.setChecked(true);
25122         }
25123     },
25124     
25125     setChecked : function(state, suppressEvent)
25126     {
25127         this.parent().setValue(this.value, suppressEvent);
25128         
25129     },
25130     
25131     setBoxLabel : function(v)
25132     {
25133         this.boxLabel = v;
25134         
25135         if(this.rendered){
25136             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25137         }
25138     }
25139     
25140 });
25141  
25142
25143  /*
25144  * - LGPL
25145  *
25146  * Input
25147  * 
25148  */
25149
25150 /**
25151  * @class Roo.bootstrap.SecurePass
25152  * @extends Roo.bootstrap.Input
25153  * Bootstrap SecurePass class
25154  *
25155  * 
25156  * @constructor
25157  * Create a new SecurePass
25158  * @param {Object} config The config object
25159  */
25160  
25161 Roo.bootstrap.SecurePass = function (config) {
25162     // these go here, so the translation tool can replace them..
25163     this.errors = {
25164         PwdEmpty: "Please type a password, and then retype it to confirm.",
25165         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25166         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25167         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25168         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25169         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25170         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25171         TooWeak: "Your password is Too Weak."
25172     },
25173     this.meterLabel = "Password strength:";
25174     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25175     this.meterClass = [
25176         "roo-password-meter-tooweak", 
25177         "roo-password-meter-weak", 
25178         "roo-password-meter-medium", 
25179         "roo-password-meter-strong", 
25180         "roo-password-meter-grey"
25181     ];
25182     
25183     this.errors = {};
25184     
25185     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25186 }
25187
25188 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25189     /**
25190      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25191      * {
25192      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25193      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25194      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25195      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25196      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25197      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25198      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25199      * })
25200      */
25201     // private
25202     
25203     meterWidth: 300,
25204     errorMsg :'',    
25205     errors: false,
25206     imageRoot: '/',
25207     /**
25208      * @cfg {String/Object} Label for the strength meter (defaults to
25209      * 'Password strength:')
25210      */
25211     // private
25212     meterLabel: '',
25213     /**
25214      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25215      * ['Weak', 'Medium', 'Strong'])
25216      */
25217     // private    
25218     pwdStrengths: false,    
25219     // private
25220     strength: 0,
25221     // private
25222     _lastPwd: null,
25223     // private
25224     kCapitalLetter: 0,
25225     kSmallLetter: 1,
25226     kDigit: 2,
25227     kPunctuation: 3,
25228     
25229     insecure: false,
25230     // private
25231     initEvents: function ()
25232     {
25233         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25234
25235         if (this.el.is('input[type=password]') && Roo.isSafari) {
25236             this.el.on('keydown', this.SafariOnKeyDown, this);
25237         }
25238
25239         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25240     },
25241     // private
25242     onRender: function (ct, position)
25243     {
25244         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25245         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25246         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25247
25248         this.trigger.createChild({
25249                    cn: [
25250                     {
25251                     //id: 'PwdMeter',
25252                     tag: 'div',
25253                     cls: 'roo-password-meter-grey col-xs-12',
25254                     style: {
25255                         //width: 0,
25256                         //width: this.meterWidth + 'px'                                                
25257                         }
25258                     },
25259                     {                            
25260                          cls: 'roo-password-meter-text'                          
25261                     }
25262                 ]            
25263         });
25264
25265          
25266         if (this.hideTrigger) {
25267             this.trigger.setDisplayed(false);
25268         }
25269         this.setSize(this.width || '', this.height || '');
25270     },
25271     // private
25272     onDestroy: function ()
25273     {
25274         if (this.trigger) {
25275             this.trigger.removeAllListeners();
25276             this.trigger.remove();
25277         }
25278         if (this.wrap) {
25279             this.wrap.remove();
25280         }
25281         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25282     },
25283     // private
25284     checkStrength: function ()
25285     {
25286         var pwd = this.inputEl().getValue();
25287         if (pwd == this._lastPwd) {
25288             return;
25289         }
25290
25291         var strength;
25292         if (this.ClientSideStrongPassword(pwd)) {
25293             strength = 3;
25294         } else if (this.ClientSideMediumPassword(pwd)) {
25295             strength = 2;
25296         } else if (this.ClientSideWeakPassword(pwd)) {
25297             strength = 1;
25298         } else {
25299             strength = 0;
25300         }
25301         
25302         Roo.log('strength1: ' + strength);
25303         
25304         //var pm = this.trigger.child('div/div/div').dom;
25305         var pm = this.trigger.child('div/div');
25306         pm.removeClass(this.meterClass);
25307         pm.addClass(this.meterClass[strength]);
25308                 
25309         
25310         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25311                 
25312         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25313         
25314         this._lastPwd = pwd;
25315     },
25316     reset: function ()
25317     {
25318         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25319         
25320         this._lastPwd = '';
25321         
25322         var pm = this.trigger.child('div/div');
25323         pm.removeClass(this.meterClass);
25324         pm.addClass('roo-password-meter-grey');        
25325         
25326         
25327         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25328         
25329         pt.innerHTML = '';
25330         this.inputEl().dom.type='password';
25331     },
25332     // private
25333     validateValue: function (value)
25334     {
25335         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25336             return false;
25337         }
25338         if (value.length == 0) {
25339             if (this.allowBlank) {
25340                 this.clearInvalid();
25341                 return true;
25342             }
25343
25344             this.markInvalid(this.errors.PwdEmpty);
25345             this.errorMsg = this.errors.PwdEmpty;
25346             return false;
25347         }
25348         
25349         if(this.insecure){
25350             return true;
25351         }
25352         
25353         if (!value.match(/[\x21-\x7e]+/)) {
25354             this.markInvalid(this.errors.PwdBadChar);
25355             this.errorMsg = this.errors.PwdBadChar;
25356             return false;
25357         }
25358         if (value.length < 6) {
25359             this.markInvalid(this.errors.PwdShort);
25360             this.errorMsg = this.errors.PwdShort;
25361             return false;
25362         }
25363         if (value.length > 16) {
25364             this.markInvalid(this.errors.PwdLong);
25365             this.errorMsg = this.errors.PwdLong;
25366             return false;
25367         }
25368         var strength;
25369         if (this.ClientSideStrongPassword(value)) {
25370             strength = 3;
25371         } else if (this.ClientSideMediumPassword(value)) {
25372             strength = 2;
25373         } else if (this.ClientSideWeakPassword(value)) {
25374             strength = 1;
25375         } else {
25376             strength = 0;
25377         }
25378
25379         
25380         if (strength < 2) {
25381             //this.markInvalid(this.errors.TooWeak);
25382             this.errorMsg = this.errors.TooWeak;
25383             //return false;
25384         }
25385         
25386         
25387         console.log('strength2: ' + strength);
25388         
25389         //var pm = this.trigger.child('div/div/div').dom;
25390         
25391         var pm = this.trigger.child('div/div');
25392         pm.removeClass(this.meterClass);
25393         pm.addClass(this.meterClass[strength]);
25394                 
25395         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25396                 
25397         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25398         
25399         this.errorMsg = ''; 
25400         return true;
25401     },
25402     // private
25403     CharacterSetChecks: function (type)
25404     {
25405         this.type = type;
25406         this.fResult = false;
25407     },
25408     // private
25409     isctype: function (character, type)
25410     {
25411         switch (type) {  
25412             case this.kCapitalLetter:
25413                 if (character >= 'A' && character <= 'Z') {
25414                     return true;
25415                 }
25416                 break;
25417             
25418             case this.kSmallLetter:
25419                 if (character >= 'a' && character <= 'z') {
25420                     return true;
25421                 }
25422                 break;
25423             
25424             case this.kDigit:
25425                 if (character >= '0' && character <= '9') {
25426                     return true;
25427                 }
25428                 break;
25429             
25430             case this.kPunctuation:
25431                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25432                     return true;
25433                 }
25434                 break;
25435             
25436             default:
25437                 return false;
25438         }
25439
25440     },
25441     // private
25442     IsLongEnough: function (pwd, size)
25443     {
25444         return !(pwd == null || isNaN(size) || pwd.length < size);
25445     },
25446     // private
25447     SpansEnoughCharacterSets: function (word, nb)
25448     {
25449         if (!this.IsLongEnough(word, nb))
25450         {
25451             return false;
25452         }
25453
25454         var characterSetChecks = new Array(
25455             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25456             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25457         );
25458         
25459         for (var index = 0; index < word.length; ++index) {
25460             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25461                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25462                     characterSetChecks[nCharSet].fResult = true;
25463                     break;
25464                 }
25465             }
25466         }
25467
25468         var nCharSets = 0;
25469         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25470             if (characterSetChecks[nCharSet].fResult) {
25471                 ++nCharSets;
25472             }
25473         }
25474
25475         if (nCharSets < nb) {
25476             return false;
25477         }
25478         return true;
25479     },
25480     // private
25481     ClientSideStrongPassword: function (pwd)
25482     {
25483         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25484     },
25485     // private
25486     ClientSideMediumPassword: function (pwd)
25487     {
25488         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25489     },
25490     // private
25491     ClientSideWeakPassword: function (pwd)
25492     {
25493         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25494     }
25495           
25496 })//<script type="text/javascript">
25497
25498 /*
25499  * Based  Ext JS Library 1.1.1
25500  * Copyright(c) 2006-2007, Ext JS, LLC.
25501  * LGPL
25502  *
25503  */
25504  
25505 /**
25506  * @class Roo.HtmlEditorCore
25507  * @extends Roo.Component
25508  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25509  *
25510  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25511  */
25512
25513 Roo.HtmlEditorCore = function(config){
25514     
25515     
25516     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25517     
25518     
25519     this.addEvents({
25520         /**
25521          * @event initialize
25522          * Fires when the editor is fully initialized (including the iframe)
25523          * @param {Roo.HtmlEditorCore} this
25524          */
25525         initialize: true,
25526         /**
25527          * @event activate
25528          * Fires when the editor is first receives the focus. Any insertion must wait
25529          * until after this event.
25530          * @param {Roo.HtmlEditorCore} this
25531          */
25532         activate: true,
25533          /**
25534          * @event beforesync
25535          * Fires before the textarea is updated with content from the editor iframe. Return false
25536          * to cancel the sync.
25537          * @param {Roo.HtmlEditorCore} this
25538          * @param {String} html
25539          */
25540         beforesync: true,
25541          /**
25542          * @event beforepush
25543          * Fires before the iframe editor is updated with content from the textarea. Return false
25544          * to cancel the push.
25545          * @param {Roo.HtmlEditorCore} this
25546          * @param {String} html
25547          */
25548         beforepush: true,
25549          /**
25550          * @event sync
25551          * Fires when the textarea is updated with content from the editor iframe.
25552          * @param {Roo.HtmlEditorCore} this
25553          * @param {String} html
25554          */
25555         sync: true,
25556          /**
25557          * @event push
25558          * Fires when the iframe editor is updated with content from the textarea.
25559          * @param {Roo.HtmlEditorCore} this
25560          * @param {String} html
25561          */
25562         push: true,
25563         
25564         /**
25565          * @event editorevent
25566          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25567          * @param {Roo.HtmlEditorCore} this
25568          */
25569         editorevent: true
25570         
25571     });
25572     
25573     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25574     
25575     // defaults : white / black...
25576     this.applyBlacklists();
25577     
25578     
25579     
25580 };
25581
25582
25583 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25584
25585
25586      /**
25587      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25588      */
25589     
25590     owner : false,
25591     
25592      /**
25593      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25594      *                        Roo.resizable.
25595      */
25596     resizable : false,
25597      /**
25598      * @cfg {Number} height (in pixels)
25599      */   
25600     height: 300,
25601    /**
25602      * @cfg {Number} width (in pixels)
25603      */   
25604     width: 500,
25605     
25606     /**
25607      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25608      * 
25609      */
25610     stylesheets: false,
25611     
25612     /**
25613      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25614      */
25615     allowComments: false,
25616     // id of frame..
25617     frameId: false,
25618     
25619     // private properties
25620     validationEvent : false,
25621     deferHeight: true,
25622     initialized : false,
25623     activated : false,
25624     sourceEditMode : false,
25625     onFocus : Roo.emptyFn,
25626     iframePad:3,
25627     hideMode:'offsets',
25628     
25629     clearUp: true,
25630     
25631     // blacklist + whitelisted elements..
25632     black: false,
25633     white: false,
25634      
25635     bodyCls : '',
25636
25637     /**
25638      * Protected method that will not generally be called directly. It
25639      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25640      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25641      */
25642     getDocMarkup : function(){
25643         // body styles..
25644         var st = '';
25645         
25646         // inherit styels from page...?? 
25647         if (this.stylesheets === false) {
25648             
25649             Roo.get(document.head).select('style').each(function(node) {
25650                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25651             });
25652             
25653             Roo.get(document.head).select('link').each(function(node) { 
25654                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25655             });
25656             
25657         } else if (!this.stylesheets.length) {
25658                 // simple..
25659                 st = '<style type="text/css">' +
25660                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25661                    '</style>';
25662         } else {
25663             for (var i in this.stylesheets) { 
25664                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25665             }
25666             
25667         }
25668         
25669         st +=  '<style type="text/css">' +
25670             'IMG { cursor: pointer } ' +
25671         '</style>';
25672
25673         var cls = 'roo-htmleditor-body';
25674         
25675         if(this.bodyCls.length){
25676             cls += ' ' + this.bodyCls;
25677         }
25678         
25679         return '<html><head>' + st  +
25680             //<style type="text/css">' +
25681             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25682             //'</style>' +
25683             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25684     },
25685
25686     // private
25687     onRender : function(ct, position)
25688     {
25689         var _t = this;
25690         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25691         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25692         
25693         
25694         this.el.dom.style.border = '0 none';
25695         this.el.dom.setAttribute('tabIndex', -1);
25696         this.el.addClass('x-hidden hide');
25697         
25698         
25699         
25700         if(Roo.isIE){ // fix IE 1px bogus margin
25701             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25702         }
25703        
25704         
25705         this.frameId = Roo.id();
25706         
25707          
25708         
25709         var iframe = this.owner.wrap.createChild({
25710             tag: 'iframe',
25711             cls: 'form-control', // bootstrap..
25712             id: this.frameId,
25713             name: this.frameId,
25714             frameBorder : 'no',
25715             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25716         }, this.el
25717         );
25718         
25719         
25720         this.iframe = iframe.dom;
25721
25722          this.assignDocWin();
25723         
25724         this.doc.designMode = 'on';
25725        
25726         this.doc.open();
25727         this.doc.write(this.getDocMarkup());
25728         this.doc.close();
25729
25730         
25731         var task = { // must defer to wait for browser to be ready
25732             run : function(){
25733                 //console.log("run task?" + this.doc.readyState);
25734                 this.assignDocWin();
25735                 if(this.doc.body || this.doc.readyState == 'complete'){
25736                     try {
25737                         this.doc.designMode="on";
25738                     } catch (e) {
25739                         return;
25740                     }
25741                     Roo.TaskMgr.stop(task);
25742                     this.initEditor.defer(10, this);
25743                 }
25744             },
25745             interval : 10,
25746             duration: 10000,
25747             scope: this
25748         };
25749         Roo.TaskMgr.start(task);
25750
25751     },
25752
25753     // private
25754     onResize : function(w, h)
25755     {
25756          Roo.log('resize: ' +w + ',' + h );
25757         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25758         if(!this.iframe){
25759             return;
25760         }
25761         if(typeof w == 'number'){
25762             
25763             this.iframe.style.width = w + 'px';
25764         }
25765         if(typeof h == 'number'){
25766             
25767             this.iframe.style.height = h + 'px';
25768             if(this.doc){
25769                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25770             }
25771         }
25772         
25773     },
25774
25775     /**
25776      * Toggles the editor between standard and source edit mode.
25777      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25778      */
25779     toggleSourceEdit : function(sourceEditMode){
25780         
25781         this.sourceEditMode = sourceEditMode === true;
25782         
25783         if(this.sourceEditMode){
25784  
25785             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25786             
25787         }else{
25788             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25789             //this.iframe.className = '';
25790             this.deferFocus();
25791         }
25792         //this.setSize(this.owner.wrap.getSize());
25793         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25794     },
25795
25796     
25797   
25798
25799     /**
25800      * Protected method that will not generally be called directly. If you need/want
25801      * custom HTML cleanup, this is the method you should override.
25802      * @param {String} html The HTML to be cleaned
25803      * return {String} The cleaned HTML
25804      */
25805     cleanHtml : function(html){
25806         html = String(html);
25807         if(html.length > 5){
25808             if(Roo.isSafari){ // strip safari nonsense
25809                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25810             }
25811         }
25812         if(html == '&nbsp;'){
25813             html = '';
25814         }
25815         return html;
25816     },
25817
25818     /**
25819      * HTML Editor -> Textarea
25820      * Protected method that will not generally be called directly. Syncs the contents
25821      * of the editor iframe with the textarea.
25822      */
25823     syncValue : function(){
25824         if(this.initialized){
25825             var bd = (this.doc.body || this.doc.documentElement);
25826             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25827             var html = bd.innerHTML;
25828             if(Roo.isSafari){
25829                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25830                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25831                 if(m && m[1]){
25832                     html = '<div style="'+m[0]+'">' + html + '</div>';
25833                 }
25834             }
25835             html = this.cleanHtml(html);
25836             // fix up the special chars.. normaly like back quotes in word...
25837             // however we do not want to do this with chinese..
25838             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25839                 
25840                 var cc = match.charCodeAt();
25841
25842                 // Get the character value, handling surrogate pairs
25843                 if (match.length == 2) {
25844                     // It's a surrogate pair, calculate the Unicode code point
25845                     var high = match.charCodeAt(0) - 0xD800;
25846                     var low  = match.charCodeAt(1) - 0xDC00;
25847                     cc = (high * 0x400) + low + 0x10000;
25848                 }  else if (
25849                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25850                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25851                     (cc >= 0xf900 && cc < 0xfb00 )
25852                 ) {
25853                         return match;
25854                 }  
25855          
25856                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25857                 return "&#" + cc + ";";
25858                 
25859                 
25860             });
25861             
25862             
25863              
25864             if(this.owner.fireEvent('beforesync', this, html) !== false){
25865                 this.el.dom.value = html;
25866                 this.owner.fireEvent('sync', this, html);
25867             }
25868         }
25869     },
25870
25871     /**
25872      * Protected method that will not generally be called directly. Pushes the value of the textarea
25873      * into the iframe editor.
25874      */
25875     pushValue : function(){
25876         if(this.initialized){
25877             var v = this.el.dom.value.trim();
25878             
25879 //            if(v.length < 1){
25880 //                v = '&#160;';
25881 //            }
25882             
25883             if(this.owner.fireEvent('beforepush', this, v) !== false){
25884                 var d = (this.doc.body || this.doc.documentElement);
25885                 d.innerHTML = v;
25886                 this.cleanUpPaste();
25887                 this.el.dom.value = d.innerHTML;
25888                 this.owner.fireEvent('push', this, v);
25889             }
25890         }
25891     },
25892
25893     // private
25894     deferFocus : function(){
25895         this.focus.defer(10, this);
25896     },
25897
25898     // doc'ed in Field
25899     focus : function(){
25900         if(this.win && !this.sourceEditMode){
25901             this.win.focus();
25902         }else{
25903             this.el.focus();
25904         }
25905     },
25906     
25907     assignDocWin: function()
25908     {
25909         var iframe = this.iframe;
25910         
25911          if(Roo.isIE){
25912             this.doc = iframe.contentWindow.document;
25913             this.win = iframe.contentWindow;
25914         } else {
25915 //            if (!Roo.get(this.frameId)) {
25916 //                return;
25917 //            }
25918 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25919 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25920             
25921             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25922                 return;
25923             }
25924             
25925             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25926             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25927         }
25928     },
25929     
25930     // private
25931     initEditor : function(){
25932         //console.log("INIT EDITOR");
25933         this.assignDocWin();
25934         
25935         
25936         
25937         this.doc.designMode="on";
25938         this.doc.open();
25939         this.doc.write(this.getDocMarkup());
25940         this.doc.close();
25941         
25942         var dbody = (this.doc.body || this.doc.documentElement);
25943         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25944         // this copies styles from the containing element into thsi one..
25945         // not sure why we need all of this..
25946         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25947         
25948         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25949         //ss['background-attachment'] = 'fixed'; // w3c
25950         dbody.bgProperties = 'fixed'; // ie
25951         //Roo.DomHelper.applyStyles(dbody, ss);
25952         Roo.EventManager.on(this.doc, {
25953             //'mousedown': this.onEditorEvent,
25954             'mouseup': this.onEditorEvent,
25955             'dblclick': this.onEditorEvent,
25956             'click': this.onEditorEvent,
25957             'keyup': this.onEditorEvent,
25958             buffer:100,
25959             scope: this
25960         });
25961         if(Roo.isGecko){
25962             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25963         }
25964         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25965             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25966         }
25967         this.initialized = true;
25968
25969         this.owner.fireEvent('initialize', this);
25970         this.pushValue();
25971     },
25972
25973     // private
25974     onDestroy : function(){
25975         
25976         
25977         
25978         if(this.rendered){
25979             
25980             //for (var i =0; i < this.toolbars.length;i++) {
25981             //    // fixme - ask toolbars for heights?
25982             //    this.toolbars[i].onDestroy();
25983            // }
25984             
25985             //this.wrap.dom.innerHTML = '';
25986             //this.wrap.remove();
25987         }
25988     },
25989
25990     // private
25991     onFirstFocus : function(){
25992         
25993         this.assignDocWin();
25994         
25995         
25996         this.activated = true;
25997          
25998     
25999         if(Roo.isGecko){ // prevent silly gecko errors
26000             this.win.focus();
26001             var s = this.win.getSelection();
26002             if(!s.focusNode || s.focusNode.nodeType != 3){
26003                 var r = s.getRangeAt(0);
26004                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26005                 r.collapse(true);
26006                 this.deferFocus();
26007             }
26008             try{
26009                 this.execCmd('useCSS', true);
26010                 this.execCmd('styleWithCSS', false);
26011             }catch(e){}
26012         }
26013         this.owner.fireEvent('activate', this);
26014     },
26015
26016     // private
26017     adjustFont: function(btn){
26018         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26019         //if(Roo.isSafari){ // safari
26020         //    adjust *= 2;
26021        // }
26022         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26023         if(Roo.isSafari){ // safari
26024             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26025             v =  (v < 10) ? 10 : v;
26026             v =  (v > 48) ? 48 : v;
26027             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26028             
26029         }
26030         
26031         
26032         v = Math.max(1, v+adjust);
26033         
26034         this.execCmd('FontSize', v  );
26035     },
26036
26037     onEditorEvent : function(e)
26038     {
26039         this.owner.fireEvent('editorevent', this, e);
26040       //  this.updateToolbar();
26041         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26042     },
26043
26044     insertTag : function(tg)
26045     {
26046         // could be a bit smarter... -> wrap the current selected tRoo..
26047         if (tg.toLowerCase() == 'span' ||
26048             tg.toLowerCase() == 'code' ||
26049             tg.toLowerCase() == 'sup' ||
26050             tg.toLowerCase() == 'sub' 
26051             ) {
26052             
26053             range = this.createRange(this.getSelection());
26054             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26055             wrappingNode.appendChild(range.extractContents());
26056             range.insertNode(wrappingNode);
26057
26058             return;
26059             
26060             
26061             
26062         }
26063         this.execCmd("formatblock",   tg);
26064         
26065     },
26066     
26067     insertText : function(txt)
26068     {
26069         
26070         
26071         var range = this.createRange();
26072         range.deleteContents();
26073                //alert(Sender.getAttribute('label'));
26074                
26075         range.insertNode(this.doc.createTextNode(txt));
26076     } ,
26077     
26078      
26079
26080     /**
26081      * Executes a Midas editor command on the editor document and performs necessary focus and
26082      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26083      * @param {String} cmd The Midas command
26084      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26085      */
26086     relayCmd : function(cmd, value){
26087         this.win.focus();
26088         this.execCmd(cmd, value);
26089         this.owner.fireEvent('editorevent', this);
26090         //this.updateToolbar();
26091         this.owner.deferFocus();
26092     },
26093
26094     /**
26095      * Executes a Midas editor command directly on the editor document.
26096      * For visual commands, you should use {@link #relayCmd} instead.
26097      * <b>This should only be called after the editor is initialized.</b>
26098      * @param {String} cmd The Midas command
26099      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26100      */
26101     execCmd : function(cmd, value){
26102         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26103         this.syncValue();
26104     },
26105  
26106  
26107    
26108     /**
26109      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26110      * to insert tRoo.
26111      * @param {String} text | dom node.. 
26112      */
26113     insertAtCursor : function(text)
26114     {
26115         
26116         if(!this.activated){
26117             return;
26118         }
26119         /*
26120         if(Roo.isIE){
26121             this.win.focus();
26122             var r = this.doc.selection.createRange();
26123             if(r){
26124                 r.collapse(true);
26125                 r.pasteHTML(text);
26126                 this.syncValue();
26127                 this.deferFocus();
26128             
26129             }
26130             return;
26131         }
26132         */
26133         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26134             this.win.focus();
26135             
26136             
26137             // from jquery ui (MIT licenced)
26138             var range, node;
26139             var win = this.win;
26140             
26141             if (win.getSelection && win.getSelection().getRangeAt) {
26142                 range = win.getSelection().getRangeAt(0);
26143                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26144                 range.insertNode(node);
26145             } else if (win.document.selection && win.document.selection.createRange) {
26146                 // no firefox support
26147                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26148                 win.document.selection.createRange().pasteHTML(txt);
26149             } else {
26150                 // no firefox support
26151                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26152                 this.execCmd('InsertHTML', txt);
26153             } 
26154             
26155             this.syncValue();
26156             
26157             this.deferFocus();
26158         }
26159     },
26160  // private
26161     mozKeyPress : function(e){
26162         if(e.ctrlKey){
26163             var c = e.getCharCode(), cmd;
26164           
26165             if(c > 0){
26166                 c = String.fromCharCode(c).toLowerCase();
26167                 switch(c){
26168                     case 'b':
26169                         cmd = 'bold';
26170                         break;
26171                     case 'i':
26172                         cmd = 'italic';
26173                         break;
26174                     
26175                     case 'u':
26176                         cmd = 'underline';
26177                         break;
26178                     
26179                     case 'v':
26180                         this.cleanUpPaste.defer(100, this);
26181                         return;
26182                         
26183                 }
26184                 if(cmd){
26185                     this.win.focus();
26186                     this.execCmd(cmd);
26187                     this.deferFocus();
26188                     e.preventDefault();
26189                 }
26190                 
26191             }
26192         }
26193     },
26194
26195     // private
26196     fixKeys : function(){ // load time branching for fastest keydown performance
26197         if(Roo.isIE){
26198             return function(e){
26199                 var k = e.getKey(), r;
26200                 if(k == e.TAB){
26201                     e.stopEvent();
26202                     r = this.doc.selection.createRange();
26203                     if(r){
26204                         r.collapse(true);
26205                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26206                         this.deferFocus();
26207                     }
26208                     return;
26209                 }
26210                 
26211                 if(k == e.ENTER){
26212                     r = this.doc.selection.createRange();
26213                     if(r){
26214                         var target = r.parentElement();
26215                         if(!target || target.tagName.toLowerCase() != 'li'){
26216                             e.stopEvent();
26217                             r.pasteHTML('<br />');
26218                             r.collapse(false);
26219                             r.select();
26220                         }
26221                     }
26222                 }
26223                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26224                     this.cleanUpPaste.defer(100, this);
26225                     return;
26226                 }
26227                 
26228                 
26229             };
26230         }else if(Roo.isOpera){
26231             return function(e){
26232                 var k = e.getKey();
26233                 if(k == e.TAB){
26234                     e.stopEvent();
26235                     this.win.focus();
26236                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26237                     this.deferFocus();
26238                 }
26239                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26240                     this.cleanUpPaste.defer(100, this);
26241                     return;
26242                 }
26243                 
26244             };
26245         }else if(Roo.isSafari){
26246             return function(e){
26247                 var k = e.getKey();
26248                 
26249                 if(k == e.TAB){
26250                     e.stopEvent();
26251                     this.execCmd('InsertText','\t');
26252                     this.deferFocus();
26253                     return;
26254                 }
26255                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26256                     this.cleanUpPaste.defer(100, this);
26257                     return;
26258                 }
26259                 
26260              };
26261         }
26262     }(),
26263     
26264     getAllAncestors: function()
26265     {
26266         var p = this.getSelectedNode();
26267         var a = [];
26268         if (!p) {
26269             a.push(p); // push blank onto stack..
26270             p = this.getParentElement();
26271         }
26272         
26273         
26274         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26275             a.push(p);
26276             p = p.parentNode;
26277         }
26278         a.push(this.doc.body);
26279         return a;
26280     },
26281     lastSel : false,
26282     lastSelNode : false,
26283     
26284     
26285     getSelection : function() 
26286     {
26287         this.assignDocWin();
26288         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26289     },
26290     
26291     getSelectedNode: function() 
26292     {
26293         // this may only work on Gecko!!!
26294         
26295         // should we cache this!!!!
26296         
26297         
26298         
26299          
26300         var range = this.createRange(this.getSelection()).cloneRange();
26301         
26302         if (Roo.isIE) {
26303             var parent = range.parentElement();
26304             while (true) {
26305                 var testRange = range.duplicate();
26306                 testRange.moveToElementText(parent);
26307                 if (testRange.inRange(range)) {
26308                     break;
26309                 }
26310                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26311                     break;
26312                 }
26313                 parent = parent.parentElement;
26314             }
26315             return parent;
26316         }
26317         
26318         // is ancestor a text element.
26319         var ac =  range.commonAncestorContainer;
26320         if (ac.nodeType == 3) {
26321             ac = ac.parentNode;
26322         }
26323         
26324         var ar = ac.childNodes;
26325          
26326         var nodes = [];
26327         var other_nodes = [];
26328         var has_other_nodes = false;
26329         for (var i=0;i<ar.length;i++) {
26330             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26331                 continue;
26332             }
26333             // fullly contained node.
26334             
26335             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26336                 nodes.push(ar[i]);
26337                 continue;
26338             }
26339             
26340             // probably selected..
26341             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26342                 other_nodes.push(ar[i]);
26343                 continue;
26344             }
26345             // outer..
26346             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26347                 continue;
26348             }
26349             
26350             
26351             has_other_nodes = true;
26352         }
26353         if (!nodes.length && other_nodes.length) {
26354             nodes= other_nodes;
26355         }
26356         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26357             return false;
26358         }
26359         
26360         return nodes[0];
26361     },
26362     createRange: function(sel)
26363     {
26364         // this has strange effects when using with 
26365         // top toolbar - not sure if it's a great idea.
26366         //this.editor.contentWindow.focus();
26367         if (typeof sel != "undefined") {
26368             try {
26369                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26370             } catch(e) {
26371                 return this.doc.createRange();
26372             }
26373         } else {
26374             return this.doc.createRange();
26375         }
26376     },
26377     getParentElement: function()
26378     {
26379         
26380         this.assignDocWin();
26381         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26382         
26383         var range = this.createRange(sel);
26384          
26385         try {
26386             var p = range.commonAncestorContainer;
26387             while (p.nodeType == 3) { // text node
26388                 p = p.parentNode;
26389             }
26390             return p;
26391         } catch (e) {
26392             return null;
26393         }
26394     
26395     },
26396     /***
26397      *
26398      * Range intersection.. the hard stuff...
26399      *  '-1' = before
26400      *  '0' = hits..
26401      *  '1' = after.
26402      *         [ -- selected range --- ]
26403      *   [fail]                        [fail]
26404      *
26405      *    basically..
26406      *      if end is before start or  hits it. fail.
26407      *      if start is after end or hits it fail.
26408      *
26409      *   if either hits (but other is outside. - then it's not 
26410      *   
26411      *    
26412      **/
26413     
26414     
26415     // @see http://www.thismuchiknow.co.uk/?p=64.
26416     rangeIntersectsNode : function(range, node)
26417     {
26418         var nodeRange = node.ownerDocument.createRange();
26419         try {
26420             nodeRange.selectNode(node);
26421         } catch (e) {
26422             nodeRange.selectNodeContents(node);
26423         }
26424     
26425         var rangeStartRange = range.cloneRange();
26426         rangeStartRange.collapse(true);
26427     
26428         var rangeEndRange = range.cloneRange();
26429         rangeEndRange.collapse(false);
26430     
26431         var nodeStartRange = nodeRange.cloneRange();
26432         nodeStartRange.collapse(true);
26433     
26434         var nodeEndRange = nodeRange.cloneRange();
26435         nodeEndRange.collapse(false);
26436     
26437         return rangeStartRange.compareBoundaryPoints(
26438                  Range.START_TO_START, nodeEndRange) == -1 &&
26439                rangeEndRange.compareBoundaryPoints(
26440                  Range.START_TO_START, nodeStartRange) == 1;
26441         
26442          
26443     },
26444     rangeCompareNode : function(range, node)
26445     {
26446         var nodeRange = node.ownerDocument.createRange();
26447         try {
26448             nodeRange.selectNode(node);
26449         } catch (e) {
26450             nodeRange.selectNodeContents(node);
26451         }
26452         
26453         
26454         range.collapse(true);
26455     
26456         nodeRange.collapse(true);
26457      
26458         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26459         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26460          
26461         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26462         
26463         var nodeIsBefore   =  ss == 1;
26464         var nodeIsAfter    = ee == -1;
26465         
26466         if (nodeIsBefore && nodeIsAfter) {
26467             return 0; // outer
26468         }
26469         if (!nodeIsBefore && nodeIsAfter) {
26470             return 1; //right trailed.
26471         }
26472         
26473         if (nodeIsBefore && !nodeIsAfter) {
26474             return 2;  // left trailed.
26475         }
26476         // fully contined.
26477         return 3;
26478     },
26479
26480     // private? - in a new class?
26481     cleanUpPaste :  function()
26482     {
26483         // cleans up the whole document..
26484         Roo.log('cleanuppaste');
26485         
26486         this.cleanUpChildren(this.doc.body);
26487         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26488         if (clean != this.doc.body.innerHTML) {
26489             this.doc.body.innerHTML = clean;
26490         }
26491         
26492     },
26493     
26494     cleanWordChars : function(input) {// change the chars to hex code
26495         var he = Roo.HtmlEditorCore;
26496         
26497         var output = input;
26498         Roo.each(he.swapCodes, function(sw) { 
26499             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26500             
26501             output = output.replace(swapper, sw[1]);
26502         });
26503         
26504         return output;
26505     },
26506     
26507     
26508     cleanUpChildren : function (n)
26509     {
26510         if (!n.childNodes.length) {
26511             return;
26512         }
26513         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26514            this.cleanUpChild(n.childNodes[i]);
26515         }
26516     },
26517     
26518     
26519         
26520     
26521     cleanUpChild : function (node)
26522     {
26523         var ed = this;
26524         //console.log(node);
26525         if (node.nodeName == "#text") {
26526             // clean up silly Windows -- stuff?
26527             return; 
26528         }
26529         if (node.nodeName == "#comment") {
26530             if (!this.allowComments) {
26531                 node.parentNode.removeChild(node);
26532             }
26533             // clean up silly Windows -- stuff?
26534             return; 
26535         }
26536         var lcname = node.tagName.toLowerCase();
26537         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26538         // whitelist of tags..
26539         
26540         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26541             // remove node.
26542             node.parentNode.removeChild(node);
26543             return;
26544             
26545         }
26546         
26547         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26548         
26549         // spans with no attributes - just remove them..
26550         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26551             remove_keep_children = true;
26552         }
26553         
26554         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26555         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26556         
26557         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26558         //    remove_keep_children = true;
26559         //}
26560         
26561         if (remove_keep_children) {
26562             this.cleanUpChildren(node);
26563             // inserts everything just before this node...
26564             while (node.childNodes.length) {
26565                 var cn = node.childNodes[0];
26566                 node.removeChild(cn);
26567                 node.parentNode.insertBefore(cn, node);
26568             }
26569             node.parentNode.removeChild(node);
26570             return;
26571         }
26572         
26573         if (!node.attributes || !node.attributes.length) {
26574             
26575           
26576             
26577             
26578             this.cleanUpChildren(node);
26579             return;
26580         }
26581         
26582         function cleanAttr(n,v)
26583         {
26584             
26585             if (v.match(/^\./) || v.match(/^\//)) {
26586                 return;
26587             }
26588             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26589                 return;
26590             }
26591             if (v.match(/^#/)) {
26592                 return;
26593             }
26594             if (v.match(/^\{/)) { // allow template editing.
26595                 return;
26596             }
26597 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26598             node.removeAttribute(n);
26599             
26600         }
26601         
26602         var cwhite = this.cwhite;
26603         var cblack = this.cblack;
26604             
26605         function cleanStyle(n,v)
26606         {
26607             if (v.match(/expression/)) { //XSS?? should we even bother..
26608                 node.removeAttribute(n);
26609                 return;
26610             }
26611             
26612             var parts = v.split(/;/);
26613             var clean = [];
26614             
26615             Roo.each(parts, function(p) {
26616                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26617                 if (!p.length) {
26618                     return true;
26619                 }
26620                 var l = p.split(':').shift().replace(/\s+/g,'');
26621                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26622                 
26623                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26624 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26625                     //node.removeAttribute(n);
26626                     return true;
26627                 }
26628                 //Roo.log()
26629                 // only allow 'c whitelisted system attributes'
26630                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26631 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26632                     //node.removeAttribute(n);
26633                     return true;
26634                 }
26635                 
26636                 
26637                  
26638                 
26639                 clean.push(p);
26640                 return true;
26641             });
26642             if (clean.length) { 
26643                 node.setAttribute(n, clean.join(';'));
26644             } else {
26645                 node.removeAttribute(n);
26646             }
26647             
26648         }
26649         
26650         
26651         for (var i = node.attributes.length-1; i > -1 ; i--) {
26652             var a = node.attributes[i];
26653             //console.log(a);
26654             
26655             if (a.name.toLowerCase().substr(0,2)=='on')  {
26656                 node.removeAttribute(a.name);
26657                 continue;
26658             }
26659             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26660                 node.removeAttribute(a.name);
26661                 continue;
26662             }
26663             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26664                 cleanAttr(a.name,a.value); // fixme..
26665                 continue;
26666             }
26667             if (a.name == 'style') {
26668                 cleanStyle(a.name,a.value);
26669                 continue;
26670             }
26671             /// clean up MS crap..
26672             // tecnically this should be a list of valid class'es..
26673             
26674             
26675             if (a.name == 'class') {
26676                 if (a.value.match(/^Mso/)) {
26677                     node.removeAttribute('class');
26678                 }
26679                 
26680                 if (a.value.match(/^body$/)) {
26681                     node.removeAttribute('class');
26682                 }
26683                 continue;
26684             }
26685             
26686             // style cleanup!?
26687             // class cleanup?
26688             
26689         }
26690         
26691         
26692         this.cleanUpChildren(node);
26693         
26694         
26695     },
26696     
26697     /**
26698      * Clean up MS wordisms...
26699      */
26700     cleanWord : function(node)
26701     {
26702         if (!node) {
26703             this.cleanWord(this.doc.body);
26704             return;
26705         }
26706         
26707         if(
26708                 node.nodeName == 'SPAN' &&
26709                 !node.hasAttributes() &&
26710                 node.childNodes.length == 1 &&
26711                 node.firstChild.nodeName == "#text"  
26712         ) {
26713             var textNode = node.firstChild;
26714             node.removeChild(textNode);
26715             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26716                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26717             }
26718             node.parentNode.insertBefore(textNode, node);
26719             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26720                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26721             }
26722             node.parentNode.removeChild(node);
26723         }
26724         
26725         if (node.nodeName == "#text") {
26726             // clean up silly Windows -- stuff?
26727             return; 
26728         }
26729         if (node.nodeName == "#comment") {
26730             node.parentNode.removeChild(node);
26731             // clean up silly Windows -- stuff?
26732             return; 
26733         }
26734         
26735         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26736             node.parentNode.removeChild(node);
26737             return;
26738         }
26739         //Roo.log(node.tagName);
26740         // remove - but keep children..
26741         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26742             //Roo.log('-- removed');
26743             while (node.childNodes.length) {
26744                 var cn = node.childNodes[0];
26745                 node.removeChild(cn);
26746                 node.parentNode.insertBefore(cn, node);
26747                 // move node to parent - and clean it..
26748                 this.cleanWord(cn);
26749             }
26750             node.parentNode.removeChild(node);
26751             /// no need to iterate chidlren = it's got none..
26752             //this.iterateChildren(node, this.cleanWord);
26753             return;
26754         }
26755         // clean styles
26756         if (node.className.length) {
26757             
26758             var cn = node.className.split(/\W+/);
26759             var cna = [];
26760             Roo.each(cn, function(cls) {
26761                 if (cls.match(/Mso[a-zA-Z]+/)) {
26762                     return;
26763                 }
26764                 cna.push(cls);
26765             });
26766             node.className = cna.length ? cna.join(' ') : '';
26767             if (!cna.length) {
26768                 node.removeAttribute("class");
26769             }
26770         }
26771         
26772         if (node.hasAttribute("lang")) {
26773             node.removeAttribute("lang");
26774         }
26775         
26776         if (node.hasAttribute("style")) {
26777             
26778             var styles = node.getAttribute("style").split(";");
26779             var nstyle = [];
26780             Roo.each(styles, function(s) {
26781                 if (!s.match(/:/)) {
26782                     return;
26783                 }
26784                 var kv = s.split(":");
26785                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26786                     return;
26787                 }
26788                 // what ever is left... we allow.
26789                 nstyle.push(s);
26790             });
26791             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26792             if (!nstyle.length) {
26793                 node.removeAttribute('style');
26794             }
26795         }
26796         this.iterateChildren(node, this.cleanWord);
26797         
26798         
26799         
26800     },
26801     /**
26802      * iterateChildren of a Node, calling fn each time, using this as the scole..
26803      * @param {DomNode} node node to iterate children of.
26804      * @param {Function} fn method of this class to call on each item.
26805      */
26806     iterateChildren : function(node, fn)
26807     {
26808         if (!node.childNodes.length) {
26809                 return;
26810         }
26811         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26812            fn.call(this, node.childNodes[i])
26813         }
26814     },
26815     
26816     
26817     /**
26818      * cleanTableWidths.
26819      *
26820      * Quite often pasting from word etc.. results in tables with column and widths.
26821      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26822      *
26823      */
26824     cleanTableWidths : function(node)
26825     {
26826          
26827          
26828         if (!node) {
26829             this.cleanTableWidths(this.doc.body);
26830             return;
26831         }
26832         
26833         // ignore list...
26834         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26835             return; 
26836         }
26837         Roo.log(node.tagName);
26838         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26839             this.iterateChildren(node, this.cleanTableWidths);
26840             return;
26841         }
26842         if (node.hasAttribute('width')) {
26843             node.removeAttribute('width');
26844         }
26845         
26846          
26847         if (node.hasAttribute("style")) {
26848             // pretty basic...
26849             
26850             var styles = node.getAttribute("style").split(";");
26851             var nstyle = [];
26852             Roo.each(styles, function(s) {
26853                 if (!s.match(/:/)) {
26854                     return;
26855                 }
26856                 var kv = s.split(":");
26857                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26858                     return;
26859                 }
26860                 // what ever is left... we allow.
26861                 nstyle.push(s);
26862             });
26863             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26864             if (!nstyle.length) {
26865                 node.removeAttribute('style');
26866             }
26867         }
26868         
26869         this.iterateChildren(node, this.cleanTableWidths);
26870         
26871         
26872     },
26873     
26874     
26875     
26876     
26877     domToHTML : function(currentElement, depth, nopadtext) {
26878         
26879         depth = depth || 0;
26880         nopadtext = nopadtext || false;
26881     
26882         if (!currentElement) {
26883             return this.domToHTML(this.doc.body);
26884         }
26885         
26886         //Roo.log(currentElement);
26887         var j;
26888         var allText = false;
26889         var nodeName = currentElement.nodeName;
26890         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26891         
26892         if  (nodeName == '#text') {
26893             
26894             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26895         }
26896         
26897         
26898         var ret = '';
26899         if (nodeName != 'BODY') {
26900              
26901             var i = 0;
26902             // Prints the node tagName, such as <A>, <IMG>, etc
26903             if (tagName) {
26904                 var attr = [];
26905                 for(i = 0; i < currentElement.attributes.length;i++) {
26906                     // quoting?
26907                     var aname = currentElement.attributes.item(i).name;
26908                     if (!currentElement.attributes.item(i).value.length) {
26909                         continue;
26910                     }
26911                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26912                 }
26913                 
26914                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26915             } 
26916             else {
26917                 
26918                 // eack
26919             }
26920         } else {
26921             tagName = false;
26922         }
26923         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26924             return ret;
26925         }
26926         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26927             nopadtext = true;
26928         }
26929         
26930         
26931         // Traverse the tree
26932         i = 0;
26933         var currentElementChild = currentElement.childNodes.item(i);
26934         var allText = true;
26935         var innerHTML  = '';
26936         lastnode = '';
26937         while (currentElementChild) {
26938             // Formatting code (indent the tree so it looks nice on the screen)
26939             var nopad = nopadtext;
26940             if (lastnode == 'SPAN') {
26941                 nopad  = true;
26942             }
26943             // text
26944             if  (currentElementChild.nodeName == '#text') {
26945                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26946                 toadd = nopadtext ? toadd : toadd.trim();
26947                 if (!nopad && toadd.length > 80) {
26948                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26949                 }
26950                 innerHTML  += toadd;
26951                 
26952                 i++;
26953                 currentElementChild = currentElement.childNodes.item(i);
26954                 lastNode = '';
26955                 continue;
26956             }
26957             allText = false;
26958             
26959             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26960                 
26961             // Recursively traverse the tree structure of the child node
26962             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26963             lastnode = currentElementChild.nodeName;
26964             i++;
26965             currentElementChild=currentElement.childNodes.item(i);
26966         }
26967         
26968         ret += innerHTML;
26969         
26970         if (!allText) {
26971                 // The remaining code is mostly for formatting the tree
26972             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26973         }
26974         
26975         
26976         if (tagName) {
26977             ret+= "</"+tagName+">";
26978         }
26979         return ret;
26980         
26981     },
26982         
26983     applyBlacklists : function()
26984     {
26985         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26986         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26987         
26988         this.white = [];
26989         this.black = [];
26990         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26991             if (b.indexOf(tag) > -1) {
26992                 return;
26993             }
26994             this.white.push(tag);
26995             
26996         }, this);
26997         
26998         Roo.each(w, function(tag) {
26999             if (b.indexOf(tag) > -1) {
27000                 return;
27001             }
27002             if (this.white.indexOf(tag) > -1) {
27003                 return;
27004             }
27005             this.white.push(tag);
27006             
27007         }, this);
27008         
27009         
27010         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27011             if (w.indexOf(tag) > -1) {
27012                 return;
27013             }
27014             this.black.push(tag);
27015             
27016         }, this);
27017         
27018         Roo.each(b, function(tag) {
27019             if (w.indexOf(tag) > -1) {
27020                 return;
27021             }
27022             if (this.black.indexOf(tag) > -1) {
27023                 return;
27024             }
27025             this.black.push(tag);
27026             
27027         }, this);
27028         
27029         
27030         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27031         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27032         
27033         this.cwhite = [];
27034         this.cblack = [];
27035         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27036             if (b.indexOf(tag) > -1) {
27037                 return;
27038             }
27039             this.cwhite.push(tag);
27040             
27041         }, this);
27042         
27043         Roo.each(w, function(tag) {
27044             if (b.indexOf(tag) > -1) {
27045                 return;
27046             }
27047             if (this.cwhite.indexOf(tag) > -1) {
27048                 return;
27049             }
27050             this.cwhite.push(tag);
27051             
27052         }, this);
27053         
27054         
27055         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27056             if (w.indexOf(tag) > -1) {
27057                 return;
27058             }
27059             this.cblack.push(tag);
27060             
27061         }, this);
27062         
27063         Roo.each(b, function(tag) {
27064             if (w.indexOf(tag) > -1) {
27065                 return;
27066             }
27067             if (this.cblack.indexOf(tag) > -1) {
27068                 return;
27069             }
27070             this.cblack.push(tag);
27071             
27072         }, this);
27073     },
27074     
27075     setStylesheets : function(stylesheets)
27076     {
27077         if(typeof(stylesheets) == 'string'){
27078             Roo.get(this.iframe.contentDocument.head).createChild({
27079                 tag : 'link',
27080                 rel : 'stylesheet',
27081                 type : 'text/css',
27082                 href : stylesheets
27083             });
27084             
27085             return;
27086         }
27087         var _this = this;
27088      
27089         Roo.each(stylesheets, function(s) {
27090             if(!s.length){
27091                 return;
27092             }
27093             
27094             Roo.get(_this.iframe.contentDocument.head).createChild({
27095                 tag : 'link',
27096                 rel : 'stylesheet',
27097                 type : 'text/css',
27098                 href : s
27099             });
27100         });
27101
27102         
27103     },
27104     
27105     removeStylesheets : function()
27106     {
27107         var _this = this;
27108         
27109         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27110             s.remove();
27111         });
27112     },
27113     
27114     setStyle : function(style)
27115     {
27116         Roo.get(this.iframe.contentDocument.head).createChild({
27117             tag : 'style',
27118             type : 'text/css',
27119             html : style
27120         });
27121
27122         return;
27123     }
27124     
27125     // hide stuff that is not compatible
27126     /**
27127      * @event blur
27128      * @hide
27129      */
27130     /**
27131      * @event change
27132      * @hide
27133      */
27134     /**
27135      * @event focus
27136      * @hide
27137      */
27138     /**
27139      * @event specialkey
27140      * @hide
27141      */
27142     /**
27143      * @cfg {String} fieldClass @hide
27144      */
27145     /**
27146      * @cfg {String} focusClass @hide
27147      */
27148     /**
27149      * @cfg {String} autoCreate @hide
27150      */
27151     /**
27152      * @cfg {String} inputType @hide
27153      */
27154     /**
27155      * @cfg {String} invalidClass @hide
27156      */
27157     /**
27158      * @cfg {String} invalidText @hide
27159      */
27160     /**
27161      * @cfg {String} msgFx @hide
27162      */
27163     /**
27164      * @cfg {String} validateOnBlur @hide
27165      */
27166 });
27167
27168 Roo.HtmlEditorCore.white = [
27169         'area', 'br', 'img', 'input', 'hr', 'wbr',
27170         
27171        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27172        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27173        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27174        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27175        'table',   'ul',         'xmp', 
27176        
27177        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27178       'thead',   'tr', 
27179      
27180       'dir', 'menu', 'ol', 'ul', 'dl',
27181        
27182       'embed',  'object'
27183 ];
27184
27185
27186 Roo.HtmlEditorCore.black = [
27187     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27188         'applet', // 
27189         'base',   'basefont', 'bgsound', 'blink',  'body', 
27190         'frame',  'frameset', 'head',    'html',   'ilayer', 
27191         'iframe', 'layer',  'link',     'meta',    'object',   
27192         'script', 'style' ,'title',  'xml' // clean later..
27193 ];
27194 Roo.HtmlEditorCore.clean = [
27195     'script', 'style', 'title', 'xml'
27196 ];
27197 Roo.HtmlEditorCore.remove = [
27198     'font'
27199 ];
27200 // attributes..
27201
27202 Roo.HtmlEditorCore.ablack = [
27203     'on'
27204 ];
27205     
27206 Roo.HtmlEditorCore.aclean = [ 
27207     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27208 ];
27209
27210 // protocols..
27211 Roo.HtmlEditorCore.pwhite= [
27212         'http',  'https',  'mailto'
27213 ];
27214
27215 // white listed style attributes.
27216 Roo.HtmlEditorCore.cwhite= [
27217       //  'text-align', /// default is to allow most things..
27218       
27219          
27220 //        'font-size'//??
27221 ];
27222
27223 // black listed style attributes.
27224 Roo.HtmlEditorCore.cblack= [
27225       //  'font-size' -- this can be set by the project 
27226 ];
27227
27228
27229 Roo.HtmlEditorCore.swapCodes   =[ 
27230     [    8211, "&#8211;" ], 
27231     [    8212, "&#8212;" ], 
27232     [    8216,  "'" ],  
27233     [    8217, "'" ],  
27234     [    8220, '"' ],  
27235     [    8221, '"' ],  
27236     [    8226, "*" ],  
27237     [    8230, "..." ]
27238 ]; 
27239
27240     /*
27241  * - LGPL
27242  *
27243  * HtmlEditor
27244  * 
27245  */
27246
27247 /**
27248  * @class Roo.bootstrap.HtmlEditor
27249  * @extends Roo.bootstrap.TextArea
27250  * Bootstrap HtmlEditor class
27251
27252  * @constructor
27253  * Create a new HtmlEditor
27254  * @param {Object} config The config object
27255  */
27256
27257 Roo.bootstrap.HtmlEditor = function(config){
27258     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27259     if (!this.toolbars) {
27260         this.toolbars = [];
27261     }
27262     
27263     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27264     this.addEvents({
27265             /**
27266              * @event initialize
27267              * Fires when the editor is fully initialized (including the iframe)
27268              * @param {HtmlEditor} this
27269              */
27270             initialize: true,
27271             /**
27272              * @event activate
27273              * Fires when the editor is first receives the focus. Any insertion must wait
27274              * until after this event.
27275              * @param {HtmlEditor} this
27276              */
27277             activate: true,
27278              /**
27279              * @event beforesync
27280              * Fires before the textarea is updated with content from the editor iframe. Return false
27281              * to cancel the sync.
27282              * @param {HtmlEditor} this
27283              * @param {String} html
27284              */
27285             beforesync: true,
27286              /**
27287              * @event beforepush
27288              * Fires before the iframe editor is updated with content from the textarea. Return false
27289              * to cancel the push.
27290              * @param {HtmlEditor} this
27291              * @param {String} html
27292              */
27293             beforepush: true,
27294              /**
27295              * @event sync
27296              * Fires when the textarea is updated with content from the editor iframe.
27297              * @param {HtmlEditor} this
27298              * @param {String} html
27299              */
27300             sync: true,
27301              /**
27302              * @event push
27303              * Fires when the iframe editor is updated with content from the textarea.
27304              * @param {HtmlEditor} this
27305              * @param {String} html
27306              */
27307             push: true,
27308              /**
27309              * @event editmodechange
27310              * Fires when the editor switches edit modes
27311              * @param {HtmlEditor} this
27312              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27313              */
27314             editmodechange: true,
27315             /**
27316              * @event editorevent
27317              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27318              * @param {HtmlEditor} this
27319              */
27320             editorevent: true,
27321             /**
27322              * @event firstfocus
27323              * Fires when on first focus - needed by toolbars..
27324              * @param {HtmlEditor} this
27325              */
27326             firstfocus: true,
27327             /**
27328              * @event autosave
27329              * Auto save the htmlEditor value as a file into Events
27330              * @param {HtmlEditor} this
27331              */
27332             autosave: true,
27333             /**
27334              * @event savedpreview
27335              * preview the saved version of htmlEditor
27336              * @param {HtmlEditor} this
27337              */
27338             savedpreview: true
27339         });
27340 };
27341
27342
27343 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27344     
27345     
27346       /**
27347      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27348      */
27349     toolbars : false,
27350     
27351      /**
27352     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27353     */
27354     btns : [],
27355    
27356      /**
27357      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27358      *                        Roo.resizable.
27359      */
27360     resizable : false,
27361      /**
27362      * @cfg {Number} height (in pixels)
27363      */   
27364     height: 300,
27365    /**
27366      * @cfg {Number} width (in pixels)
27367      */   
27368     width: false,
27369     
27370     /**
27371      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27372      * 
27373      */
27374     stylesheets: false,
27375     
27376     // id of frame..
27377     frameId: false,
27378     
27379     // private properties
27380     validationEvent : false,
27381     deferHeight: true,
27382     initialized : false,
27383     activated : false,
27384     
27385     onFocus : Roo.emptyFn,
27386     iframePad:3,
27387     hideMode:'offsets',
27388     
27389     tbContainer : false,
27390     
27391     bodyCls : '',
27392     
27393     toolbarContainer :function() {
27394         return this.wrap.select('.x-html-editor-tb',true).first();
27395     },
27396
27397     /**
27398      * Protected method that will not generally be called directly. It
27399      * is called when the editor creates its toolbar. Override this method if you need to
27400      * add custom toolbar buttons.
27401      * @param {HtmlEditor} editor
27402      */
27403     createToolbar : function(){
27404         Roo.log('renewing');
27405         Roo.log("create toolbars");
27406         
27407         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27408         this.toolbars[0].render(this.toolbarContainer());
27409         
27410         return;
27411         
27412 //        if (!editor.toolbars || !editor.toolbars.length) {
27413 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27414 //        }
27415 //        
27416 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27417 //            editor.toolbars[i] = Roo.factory(
27418 //                    typeof(editor.toolbars[i]) == 'string' ?
27419 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27420 //                Roo.bootstrap.HtmlEditor);
27421 //            editor.toolbars[i].init(editor);
27422 //        }
27423     },
27424
27425      
27426     // private
27427     onRender : function(ct, position)
27428     {
27429        // Roo.log("Call onRender: " + this.xtype);
27430         var _t = this;
27431         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27432       
27433         this.wrap = this.inputEl().wrap({
27434             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27435         });
27436         
27437         this.editorcore.onRender(ct, position);
27438          
27439         if (this.resizable) {
27440             this.resizeEl = new Roo.Resizable(this.wrap, {
27441                 pinned : true,
27442                 wrap: true,
27443                 dynamic : true,
27444                 minHeight : this.height,
27445                 height: this.height,
27446                 handles : this.resizable,
27447                 width: this.width,
27448                 listeners : {
27449                     resize : function(r, w, h) {
27450                         _t.onResize(w,h); // -something
27451                     }
27452                 }
27453             });
27454             
27455         }
27456         this.createToolbar(this);
27457        
27458         
27459         if(!this.width && this.resizable){
27460             this.setSize(this.wrap.getSize());
27461         }
27462         if (this.resizeEl) {
27463             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27464             // should trigger onReize..
27465         }
27466         
27467     },
27468
27469     // private
27470     onResize : function(w, h)
27471     {
27472         Roo.log('resize: ' +w + ',' + h );
27473         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27474         var ew = false;
27475         var eh = false;
27476         
27477         if(this.inputEl() ){
27478             if(typeof w == 'number'){
27479                 var aw = w - this.wrap.getFrameWidth('lr');
27480                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27481                 ew = aw;
27482             }
27483             if(typeof h == 'number'){
27484                  var tbh = -11;  // fixme it needs to tool bar size!
27485                 for (var i =0; i < this.toolbars.length;i++) {
27486                     // fixme - ask toolbars for heights?
27487                     tbh += this.toolbars[i].el.getHeight();
27488                     //if (this.toolbars[i].footer) {
27489                     //    tbh += this.toolbars[i].footer.el.getHeight();
27490                     //}
27491                 }
27492               
27493                 
27494                 
27495                 
27496                 
27497                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27498                 ah -= 5; // knock a few pixes off for look..
27499                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27500                 var eh = ah;
27501             }
27502         }
27503         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27504         this.editorcore.onResize(ew,eh);
27505         
27506     },
27507
27508     /**
27509      * Toggles the editor between standard and source edit mode.
27510      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27511      */
27512     toggleSourceEdit : function(sourceEditMode)
27513     {
27514         this.editorcore.toggleSourceEdit(sourceEditMode);
27515         
27516         if(this.editorcore.sourceEditMode){
27517             Roo.log('editor - showing textarea');
27518             
27519 //            Roo.log('in');
27520 //            Roo.log(this.syncValue());
27521             this.syncValue();
27522             this.inputEl().removeClass(['hide', 'x-hidden']);
27523             this.inputEl().dom.removeAttribute('tabIndex');
27524             this.inputEl().focus();
27525         }else{
27526             Roo.log('editor - hiding textarea');
27527 //            Roo.log('out')
27528 //            Roo.log(this.pushValue()); 
27529             this.pushValue();
27530             
27531             this.inputEl().addClass(['hide', 'x-hidden']);
27532             this.inputEl().dom.setAttribute('tabIndex', -1);
27533             //this.deferFocus();
27534         }
27535          
27536         if(this.resizable){
27537             this.setSize(this.wrap.getSize());
27538         }
27539         
27540         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27541     },
27542  
27543     // private (for BoxComponent)
27544     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27545
27546     // private (for BoxComponent)
27547     getResizeEl : function(){
27548         return this.wrap;
27549     },
27550
27551     // private (for BoxComponent)
27552     getPositionEl : function(){
27553         return this.wrap;
27554     },
27555
27556     // private
27557     initEvents : function(){
27558         this.originalValue = this.getValue();
27559     },
27560
27561 //    /**
27562 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27563 //     * @method
27564 //     */
27565 //    markInvalid : Roo.emptyFn,
27566 //    /**
27567 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27568 //     * @method
27569 //     */
27570 //    clearInvalid : Roo.emptyFn,
27571
27572     setValue : function(v){
27573         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27574         this.editorcore.pushValue();
27575     },
27576
27577      
27578     // private
27579     deferFocus : function(){
27580         this.focus.defer(10, this);
27581     },
27582
27583     // doc'ed in Field
27584     focus : function(){
27585         this.editorcore.focus();
27586         
27587     },
27588       
27589
27590     // private
27591     onDestroy : function(){
27592         
27593         
27594         
27595         if(this.rendered){
27596             
27597             for (var i =0; i < this.toolbars.length;i++) {
27598                 // fixme - ask toolbars for heights?
27599                 this.toolbars[i].onDestroy();
27600             }
27601             
27602             this.wrap.dom.innerHTML = '';
27603             this.wrap.remove();
27604         }
27605     },
27606
27607     // private
27608     onFirstFocus : function(){
27609         //Roo.log("onFirstFocus");
27610         this.editorcore.onFirstFocus();
27611          for (var i =0; i < this.toolbars.length;i++) {
27612             this.toolbars[i].onFirstFocus();
27613         }
27614         
27615     },
27616     
27617     // private
27618     syncValue : function()
27619     {   
27620         this.editorcore.syncValue();
27621     },
27622     
27623     pushValue : function()
27624     {   
27625         this.editorcore.pushValue();
27626     }
27627      
27628     
27629     // hide stuff that is not compatible
27630     /**
27631      * @event blur
27632      * @hide
27633      */
27634     /**
27635      * @event change
27636      * @hide
27637      */
27638     /**
27639      * @event focus
27640      * @hide
27641      */
27642     /**
27643      * @event specialkey
27644      * @hide
27645      */
27646     /**
27647      * @cfg {String} fieldClass @hide
27648      */
27649     /**
27650      * @cfg {String} focusClass @hide
27651      */
27652     /**
27653      * @cfg {String} autoCreate @hide
27654      */
27655     /**
27656      * @cfg {String} inputType @hide
27657      */
27658      
27659     /**
27660      * @cfg {String} invalidText @hide
27661      */
27662     /**
27663      * @cfg {String} msgFx @hide
27664      */
27665     /**
27666      * @cfg {String} validateOnBlur @hide
27667      */
27668 });
27669  
27670     
27671    
27672    
27673    
27674       
27675 Roo.namespace('Roo.bootstrap.htmleditor');
27676 /**
27677  * @class Roo.bootstrap.HtmlEditorToolbar1
27678  * Basic Toolbar
27679  * 
27680  * @example
27681  * Usage:
27682  *
27683  new Roo.bootstrap.HtmlEditor({
27684     ....
27685     toolbars : [
27686         new Roo.bootstrap.HtmlEditorToolbar1({
27687             disable : { fonts: 1 , format: 1, ..., ... , ...],
27688             btns : [ .... ]
27689         })
27690     }
27691      
27692  * 
27693  * @cfg {Object} disable List of elements to disable..
27694  * @cfg {Array} btns List of additional buttons.
27695  * 
27696  * 
27697  * NEEDS Extra CSS? 
27698  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27699  */
27700  
27701 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27702 {
27703     
27704     Roo.apply(this, config);
27705     
27706     // default disabled, based on 'good practice'..
27707     this.disable = this.disable || {};
27708     Roo.applyIf(this.disable, {
27709         fontSize : true,
27710         colors : true,
27711         specialElements : true
27712     });
27713     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27714     
27715     this.editor = config.editor;
27716     this.editorcore = config.editor.editorcore;
27717     
27718     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27719     
27720     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27721     // dont call parent... till later.
27722 }
27723 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27724      
27725     bar : true,
27726     
27727     editor : false,
27728     editorcore : false,
27729     
27730     
27731     formats : [
27732         "p" ,  
27733         "h1","h2","h3","h4","h5","h6", 
27734         "pre", "code", 
27735         "abbr", "acronym", "address", "cite", "samp", "var",
27736         'div','span'
27737     ],
27738     
27739     onRender : function(ct, position)
27740     {
27741        // Roo.log("Call onRender: " + this.xtype);
27742         
27743        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27744        Roo.log(this.el);
27745        this.el.dom.style.marginBottom = '0';
27746        var _this = this;
27747        var editorcore = this.editorcore;
27748        var editor= this.editor;
27749        
27750        var children = [];
27751        var btn = function(id,cmd , toggle, handler, html){
27752        
27753             var  event = toggle ? 'toggle' : 'click';
27754        
27755             var a = {
27756                 size : 'sm',
27757                 xtype: 'Button',
27758                 xns: Roo.bootstrap,
27759                 //glyphicon : id,
27760                 fa: id,
27761                 cmd : id || cmd,
27762                 enableToggle:toggle !== false,
27763                 html : html || '',
27764                 pressed : toggle ? false : null,
27765                 listeners : {}
27766             };
27767             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27768                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27769             };
27770             children.push(a);
27771             return a;
27772        }
27773        
27774     //    var cb_box = function...
27775         
27776         var style = {
27777                 xtype: 'Button',
27778                 size : 'sm',
27779                 xns: Roo.bootstrap,
27780                 fa : 'font',
27781                 //html : 'submit'
27782                 menu : {
27783                     xtype: 'Menu',
27784                     xns: Roo.bootstrap,
27785                     items:  []
27786                 }
27787         };
27788         Roo.each(this.formats, function(f) {
27789             style.menu.items.push({
27790                 xtype :'MenuItem',
27791                 xns: Roo.bootstrap,
27792                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27793                 tagname : f,
27794                 listeners : {
27795                     click : function()
27796                     {
27797                         editorcore.insertTag(this.tagname);
27798                         editor.focus();
27799                     }
27800                 }
27801                 
27802             });
27803         });
27804         children.push(style);   
27805         
27806         btn('bold',false,true);
27807         btn('italic',false,true);
27808         btn('align-left', 'justifyleft',true);
27809         btn('align-center', 'justifycenter',true);
27810         btn('align-right' , 'justifyright',true);
27811         btn('link', false, false, function(btn) {
27812             //Roo.log("create link?");
27813             var url = prompt(this.createLinkText, this.defaultLinkValue);
27814             if(url && url != 'http:/'+'/'){
27815                 this.editorcore.relayCmd('createlink', url);
27816             }
27817         }),
27818         btn('list','insertunorderedlist',true);
27819         btn('pencil', false,true, function(btn){
27820                 Roo.log(this);
27821                 this.toggleSourceEdit(btn.pressed);
27822         });
27823         
27824         if (this.editor.btns.length > 0) {
27825             for (var i = 0; i<this.editor.btns.length; i++) {
27826                 children.push(this.editor.btns[i]);
27827             }
27828         }
27829         
27830         /*
27831         var cog = {
27832                 xtype: 'Button',
27833                 size : 'sm',
27834                 xns: Roo.bootstrap,
27835                 glyphicon : 'cog',
27836                 //html : 'submit'
27837                 menu : {
27838                     xtype: 'Menu',
27839                     xns: Roo.bootstrap,
27840                     items:  []
27841                 }
27842         };
27843         
27844         cog.menu.items.push({
27845             xtype :'MenuItem',
27846             xns: Roo.bootstrap,
27847             html : Clean styles,
27848             tagname : f,
27849             listeners : {
27850                 click : function()
27851                 {
27852                     editorcore.insertTag(this.tagname);
27853                     editor.focus();
27854                 }
27855             }
27856             
27857         });
27858        */
27859         
27860          
27861        this.xtype = 'NavSimplebar';
27862         
27863         for(var i=0;i< children.length;i++) {
27864             
27865             this.buttons.add(this.addxtypeChild(children[i]));
27866             
27867         }
27868         
27869         editor.on('editorevent', this.updateToolbar, this);
27870     },
27871     onBtnClick : function(id)
27872     {
27873        this.editorcore.relayCmd(id);
27874        this.editorcore.focus();
27875     },
27876     
27877     /**
27878      * Protected method that will not generally be called directly. It triggers
27879      * a toolbar update by reading the markup state of the current selection in the editor.
27880      */
27881     updateToolbar: function(){
27882
27883         if(!this.editorcore.activated){
27884             this.editor.onFirstFocus(); // is this neeed?
27885             return;
27886         }
27887
27888         var btns = this.buttons; 
27889         var doc = this.editorcore.doc;
27890         btns.get('bold').setActive(doc.queryCommandState('bold'));
27891         btns.get('italic').setActive(doc.queryCommandState('italic'));
27892         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27893         
27894         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27895         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27896         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27897         
27898         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27899         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27900          /*
27901         
27902         var ans = this.editorcore.getAllAncestors();
27903         if (this.formatCombo) {
27904             
27905             
27906             var store = this.formatCombo.store;
27907             this.formatCombo.setValue("");
27908             for (var i =0; i < ans.length;i++) {
27909                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27910                     // select it..
27911                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27912                     break;
27913                 }
27914             }
27915         }
27916         
27917         
27918         
27919         // hides menus... - so this cant be on a menu...
27920         Roo.bootstrap.MenuMgr.hideAll();
27921         */
27922         Roo.bootstrap.MenuMgr.hideAll();
27923         //this.editorsyncValue();
27924     },
27925     onFirstFocus: function() {
27926         this.buttons.each(function(item){
27927            item.enable();
27928         });
27929     },
27930     toggleSourceEdit : function(sourceEditMode){
27931         
27932           
27933         if(sourceEditMode){
27934             Roo.log("disabling buttons");
27935            this.buttons.each( function(item){
27936                 if(item.cmd != 'pencil'){
27937                     item.disable();
27938                 }
27939             });
27940           
27941         }else{
27942             Roo.log("enabling buttons");
27943             if(this.editorcore.initialized){
27944                 this.buttons.each( function(item){
27945                     item.enable();
27946                 });
27947             }
27948             
27949         }
27950         Roo.log("calling toggole on editor");
27951         // tell the editor that it's been pressed..
27952         this.editor.toggleSourceEdit(sourceEditMode);
27953        
27954     }
27955 });
27956
27957
27958
27959
27960  
27961 /*
27962  * - LGPL
27963  */
27964
27965 /**
27966  * @class Roo.bootstrap.Markdown
27967  * @extends Roo.bootstrap.TextArea
27968  * Bootstrap Showdown editable area
27969  * @cfg {string} content
27970  * 
27971  * @constructor
27972  * Create a new Showdown
27973  */
27974
27975 Roo.bootstrap.Markdown = function(config){
27976     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27977    
27978 };
27979
27980 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27981     
27982     editing :false,
27983     
27984     initEvents : function()
27985     {
27986         
27987         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27988         this.markdownEl = this.el.createChild({
27989             cls : 'roo-markdown-area'
27990         });
27991         this.inputEl().addClass('d-none');
27992         if (this.getValue() == '') {
27993             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27994             
27995         } else {
27996             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27997         }
27998         this.markdownEl.on('click', this.toggleTextEdit, this);
27999         this.on('blur', this.toggleTextEdit, this);
28000         this.on('specialkey', this.resizeTextArea, this);
28001     },
28002     
28003     toggleTextEdit : function()
28004     {
28005         var sh = this.markdownEl.getHeight();
28006         this.inputEl().addClass('d-none');
28007         this.markdownEl.addClass('d-none');
28008         if (!this.editing) {
28009             // show editor?
28010             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28011             this.inputEl().removeClass('d-none');
28012             this.inputEl().focus();
28013             this.editing = true;
28014             return;
28015         }
28016         // show showdown...
28017         this.updateMarkdown();
28018         this.markdownEl.removeClass('d-none');
28019         this.editing = false;
28020         return;
28021     },
28022     updateMarkdown : function()
28023     {
28024         if (this.getValue() == '') {
28025             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28026             return;
28027         }
28028  
28029         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28030     },
28031     
28032     resizeTextArea: function () {
28033         
28034         var sh = 100;
28035         Roo.log([sh, this.getValue().split("\n").length * 30]);
28036         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28037     },
28038     setValue : function(val)
28039     {
28040         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28041         if (!this.editing) {
28042             this.updateMarkdown();
28043         }
28044         
28045     },
28046     focus : function()
28047     {
28048         if (!this.editing) {
28049             this.toggleTextEdit();
28050         }
28051         
28052     }
28053
28054
28055 });/*
28056  * Based on:
28057  * Ext JS Library 1.1.1
28058  * Copyright(c) 2006-2007, Ext JS, LLC.
28059  *
28060  * Originally Released Under LGPL - original licence link has changed is not relivant.
28061  *
28062  * Fork - LGPL
28063  * <script type="text/javascript">
28064  */
28065  
28066 /**
28067  * @class Roo.bootstrap.PagingToolbar
28068  * @extends Roo.bootstrap.NavSimplebar
28069  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28070  * @constructor
28071  * Create a new PagingToolbar
28072  * @param {Object} config The config object
28073  * @param {Roo.data.Store} store
28074  */
28075 Roo.bootstrap.PagingToolbar = function(config)
28076 {
28077     // old args format still supported... - xtype is prefered..
28078         // created from xtype...
28079     
28080     this.ds = config.dataSource;
28081     
28082     if (config.store && !this.ds) {
28083         this.store= Roo.factory(config.store, Roo.data);
28084         this.ds = this.store;
28085         this.ds.xmodule = this.xmodule || false;
28086     }
28087     
28088     this.toolbarItems = [];
28089     if (config.items) {
28090         this.toolbarItems = config.items;
28091     }
28092     
28093     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28094     
28095     this.cursor = 0;
28096     
28097     if (this.ds) { 
28098         this.bind(this.ds);
28099     }
28100     
28101     if (Roo.bootstrap.version == 4) {
28102         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28103     } else {
28104         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28105     }
28106     
28107 };
28108
28109 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28110     /**
28111      * @cfg {Roo.data.Store} dataSource
28112      * The underlying data store providing the paged data
28113      */
28114     /**
28115      * @cfg {String/HTMLElement/Element} container
28116      * container The id or element that will contain the toolbar
28117      */
28118     /**
28119      * @cfg {Boolean} displayInfo
28120      * True to display the displayMsg (defaults to false)
28121      */
28122     /**
28123      * @cfg {Number} pageSize
28124      * The number of records to display per page (defaults to 20)
28125      */
28126     pageSize: 20,
28127     /**
28128      * @cfg {String} displayMsg
28129      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28130      */
28131     displayMsg : 'Displaying {0} - {1} of {2}',
28132     /**
28133      * @cfg {String} emptyMsg
28134      * The message to display when no records are found (defaults to "No data to display")
28135      */
28136     emptyMsg : 'No data to display',
28137     /**
28138      * Customizable piece of the default paging text (defaults to "Page")
28139      * @type String
28140      */
28141     beforePageText : "Page",
28142     /**
28143      * Customizable piece of the default paging text (defaults to "of %0")
28144      * @type String
28145      */
28146     afterPageText : "of {0}",
28147     /**
28148      * Customizable piece of the default paging text (defaults to "First Page")
28149      * @type String
28150      */
28151     firstText : "First Page",
28152     /**
28153      * Customizable piece of the default paging text (defaults to "Previous Page")
28154      * @type String
28155      */
28156     prevText : "Previous Page",
28157     /**
28158      * Customizable piece of the default paging text (defaults to "Next Page")
28159      * @type String
28160      */
28161     nextText : "Next Page",
28162     /**
28163      * Customizable piece of the default paging text (defaults to "Last Page")
28164      * @type String
28165      */
28166     lastText : "Last Page",
28167     /**
28168      * Customizable piece of the default paging text (defaults to "Refresh")
28169      * @type String
28170      */
28171     refreshText : "Refresh",
28172
28173     buttons : false,
28174     // private
28175     onRender : function(ct, position) 
28176     {
28177         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28178         this.navgroup.parentId = this.id;
28179         this.navgroup.onRender(this.el, null);
28180         // add the buttons to the navgroup
28181         
28182         if(this.displayInfo){
28183             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28184             this.displayEl = this.el.select('.x-paging-info', true).first();
28185 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28186 //            this.displayEl = navel.el.select('span',true).first();
28187         }
28188         
28189         var _this = this;
28190         
28191         if(this.buttons){
28192             Roo.each(_this.buttons, function(e){ // this might need to use render????
28193                Roo.factory(e).render(_this.el);
28194             });
28195         }
28196             
28197         Roo.each(_this.toolbarItems, function(e) {
28198             _this.navgroup.addItem(e);
28199         });
28200         
28201         
28202         this.first = this.navgroup.addItem({
28203             tooltip: this.firstText,
28204             cls: "prev btn-outline-secondary",
28205             html : ' <i class="fa fa-step-backward"></i>',
28206             disabled: true,
28207             preventDefault: true,
28208             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28209         });
28210         
28211         this.prev =  this.navgroup.addItem({
28212             tooltip: this.prevText,
28213             cls: "prev btn-outline-secondary",
28214             html : ' <i class="fa fa-backward"></i>',
28215             disabled: true,
28216             preventDefault: true,
28217             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28218         });
28219     //this.addSeparator();
28220         
28221         
28222         var field = this.navgroup.addItem( {
28223             tagtype : 'span',
28224             cls : 'x-paging-position  btn-outline-secondary',
28225              disabled: true,
28226             html : this.beforePageText  +
28227                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28228                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28229          } ); //?? escaped?
28230         
28231         this.field = field.el.select('input', true).first();
28232         this.field.on("keydown", this.onPagingKeydown, this);
28233         this.field.on("focus", function(){this.dom.select();});
28234     
28235     
28236         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28237         //this.field.setHeight(18);
28238         //this.addSeparator();
28239         this.next = this.navgroup.addItem({
28240             tooltip: this.nextText,
28241             cls: "next btn-outline-secondary",
28242             html : ' <i class="fa fa-forward"></i>',
28243             disabled: true,
28244             preventDefault: true,
28245             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28246         });
28247         this.last = this.navgroup.addItem({
28248             tooltip: this.lastText,
28249             html : ' <i class="fa fa-step-forward"></i>',
28250             cls: "next btn-outline-secondary",
28251             disabled: true,
28252             preventDefault: true,
28253             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28254         });
28255     //this.addSeparator();
28256         this.loading = this.navgroup.addItem({
28257             tooltip: this.refreshText,
28258             cls: "btn-outline-secondary",
28259             html : ' <i class="fa fa-refresh"></i>',
28260             preventDefault: true,
28261             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28262         });
28263         
28264     },
28265
28266     // private
28267     updateInfo : function(){
28268         if(this.displayEl){
28269             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28270             var msg = count == 0 ?
28271                 this.emptyMsg :
28272                 String.format(
28273                     this.displayMsg,
28274                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28275                 );
28276             this.displayEl.update(msg);
28277         }
28278     },
28279
28280     // private
28281     onLoad : function(ds, r, o)
28282     {
28283         this.cursor = o.params && o.params.start ? o.params.start : 0;
28284         
28285         var d = this.getPageData(),
28286             ap = d.activePage,
28287             ps = d.pages;
28288         
28289         
28290         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28291         this.field.dom.value = ap;
28292         this.first.setDisabled(ap == 1);
28293         this.prev.setDisabled(ap == 1);
28294         this.next.setDisabled(ap == ps);
28295         this.last.setDisabled(ap == ps);
28296         this.loading.enable();
28297         this.updateInfo();
28298     },
28299
28300     // private
28301     getPageData : function(){
28302         var total = this.ds.getTotalCount();
28303         return {
28304             total : total,
28305             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28306             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28307         };
28308     },
28309
28310     // private
28311     onLoadError : function(){
28312         this.loading.enable();
28313     },
28314
28315     // private
28316     onPagingKeydown : function(e){
28317         var k = e.getKey();
28318         var d = this.getPageData();
28319         if(k == e.RETURN){
28320             var v = this.field.dom.value, pageNum;
28321             if(!v || isNaN(pageNum = parseInt(v, 10))){
28322                 this.field.dom.value = d.activePage;
28323                 return;
28324             }
28325             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28326             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28327             e.stopEvent();
28328         }
28329         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))
28330         {
28331           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28332           this.field.dom.value = pageNum;
28333           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28334           e.stopEvent();
28335         }
28336         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28337         {
28338           var v = this.field.dom.value, pageNum; 
28339           var increment = (e.shiftKey) ? 10 : 1;
28340           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28341                 increment *= -1;
28342           }
28343           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28344             this.field.dom.value = d.activePage;
28345             return;
28346           }
28347           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28348           {
28349             this.field.dom.value = parseInt(v, 10) + increment;
28350             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28351             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28352           }
28353           e.stopEvent();
28354         }
28355     },
28356
28357     // private
28358     beforeLoad : function(){
28359         if(this.loading){
28360             this.loading.disable();
28361         }
28362     },
28363
28364     // private
28365     onClick : function(which){
28366         
28367         var ds = this.ds;
28368         if (!ds) {
28369             return;
28370         }
28371         
28372         switch(which){
28373             case "first":
28374                 ds.load({params:{start: 0, limit: this.pageSize}});
28375             break;
28376             case "prev":
28377                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28378             break;
28379             case "next":
28380                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28381             break;
28382             case "last":
28383                 var total = ds.getTotalCount();
28384                 var extra = total % this.pageSize;
28385                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28386                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28387             break;
28388             case "refresh":
28389                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28390             break;
28391         }
28392     },
28393
28394     /**
28395      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28396      * @param {Roo.data.Store} store The data store to unbind
28397      */
28398     unbind : function(ds){
28399         ds.un("beforeload", this.beforeLoad, this);
28400         ds.un("load", this.onLoad, this);
28401         ds.un("loadexception", this.onLoadError, this);
28402         ds.un("remove", this.updateInfo, this);
28403         ds.un("add", this.updateInfo, this);
28404         this.ds = undefined;
28405     },
28406
28407     /**
28408      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28409      * @param {Roo.data.Store} store The data store to bind
28410      */
28411     bind : function(ds){
28412         ds.on("beforeload", this.beforeLoad, this);
28413         ds.on("load", this.onLoad, this);
28414         ds.on("loadexception", this.onLoadError, this);
28415         ds.on("remove", this.updateInfo, this);
28416         ds.on("add", this.updateInfo, this);
28417         this.ds = ds;
28418     }
28419 });/*
28420  * - LGPL
28421  *
28422  * element
28423  * 
28424  */
28425
28426 /**
28427  * @class Roo.bootstrap.MessageBar
28428  * @extends Roo.bootstrap.Component
28429  * Bootstrap MessageBar class
28430  * @cfg {String} html contents of the MessageBar
28431  * @cfg {String} weight (info | success | warning | danger) default info
28432  * @cfg {String} beforeClass insert the bar before the given class
28433  * @cfg {Boolean} closable (true | false) default false
28434  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28435  * 
28436  * @constructor
28437  * Create a new Element
28438  * @param {Object} config The config object
28439  */
28440
28441 Roo.bootstrap.MessageBar = function(config){
28442     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28443 };
28444
28445 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28446     
28447     html: '',
28448     weight: 'info',
28449     closable: false,
28450     fixed: false,
28451     beforeClass: 'bootstrap-sticky-wrap',
28452     
28453     getAutoCreate : function(){
28454         
28455         var cfg = {
28456             tag: 'div',
28457             cls: 'alert alert-dismissable alert-' + this.weight,
28458             cn: [
28459                 {
28460                     tag: 'span',
28461                     cls: 'message',
28462                     html: this.html || ''
28463                 }
28464             ]
28465         };
28466         
28467         if(this.fixed){
28468             cfg.cls += ' alert-messages-fixed';
28469         }
28470         
28471         if(this.closable){
28472             cfg.cn.push({
28473                 tag: 'button',
28474                 cls: 'close',
28475                 html: 'x'
28476             });
28477         }
28478         
28479         return cfg;
28480     },
28481     
28482     onRender : function(ct, position)
28483     {
28484         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28485         
28486         if(!this.el){
28487             var cfg = Roo.apply({},  this.getAutoCreate());
28488             cfg.id = Roo.id();
28489             
28490             if (this.cls) {
28491                 cfg.cls += ' ' + this.cls;
28492             }
28493             if (this.style) {
28494                 cfg.style = this.style;
28495             }
28496             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28497             
28498             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28499         }
28500         
28501         this.el.select('>button.close').on('click', this.hide, this);
28502         
28503     },
28504     
28505     show : function()
28506     {
28507         if (!this.rendered) {
28508             this.render();
28509         }
28510         
28511         this.el.show();
28512         
28513         this.fireEvent('show', this);
28514         
28515     },
28516     
28517     hide : function()
28518     {
28519         if (!this.rendered) {
28520             this.render();
28521         }
28522         
28523         this.el.hide();
28524         
28525         this.fireEvent('hide', this);
28526     },
28527     
28528     update : function()
28529     {
28530 //        var e = this.el.dom.firstChild;
28531 //        
28532 //        if(this.closable){
28533 //            e = e.nextSibling;
28534 //        }
28535 //        
28536 //        e.data = this.html || '';
28537
28538         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28539     }
28540    
28541 });
28542
28543  
28544
28545      /*
28546  * - LGPL
28547  *
28548  * Graph
28549  * 
28550  */
28551
28552
28553 /**
28554  * @class Roo.bootstrap.Graph
28555  * @extends Roo.bootstrap.Component
28556  * Bootstrap Graph class
28557 > Prameters
28558  -sm {number} sm 4
28559  -md {number} md 5
28560  @cfg {String} graphtype  bar | vbar | pie
28561  @cfg {number} g_x coodinator | centre x (pie)
28562  @cfg {number} g_y coodinator | centre y (pie)
28563  @cfg {number} g_r radius (pie)
28564  @cfg {number} g_height height of the chart (respected by all elements in the set)
28565  @cfg {number} g_width width of the chart (respected by all elements in the set)
28566  @cfg {Object} title The title of the chart
28567     
28568  -{Array}  values
28569  -opts (object) options for the chart 
28570      o {
28571      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28572      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28573      o vgutter (number)
28574      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.
28575      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28576      o to
28577      o stretch (boolean)
28578      o }
28579  -opts (object) options for the pie
28580      o{
28581      o cut
28582      o startAngle (number)
28583      o endAngle (number)
28584      } 
28585  *
28586  * @constructor
28587  * Create a new Input
28588  * @param {Object} config The config object
28589  */
28590
28591 Roo.bootstrap.Graph = function(config){
28592     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28593     
28594     this.addEvents({
28595         // img events
28596         /**
28597          * @event click
28598          * The img click event for the img.
28599          * @param {Roo.EventObject} e
28600          */
28601         "click" : true
28602     });
28603 };
28604
28605 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28606     
28607     sm: 4,
28608     md: 5,
28609     graphtype: 'bar',
28610     g_height: 250,
28611     g_width: 400,
28612     g_x: 50,
28613     g_y: 50,
28614     g_r: 30,
28615     opts:{
28616         //g_colors: this.colors,
28617         g_type: 'soft',
28618         g_gutter: '20%'
28619
28620     },
28621     title : false,
28622
28623     getAutoCreate : function(){
28624         
28625         var cfg = {
28626             tag: 'div',
28627             html : null
28628         };
28629         
28630         
28631         return  cfg;
28632     },
28633
28634     onRender : function(ct,position){
28635         
28636         
28637         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28638         
28639         if (typeof(Raphael) == 'undefined') {
28640             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28641             return;
28642         }
28643         
28644         this.raphael = Raphael(this.el.dom);
28645         
28646                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28647                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28648                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28649                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28650                 /*
28651                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28652                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28653                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28654                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28655                 
28656                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28657                 r.barchart(330, 10, 300, 220, data1);
28658                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28659                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28660                 */
28661                 
28662                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28663                 // r.barchart(30, 30, 560, 250,  xdata, {
28664                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28665                 //     axis : "0 0 1 1",
28666                 //     axisxlabels :  xdata
28667                 //     //yvalues : cols,
28668                    
28669                 // });
28670 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28671 //        
28672 //        this.load(null,xdata,{
28673 //                axis : "0 0 1 1",
28674 //                axisxlabels :  xdata
28675 //                });
28676
28677     },
28678
28679     load : function(graphtype,xdata,opts)
28680     {
28681         this.raphael.clear();
28682         if(!graphtype) {
28683             graphtype = this.graphtype;
28684         }
28685         if(!opts){
28686             opts = this.opts;
28687         }
28688         var r = this.raphael,
28689             fin = function () {
28690                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28691             },
28692             fout = function () {
28693                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28694             },
28695             pfin = function() {
28696                 this.sector.stop();
28697                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28698
28699                 if (this.label) {
28700                     this.label[0].stop();
28701                     this.label[0].attr({ r: 7.5 });
28702                     this.label[1].attr({ "font-weight": 800 });
28703                 }
28704             },
28705             pfout = function() {
28706                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28707
28708                 if (this.label) {
28709                     this.label[0].animate({ r: 5 }, 500, "bounce");
28710                     this.label[1].attr({ "font-weight": 400 });
28711                 }
28712             };
28713
28714         switch(graphtype){
28715             case 'bar':
28716                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28717                 break;
28718             case 'hbar':
28719                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28720                 break;
28721             case 'pie':
28722 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28723 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28724 //            
28725                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28726                 
28727                 break;
28728
28729         }
28730         
28731         if(this.title){
28732             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28733         }
28734         
28735     },
28736     
28737     setTitle: function(o)
28738     {
28739         this.title = o;
28740     },
28741     
28742     initEvents: function() {
28743         
28744         if(!this.href){
28745             this.el.on('click', this.onClick, this);
28746         }
28747     },
28748     
28749     onClick : function(e)
28750     {
28751         Roo.log('img onclick');
28752         this.fireEvent('click', this, e);
28753     }
28754    
28755 });
28756
28757  
28758 /*
28759  * - LGPL
28760  *
28761  * numberBox
28762  * 
28763  */
28764 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28765
28766 /**
28767  * @class Roo.bootstrap.dash.NumberBox
28768  * @extends Roo.bootstrap.Component
28769  * Bootstrap NumberBox class
28770  * @cfg {String} headline Box headline
28771  * @cfg {String} content Box content
28772  * @cfg {String} icon Box icon
28773  * @cfg {String} footer Footer text
28774  * @cfg {String} fhref Footer href
28775  * 
28776  * @constructor
28777  * Create a new NumberBox
28778  * @param {Object} config The config object
28779  */
28780
28781
28782 Roo.bootstrap.dash.NumberBox = function(config){
28783     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28784     
28785 };
28786
28787 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28788     
28789     headline : '',
28790     content : '',
28791     icon : '',
28792     footer : '',
28793     fhref : '',
28794     ficon : '',
28795     
28796     getAutoCreate : function(){
28797         
28798         var cfg = {
28799             tag : 'div',
28800             cls : 'small-box ',
28801             cn : [
28802                 {
28803                     tag : 'div',
28804                     cls : 'inner',
28805                     cn :[
28806                         {
28807                             tag : 'h3',
28808                             cls : 'roo-headline',
28809                             html : this.headline
28810                         },
28811                         {
28812                             tag : 'p',
28813                             cls : 'roo-content',
28814                             html : this.content
28815                         }
28816                     ]
28817                 }
28818             ]
28819         };
28820         
28821         if(this.icon){
28822             cfg.cn.push({
28823                 tag : 'div',
28824                 cls : 'icon',
28825                 cn :[
28826                     {
28827                         tag : 'i',
28828                         cls : 'ion ' + this.icon
28829                     }
28830                 ]
28831             });
28832         }
28833         
28834         if(this.footer){
28835             var footer = {
28836                 tag : 'a',
28837                 cls : 'small-box-footer',
28838                 href : this.fhref || '#',
28839                 html : this.footer
28840             };
28841             
28842             cfg.cn.push(footer);
28843             
28844         }
28845         
28846         return  cfg;
28847     },
28848
28849     onRender : function(ct,position){
28850         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28851
28852
28853        
28854                 
28855     },
28856
28857     setHeadline: function (value)
28858     {
28859         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28860     },
28861     
28862     setFooter: function (value, href)
28863     {
28864         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28865         
28866         if(href){
28867             this.el.select('a.small-box-footer',true).first().attr('href', href);
28868         }
28869         
28870     },
28871
28872     setContent: function (value)
28873     {
28874         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28875     },
28876
28877     initEvents: function() 
28878     {   
28879         
28880     }
28881     
28882 });
28883
28884  
28885 /*
28886  * - LGPL
28887  *
28888  * TabBox
28889  * 
28890  */
28891 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28892
28893 /**
28894  * @class Roo.bootstrap.dash.TabBox
28895  * @extends Roo.bootstrap.Component
28896  * Bootstrap TabBox class
28897  * @cfg {String} title Title of the TabBox
28898  * @cfg {String} icon Icon of the TabBox
28899  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28900  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28901  * 
28902  * @constructor
28903  * Create a new TabBox
28904  * @param {Object} config The config object
28905  */
28906
28907
28908 Roo.bootstrap.dash.TabBox = function(config){
28909     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28910     this.addEvents({
28911         // raw events
28912         /**
28913          * @event addpane
28914          * When a pane is added
28915          * @param {Roo.bootstrap.dash.TabPane} pane
28916          */
28917         "addpane" : true,
28918         /**
28919          * @event activatepane
28920          * When a pane is activated
28921          * @param {Roo.bootstrap.dash.TabPane} pane
28922          */
28923         "activatepane" : true
28924         
28925          
28926     });
28927     
28928     this.panes = [];
28929 };
28930
28931 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28932
28933     title : '',
28934     icon : false,
28935     showtabs : true,
28936     tabScrollable : false,
28937     
28938     getChildContainer : function()
28939     {
28940         return this.el.select('.tab-content', true).first();
28941     },
28942     
28943     getAutoCreate : function(){
28944         
28945         var header = {
28946             tag: 'li',
28947             cls: 'pull-left header',
28948             html: this.title,
28949             cn : []
28950         };
28951         
28952         if(this.icon){
28953             header.cn.push({
28954                 tag: 'i',
28955                 cls: 'fa ' + this.icon
28956             });
28957         }
28958         
28959         var h = {
28960             tag: 'ul',
28961             cls: 'nav nav-tabs pull-right',
28962             cn: [
28963                 header
28964             ]
28965         };
28966         
28967         if(this.tabScrollable){
28968             h = {
28969                 tag: 'div',
28970                 cls: 'tab-header',
28971                 cn: [
28972                     {
28973                         tag: 'ul',
28974                         cls: 'nav nav-tabs pull-right',
28975                         cn: [
28976                             header
28977                         ]
28978                     }
28979                 ]
28980             };
28981         }
28982         
28983         var cfg = {
28984             tag: 'div',
28985             cls: 'nav-tabs-custom',
28986             cn: [
28987                 h,
28988                 {
28989                     tag: 'div',
28990                     cls: 'tab-content no-padding',
28991                     cn: []
28992                 }
28993             ]
28994         };
28995
28996         return  cfg;
28997     },
28998     initEvents : function()
28999     {
29000         //Roo.log('add add pane handler');
29001         this.on('addpane', this.onAddPane, this);
29002     },
29003      /**
29004      * Updates the box title
29005      * @param {String} html to set the title to.
29006      */
29007     setTitle : function(value)
29008     {
29009         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29010     },
29011     onAddPane : function(pane)
29012     {
29013         this.panes.push(pane);
29014         //Roo.log('addpane');
29015         //Roo.log(pane);
29016         // tabs are rendere left to right..
29017         if(!this.showtabs){
29018             return;
29019         }
29020         
29021         var ctr = this.el.select('.nav-tabs', true).first();
29022          
29023          
29024         var existing = ctr.select('.nav-tab',true);
29025         var qty = existing.getCount();;
29026         
29027         
29028         var tab = ctr.createChild({
29029             tag : 'li',
29030             cls : 'nav-tab' + (qty ? '' : ' active'),
29031             cn : [
29032                 {
29033                     tag : 'a',
29034                     href:'#',
29035                     html : pane.title
29036                 }
29037             ]
29038         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29039         pane.tab = tab;
29040         
29041         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29042         if (!qty) {
29043             pane.el.addClass('active');
29044         }
29045         
29046                 
29047     },
29048     onTabClick : function(ev,un,ob,pane)
29049     {
29050         //Roo.log('tab - prev default');
29051         ev.preventDefault();
29052         
29053         
29054         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29055         pane.tab.addClass('active');
29056         //Roo.log(pane.title);
29057         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29058         // technically we should have a deactivate event.. but maybe add later.
29059         // and it should not de-activate the selected tab...
29060         this.fireEvent('activatepane', pane);
29061         pane.el.addClass('active');
29062         pane.fireEvent('activate');
29063         
29064         
29065     },
29066     
29067     getActivePane : function()
29068     {
29069         var r = false;
29070         Roo.each(this.panes, function(p) {
29071             if(p.el.hasClass('active')){
29072                 r = p;
29073                 return false;
29074             }
29075             
29076             return;
29077         });
29078         
29079         return r;
29080     }
29081     
29082     
29083 });
29084
29085  
29086 /*
29087  * - LGPL
29088  *
29089  * Tab pane
29090  * 
29091  */
29092 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29093 /**
29094  * @class Roo.bootstrap.TabPane
29095  * @extends Roo.bootstrap.Component
29096  * Bootstrap TabPane class
29097  * @cfg {Boolean} active (false | true) Default false
29098  * @cfg {String} title title of panel
29099
29100  * 
29101  * @constructor
29102  * Create a new TabPane
29103  * @param {Object} config The config object
29104  */
29105
29106 Roo.bootstrap.dash.TabPane = function(config){
29107     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29108     
29109     this.addEvents({
29110         // raw events
29111         /**
29112          * @event activate
29113          * When a pane is activated
29114          * @param {Roo.bootstrap.dash.TabPane} pane
29115          */
29116         "activate" : true
29117          
29118     });
29119 };
29120
29121 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29122     
29123     active : false,
29124     title : '',
29125     
29126     // the tabBox that this is attached to.
29127     tab : false,
29128      
29129     getAutoCreate : function() 
29130     {
29131         var cfg = {
29132             tag: 'div',
29133             cls: 'tab-pane'
29134         };
29135         
29136         if(this.active){
29137             cfg.cls += ' active';
29138         }
29139         
29140         return cfg;
29141     },
29142     initEvents  : function()
29143     {
29144         //Roo.log('trigger add pane handler');
29145         this.parent().fireEvent('addpane', this)
29146     },
29147     
29148      /**
29149      * Updates the tab title 
29150      * @param {String} html to set the title to.
29151      */
29152     setTitle: function(str)
29153     {
29154         if (!this.tab) {
29155             return;
29156         }
29157         this.title = str;
29158         this.tab.select('a', true).first().dom.innerHTML = str;
29159         
29160     }
29161     
29162     
29163     
29164 });
29165
29166  
29167
29168
29169  /*
29170  * - LGPL
29171  *
29172  * menu
29173  * 
29174  */
29175 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29176
29177 /**
29178  * @class Roo.bootstrap.menu.Menu
29179  * @extends Roo.bootstrap.Component
29180  * Bootstrap Menu class - container for Menu
29181  * @cfg {String} html Text of the menu
29182  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29183  * @cfg {String} icon Font awesome icon
29184  * @cfg {String} pos Menu align to (top | bottom) default bottom
29185  * 
29186  * 
29187  * @constructor
29188  * Create a new Menu
29189  * @param {Object} config The config object
29190  */
29191
29192
29193 Roo.bootstrap.menu.Menu = function(config){
29194     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29195     
29196     this.addEvents({
29197         /**
29198          * @event beforeshow
29199          * Fires before this menu is displayed
29200          * @param {Roo.bootstrap.menu.Menu} this
29201          */
29202         beforeshow : true,
29203         /**
29204          * @event beforehide
29205          * Fires before this menu is hidden
29206          * @param {Roo.bootstrap.menu.Menu} this
29207          */
29208         beforehide : true,
29209         /**
29210          * @event show
29211          * Fires after this menu is displayed
29212          * @param {Roo.bootstrap.menu.Menu} this
29213          */
29214         show : true,
29215         /**
29216          * @event hide
29217          * Fires after this menu is hidden
29218          * @param {Roo.bootstrap.menu.Menu} this
29219          */
29220         hide : true,
29221         /**
29222          * @event click
29223          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29224          * @param {Roo.bootstrap.menu.Menu} this
29225          * @param {Roo.EventObject} e
29226          */
29227         click : true
29228     });
29229     
29230 };
29231
29232 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29233     
29234     submenu : false,
29235     html : '',
29236     weight : 'default',
29237     icon : false,
29238     pos : 'bottom',
29239     
29240     
29241     getChildContainer : function() {
29242         if(this.isSubMenu){
29243             return this.el;
29244         }
29245         
29246         return this.el.select('ul.dropdown-menu', true).first();  
29247     },
29248     
29249     getAutoCreate : function()
29250     {
29251         var text = [
29252             {
29253                 tag : 'span',
29254                 cls : 'roo-menu-text',
29255                 html : this.html
29256             }
29257         ];
29258         
29259         if(this.icon){
29260             text.unshift({
29261                 tag : 'i',
29262                 cls : 'fa ' + this.icon
29263             })
29264         }
29265         
29266         
29267         var cfg = {
29268             tag : 'div',
29269             cls : 'btn-group',
29270             cn : [
29271                 {
29272                     tag : 'button',
29273                     cls : 'dropdown-button btn btn-' + this.weight,
29274                     cn : text
29275                 },
29276                 {
29277                     tag : 'button',
29278                     cls : 'dropdown-toggle btn btn-' + this.weight,
29279                     cn : [
29280                         {
29281                             tag : 'span',
29282                             cls : 'caret'
29283                         }
29284                     ]
29285                 },
29286                 {
29287                     tag : 'ul',
29288                     cls : 'dropdown-menu'
29289                 }
29290             ]
29291             
29292         };
29293         
29294         if(this.pos == 'top'){
29295             cfg.cls += ' dropup';
29296         }
29297         
29298         if(this.isSubMenu){
29299             cfg = {
29300                 tag : 'ul',
29301                 cls : 'dropdown-menu'
29302             }
29303         }
29304         
29305         return cfg;
29306     },
29307     
29308     onRender : function(ct, position)
29309     {
29310         this.isSubMenu = ct.hasClass('dropdown-submenu');
29311         
29312         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29313     },
29314     
29315     initEvents : function() 
29316     {
29317         if(this.isSubMenu){
29318             return;
29319         }
29320         
29321         this.hidden = true;
29322         
29323         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29324         this.triggerEl.on('click', this.onTriggerPress, this);
29325         
29326         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29327         this.buttonEl.on('click', this.onClick, this);
29328         
29329     },
29330     
29331     list : function()
29332     {
29333         if(this.isSubMenu){
29334             return this.el;
29335         }
29336         
29337         return this.el.select('ul.dropdown-menu', true).first();
29338     },
29339     
29340     onClick : function(e)
29341     {
29342         this.fireEvent("click", this, e);
29343     },
29344     
29345     onTriggerPress  : function(e)
29346     {   
29347         if (this.isVisible()) {
29348             this.hide();
29349         } else {
29350             this.show();
29351         }
29352     },
29353     
29354     isVisible : function(){
29355         return !this.hidden;
29356     },
29357     
29358     show : function()
29359     {
29360         this.fireEvent("beforeshow", this);
29361         
29362         this.hidden = false;
29363         this.el.addClass('open');
29364         
29365         Roo.get(document).on("mouseup", this.onMouseUp, this);
29366         
29367         this.fireEvent("show", this);
29368         
29369         
29370     },
29371     
29372     hide : function()
29373     {
29374         this.fireEvent("beforehide", this);
29375         
29376         this.hidden = true;
29377         this.el.removeClass('open');
29378         
29379         Roo.get(document).un("mouseup", this.onMouseUp);
29380         
29381         this.fireEvent("hide", this);
29382     },
29383     
29384     onMouseUp : function()
29385     {
29386         this.hide();
29387     }
29388     
29389 });
29390
29391  
29392  /*
29393  * - LGPL
29394  *
29395  * menu item
29396  * 
29397  */
29398 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29399
29400 /**
29401  * @class Roo.bootstrap.menu.Item
29402  * @extends Roo.bootstrap.Component
29403  * Bootstrap MenuItem class
29404  * @cfg {Boolean} submenu (true | false) default false
29405  * @cfg {String} html text of the item
29406  * @cfg {String} href the link
29407  * @cfg {Boolean} disable (true | false) default false
29408  * @cfg {Boolean} preventDefault (true | false) default true
29409  * @cfg {String} icon Font awesome icon
29410  * @cfg {String} pos Submenu align to (left | right) default right 
29411  * 
29412  * 
29413  * @constructor
29414  * Create a new Item
29415  * @param {Object} config The config object
29416  */
29417
29418
29419 Roo.bootstrap.menu.Item = function(config){
29420     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29421     this.addEvents({
29422         /**
29423          * @event mouseover
29424          * Fires when the mouse is hovering over this menu
29425          * @param {Roo.bootstrap.menu.Item} this
29426          * @param {Roo.EventObject} e
29427          */
29428         mouseover : true,
29429         /**
29430          * @event mouseout
29431          * Fires when the mouse exits this menu
29432          * @param {Roo.bootstrap.menu.Item} this
29433          * @param {Roo.EventObject} e
29434          */
29435         mouseout : true,
29436         // raw events
29437         /**
29438          * @event click
29439          * The raw click event for the entire grid.
29440          * @param {Roo.EventObject} e
29441          */
29442         click : true
29443     });
29444 };
29445
29446 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29447     
29448     submenu : false,
29449     href : '',
29450     html : '',
29451     preventDefault: true,
29452     disable : false,
29453     icon : false,
29454     pos : 'right',
29455     
29456     getAutoCreate : function()
29457     {
29458         var text = [
29459             {
29460                 tag : 'span',
29461                 cls : 'roo-menu-item-text',
29462                 html : this.html
29463             }
29464         ];
29465         
29466         if(this.icon){
29467             text.unshift({
29468                 tag : 'i',
29469                 cls : 'fa ' + this.icon
29470             })
29471         }
29472         
29473         var cfg = {
29474             tag : 'li',
29475             cn : [
29476                 {
29477                     tag : 'a',
29478                     href : this.href || '#',
29479                     cn : text
29480                 }
29481             ]
29482         };
29483         
29484         if(this.disable){
29485             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29486         }
29487         
29488         if(this.submenu){
29489             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29490             
29491             if(this.pos == 'left'){
29492                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29493             }
29494         }
29495         
29496         return cfg;
29497     },
29498     
29499     initEvents : function() 
29500     {
29501         this.el.on('mouseover', this.onMouseOver, this);
29502         this.el.on('mouseout', this.onMouseOut, this);
29503         
29504         this.el.select('a', true).first().on('click', this.onClick, this);
29505         
29506     },
29507     
29508     onClick : function(e)
29509     {
29510         if(this.preventDefault){
29511             e.preventDefault();
29512         }
29513         
29514         this.fireEvent("click", this, e);
29515     },
29516     
29517     onMouseOver : function(e)
29518     {
29519         if(this.submenu && this.pos == 'left'){
29520             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29521         }
29522         
29523         this.fireEvent("mouseover", this, e);
29524     },
29525     
29526     onMouseOut : function(e)
29527     {
29528         this.fireEvent("mouseout", this, e);
29529     }
29530 });
29531
29532  
29533
29534  /*
29535  * - LGPL
29536  *
29537  * menu separator
29538  * 
29539  */
29540 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29541
29542 /**
29543  * @class Roo.bootstrap.menu.Separator
29544  * @extends Roo.bootstrap.Component
29545  * Bootstrap Separator class
29546  * 
29547  * @constructor
29548  * Create a new Separator
29549  * @param {Object} config The config object
29550  */
29551
29552
29553 Roo.bootstrap.menu.Separator = function(config){
29554     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29555 };
29556
29557 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29558     
29559     getAutoCreate : function(){
29560         var cfg = {
29561             tag : 'li',
29562             cls: 'dropdown-divider divider'
29563         };
29564         
29565         return cfg;
29566     }
29567    
29568 });
29569
29570  
29571
29572  /*
29573  * - LGPL
29574  *
29575  * Tooltip
29576  * 
29577  */
29578
29579 /**
29580  * @class Roo.bootstrap.Tooltip
29581  * Bootstrap Tooltip class
29582  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29583  * to determine which dom element triggers the tooltip.
29584  * 
29585  * It needs to add support for additional attributes like tooltip-position
29586  * 
29587  * @constructor
29588  * Create a new Toolti
29589  * @param {Object} config The config object
29590  */
29591
29592 Roo.bootstrap.Tooltip = function(config){
29593     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29594     
29595     this.alignment = Roo.bootstrap.Tooltip.alignment;
29596     
29597     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29598         this.alignment = config.alignment;
29599     }
29600     
29601 };
29602
29603 Roo.apply(Roo.bootstrap.Tooltip, {
29604     /**
29605      * @function init initialize tooltip monitoring.
29606      * @static
29607      */
29608     currentEl : false,
29609     currentTip : false,
29610     currentRegion : false,
29611     
29612     //  init : delay?
29613     
29614     init : function()
29615     {
29616         Roo.get(document).on('mouseover', this.enter ,this);
29617         Roo.get(document).on('mouseout', this.leave, this);
29618          
29619         
29620         this.currentTip = new Roo.bootstrap.Tooltip();
29621     },
29622     
29623     enter : function(ev)
29624     {
29625         var dom = ev.getTarget();
29626         
29627         //Roo.log(['enter',dom]);
29628         var el = Roo.fly(dom);
29629         if (this.currentEl) {
29630             //Roo.log(dom);
29631             //Roo.log(this.currentEl);
29632             //Roo.log(this.currentEl.contains(dom));
29633             if (this.currentEl == el) {
29634                 return;
29635             }
29636             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29637                 return;
29638             }
29639
29640         }
29641         
29642         if (this.currentTip.el) {
29643             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29644         }    
29645         //Roo.log(ev);
29646         
29647         if(!el || el.dom == document){
29648             return;
29649         }
29650         
29651         var bindEl = el; 
29652         var pel = false;
29653         if (!el.attr('tooltip')) {
29654             pel = el.findParent("[tooltip]");
29655             if (pel) {
29656                 bindEl = Roo.get(pel);
29657             }
29658         }
29659         
29660        
29661         
29662         // you can not look for children, as if el is the body.. then everythign is the child..
29663         if (!pel && !el.attr('tooltip')) { //
29664             if (!el.select("[tooltip]").elements.length) {
29665                 return;
29666             }
29667             // is the mouse over this child...?
29668             bindEl = el.select("[tooltip]").first();
29669             var xy = ev.getXY();
29670             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29671                 //Roo.log("not in region.");
29672                 return;
29673             }
29674             //Roo.log("child element over..");
29675             
29676         }
29677         this.currentEl = el;
29678         this.currentTip.bind(bindEl);
29679         this.currentRegion = Roo.lib.Region.getRegion(dom);
29680         this.currentTip.enter();
29681         
29682     },
29683     leave : function(ev)
29684     {
29685         var dom = ev.getTarget();
29686         //Roo.log(['leave',dom]);
29687         if (!this.currentEl) {
29688             return;
29689         }
29690         
29691         
29692         if (dom != this.currentEl.dom) {
29693             return;
29694         }
29695         var xy = ev.getXY();
29696         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29697             return;
29698         }
29699         // only activate leave if mouse cursor is outside... bounding box..
29700         
29701         
29702         
29703         
29704         if (this.currentTip) {
29705             this.currentTip.leave();
29706         }
29707         //Roo.log('clear currentEl');
29708         this.currentEl = false;
29709         
29710         
29711     },
29712     alignment : {
29713         'left' : ['r-l', [-2,0], 'right'],
29714         'right' : ['l-r', [2,0], 'left'],
29715         'bottom' : ['t-b', [0,2], 'top'],
29716         'top' : [ 'b-t', [0,-2], 'bottom']
29717     }
29718     
29719 });
29720
29721
29722 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29723     
29724     
29725     bindEl : false,
29726     
29727     delay : null, // can be { show : 300 , hide: 500}
29728     
29729     timeout : null,
29730     
29731     hoverState : null, //???
29732     
29733     placement : 'bottom', 
29734     
29735     alignment : false,
29736     
29737     getAutoCreate : function(){
29738     
29739         var cfg = {
29740            cls : 'tooltip',   
29741            role : 'tooltip',
29742            cn : [
29743                 {
29744                     cls : 'tooltip-arrow arrow'
29745                 },
29746                 {
29747                     cls : 'tooltip-inner'
29748                 }
29749            ]
29750         };
29751         
29752         return cfg;
29753     },
29754     bind : function(el)
29755     {
29756         this.bindEl = el;
29757     },
29758     
29759     initEvents : function()
29760     {
29761         this.arrowEl = this.el.select('.arrow', true).first();
29762         this.innerEl = this.el.select('.tooltip-inner', true).first();
29763     },
29764     
29765     enter : function () {
29766        
29767         if (this.timeout != null) {
29768             clearTimeout(this.timeout);
29769         }
29770         
29771         this.hoverState = 'in';
29772          //Roo.log("enter - show");
29773         if (!this.delay || !this.delay.show) {
29774             this.show();
29775             return;
29776         }
29777         var _t = this;
29778         this.timeout = setTimeout(function () {
29779             if (_t.hoverState == 'in') {
29780                 _t.show();
29781             }
29782         }, this.delay.show);
29783     },
29784     leave : function()
29785     {
29786         clearTimeout(this.timeout);
29787     
29788         this.hoverState = 'out';
29789          if (!this.delay || !this.delay.hide) {
29790             this.hide();
29791             return;
29792         }
29793        
29794         var _t = this;
29795         this.timeout = setTimeout(function () {
29796             //Roo.log("leave - timeout");
29797             
29798             if (_t.hoverState == 'out') {
29799                 _t.hide();
29800                 Roo.bootstrap.Tooltip.currentEl = false;
29801             }
29802         }, delay);
29803     },
29804     
29805     show : function (msg)
29806     {
29807         if (!this.el) {
29808             this.render(document.body);
29809         }
29810         // set content.
29811         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29812         
29813         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29814         
29815         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29816         
29817         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29818                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29819         
29820         var placement = typeof this.placement == 'function' ?
29821             this.placement.call(this, this.el, on_el) :
29822             this.placement;
29823             
29824         var autoToken = /\s?auto?\s?/i;
29825         var autoPlace = autoToken.test(placement);
29826         if (autoPlace) {
29827             placement = placement.replace(autoToken, '') || 'top';
29828         }
29829         
29830         //this.el.detach()
29831         //this.el.setXY([0,0]);
29832         this.el.show();
29833         //this.el.dom.style.display='block';
29834         
29835         //this.el.appendTo(on_el);
29836         
29837         var p = this.getPosition();
29838         var box = this.el.getBox();
29839         
29840         if (autoPlace) {
29841             // fixme..
29842         }
29843         
29844         var align = this.alignment[placement];
29845         
29846         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29847         
29848         if(placement == 'top' || placement == 'bottom'){
29849             if(xy[0] < 0){
29850                 placement = 'right';
29851             }
29852             
29853             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29854                 placement = 'left';
29855             }
29856             
29857             var scroll = Roo.select('body', true).first().getScroll();
29858             
29859             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29860                 placement = 'top';
29861             }
29862             
29863             align = this.alignment[placement];
29864             
29865             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29866             
29867         }
29868         
29869         var elems = document.getElementsByTagName('div');
29870         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29871         for (var i = 0; i < elems.length; i++) {
29872           var zindex = Number.parseInt(
29873                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29874                 10
29875           );
29876           if (zindex > highest) {
29877             highest = zindex;
29878           }
29879         }
29880         
29881         
29882         
29883         this.el.dom.style.zIndex = highest;
29884         
29885         this.el.alignTo(this.bindEl, align[0],align[1]);
29886         //var arrow = this.el.select('.arrow',true).first();
29887         //arrow.set(align[2], 
29888         
29889         this.el.addClass(placement);
29890         this.el.addClass("bs-tooltip-"+ placement);
29891         
29892         this.el.addClass('in fade show');
29893         
29894         this.hoverState = null;
29895         
29896         if (this.el.hasClass('fade')) {
29897             // fade it?
29898         }
29899         
29900         
29901         
29902         
29903         
29904     },
29905     hide : function()
29906     {
29907          
29908         if (!this.el) {
29909             return;
29910         }
29911         //this.el.setXY([0,0]);
29912         this.el.removeClass(['show', 'in']);
29913         //this.el.hide();
29914         
29915     }
29916     
29917 });
29918  
29919
29920  /*
29921  * - LGPL
29922  *
29923  * Location Picker
29924  * 
29925  */
29926
29927 /**
29928  * @class Roo.bootstrap.LocationPicker
29929  * @extends Roo.bootstrap.Component
29930  * Bootstrap LocationPicker class
29931  * @cfg {Number} latitude Position when init default 0
29932  * @cfg {Number} longitude Position when init default 0
29933  * @cfg {Number} zoom default 15
29934  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29935  * @cfg {Boolean} mapTypeControl default false
29936  * @cfg {Boolean} disableDoubleClickZoom default false
29937  * @cfg {Boolean} scrollwheel default true
29938  * @cfg {Boolean} streetViewControl default false
29939  * @cfg {Number} radius default 0
29940  * @cfg {String} locationName
29941  * @cfg {Boolean} draggable default true
29942  * @cfg {Boolean} enableAutocomplete default false
29943  * @cfg {Boolean} enableReverseGeocode default true
29944  * @cfg {String} markerTitle
29945  * 
29946  * @constructor
29947  * Create a new LocationPicker
29948  * @param {Object} config The config object
29949  */
29950
29951
29952 Roo.bootstrap.LocationPicker = function(config){
29953     
29954     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29955     
29956     this.addEvents({
29957         /**
29958          * @event initial
29959          * Fires when the picker initialized.
29960          * @param {Roo.bootstrap.LocationPicker} this
29961          * @param {Google Location} location
29962          */
29963         initial : true,
29964         /**
29965          * @event positionchanged
29966          * Fires when the picker position changed.
29967          * @param {Roo.bootstrap.LocationPicker} this
29968          * @param {Google Location} location
29969          */
29970         positionchanged : true,
29971         /**
29972          * @event resize
29973          * Fires when the map resize.
29974          * @param {Roo.bootstrap.LocationPicker} this
29975          */
29976         resize : true,
29977         /**
29978          * @event show
29979          * Fires when the map show.
29980          * @param {Roo.bootstrap.LocationPicker} this
29981          */
29982         show : true,
29983         /**
29984          * @event hide
29985          * Fires when the map hide.
29986          * @param {Roo.bootstrap.LocationPicker} this
29987          */
29988         hide : true,
29989         /**
29990          * @event mapClick
29991          * Fires when click the map.
29992          * @param {Roo.bootstrap.LocationPicker} this
29993          * @param {Map event} e
29994          */
29995         mapClick : true,
29996         /**
29997          * @event mapRightClick
29998          * Fires when right click the map.
29999          * @param {Roo.bootstrap.LocationPicker} this
30000          * @param {Map event} e
30001          */
30002         mapRightClick : true,
30003         /**
30004          * @event markerClick
30005          * Fires when click the marker.
30006          * @param {Roo.bootstrap.LocationPicker} this
30007          * @param {Map event} e
30008          */
30009         markerClick : true,
30010         /**
30011          * @event markerRightClick
30012          * Fires when right click the marker.
30013          * @param {Roo.bootstrap.LocationPicker} this
30014          * @param {Map event} e
30015          */
30016         markerRightClick : true,
30017         /**
30018          * @event OverlayViewDraw
30019          * Fires when OverlayView Draw
30020          * @param {Roo.bootstrap.LocationPicker} this
30021          */
30022         OverlayViewDraw : true,
30023         /**
30024          * @event OverlayViewOnAdd
30025          * Fires when OverlayView Draw
30026          * @param {Roo.bootstrap.LocationPicker} this
30027          */
30028         OverlayViewOnAdd : true,
30029         /**
30030          * @event OverlayViewOnRemove
30031          * Fires when OverlayView Draw
30032          * @param {Roo.bootstrap.LocationPicker} this
30033          */
30034         OverlayViewOnRemove : true,
30035         /**
30036          * @event OverlayViewShow
30037          * Fires when OverlayView Draw
30038          * @param {Roo.bootstrap.LocationPicker} this
30039          * @param {Pixel} cpx
30040          */
30041         OverlayViewShow : true,
30042         /**
30043          * @event OverlayViewHide
30044          * Fires when OverlayView Draw
30045          * @param {Roo.bootstrap.LocationPicker} this
30046          */
30047         OverlayViewHide : true,
30048         /**
30049          * @event loadexception
30050          * Fires when load google lib failed.
30051          * @param {Roo.bootstrap.LocationPicker} this
30052          */
30053         loadexception : true
30054     });
30055         
30056 };
30057
30058 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30059     
30060     gMapContext: false,
30061     
30062     latitude: 0,
30063     longitude: 0,
30064     zoom: 15,
30065     mapTypeId: false,
30066     mapTypeControl: false,
30067     disableDoubleClickZoom: false,
30068     scrollwheel: true,
30069     streetViewControl: false,
30070     radius: 0,
30071     locationName: '',
30072     draggable: true,
30073     enableAutocomplete: false,
30074     enableReverseGeocode: true,
30075     markerTitle: '',
30076     
30077     getAutoCreate: function()
30078     {
30079
30080         var cfg = {
30081             tag: 'div',
30082             cls: 'roo-location-picker'
30083         };
30084         
30085         return cfg
30086     },
30087     
30088     initEvents: function(ct, position)
30089     {       
30090         if(!this.el.getWidth() || this.isApplied()){
30091             return;
30092         }
30093         
30094         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30095         
30096         this.initial();
30097     },
30098     
30099     initial: function()
30100     {
30101         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30102             this.fireEvent('loadexception', this);
30103             return;
30104         }
30105         
30106         if(!this.mapTypeId){
30107             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30108         }
30109         
30110         this.gMapContext = this.GMapContext();
30111         
30112         this.initOverlayView();
30113         
30114         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30115         
30116         var _this = this;
30117                 
30118         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30119             _this.setPosition(_this.gMapContext.marker.position);
30120         });
30121         
30122         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30123             _this.fireEvent('mapClick', this, event);
30124             
30125         });
30126
30127         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30128             _this.fireEvent('mapRightClick', this, event);
30129             
30130         });
30131         
30132         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30133             _this.fireEvent('markerClick', this, event);
30134             
30135         });
30136
30137         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30138             _this.fireEvent('markerRightClick', this, event);
30139             
30140         });
30141         
30142         this.setPosition(this.gMapContext.location);
30143         
30144         this.fireEvent('initial', this, this.gMapContext.location);
30145     },
30146     
30147     initOverlayView: function()
30148     {
30149         var _this = this;
30150         
30151         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30152             
30153             draw: function()
30154             {
30155                 _this.fireEvent('OverlayViewDraw', _this);
30156             },
30157             
30158             onAdd: function()
30159             {
30160                 _this.fireEvent('OverlayViewOnAdd', _this);
30161             },
30162             
30163             onRemove: function()
30164             {
30165                 _this.fireEvent('OverlayViewOnRemove', _this);
30166             },
30167             
30168             show: function(cpx)
30169             {
30170                 _this.fireEvent('OverlayViewShow', _this, cpx);
30171             },
30172             
30173             hide: function()
30174             {
30175                 _this.fireEvent('OverlayViewHide', _this);
30176             }
30177             
30178         });
30179     },
30180     
30181     fromLatLngToContainerPixel: function(event)
30182     {
30183         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30184     },
30185     
30186     isApplied: function() 
30187     {
30188         return this.getGmapContext() == false ? false : true;
30189     },
30190     
30191     getGmapContext: function() 
30192     {
30193         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30194     },
30195     
30196     GMapContext: function() 
30197     {
30198         var position = new google.maps.LatLng(this.latitude, this.longitude);
30199         
30200         var _map = new google.maps.Map(this.el.dom, {
30201             center: position,
30202             zoom: this.zoom,
30203             mapTypeId: this.mapTypeId,
30204             mapTypeControl: this.mapTypeControl,
30205             disableDoubleClickZoom: this.disableDoubleClickZoom,
30206             scrollwheel: this.scrollwheel,
30207             streetViewControl: this.streetViewControl,
30208             locationName: this.locationName,
30209             draggable: this.draggable,
30210             enableAutocomplete: this.enableAutocomplete,
30211             enableReverseGeocode: this.enableReverseGeocode
30212         });
30213         
30214         var _marker = new google.maps.Marker({
30215             position: position,
30216             map: _map,
30217             title: this.markerTitle,
30218             draggable: this.draggable
30219         });
30220         
30221         return {
30222             map: _map,
30223             marker: _marker,
30224             circle: null,
30225             location: position,
30226             radius: this.radius,
30227             locationName: this.locationName,
30228             addressComponents: {
30229                 formatted_address: null,
30230                 addressLine1: null,
30231                 addressLine2: null,
30232                 streetName: null,
30233                 streetNumber: null,
30234                 city: null,
30235                 district: null,
30236                 state: null,
30237                 stateOrProvince: null
30238             },
30239             settings: this,
30240             domContainer: this.el.dom,
30241             geodecoder: new google.maps.Geocoder()
30242         };
30243     },
30244     
30245     drawCircle: function(center, radius, options) 
30246     {
30247         if (this.gMapContext.circle != null) {
30248             this.gMapContext.circle.setMap(null);
30249         }
30250         if (radius > 0) {
30251             radius *= 1;
30252             options = Roo.apply({}, options, {
30253                 strokeColor: "#0000FF",
30254                 strokeOpacity: .35,
30255                 strokeWeight: 2,
30256                 fillColor: "#0000FF",
30257                 fillOpacity: .2
30258             });
30259             
30260             options.map = this.gMapContext.map;
30261             options.radius = radius;
30262             options.center = center;
30263             this.gMapContext.circle = new google.maps.Circle(options);
30264             return this.gMapContext.circle;
30265         }
30266         
30267         return null;
30268     },
30269     
30270     setPosition: function(location) 
30271     {
30272         this.gMapContext.location = location;
30273         this.gMapContext.marker.setPosition(location);
30274         this.gMapContext.map.panTo(location);
30275         this.drawCircle(location, this.gMapContext.radius, {});
30276         
30277         var _this = this;
30278         
30279         if (this.gMapContext.settings.enableReverseGeocode) {
30280             this.gMapContext.geodecoder.geocode({
30281                 latLng: this.gMapContext.location
30282             }, function(results, status) {
30283                 
30284                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30285                     _this.gMapContext.locationName = results[0].formatted_address;
30286                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30287                     
30288                     _this.fireEvent('positionchanged', this, location);
30289                 }
30290             });
30291             
30292             return;
30293         }
30294         
30295         this.fireEvent('positionchanged', this, location);
30296     },
30297     
30298     resize: function()
30299     {
30300         google.maps.event.trigger(this.gMapContext.map, "resize");
30301         
30302         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30303         
30304         this.fireEvent('resize', this);
30305     },
30306     
30307     setPositionByLatLng: function(latitude, longitude)
30308     {
30309         this.setPosition(new google.maps.LatLng(latitude, longitude));
30310     },
30311     
30312     getCurrentPosition: function() 
30313     {
30314         return {
30315             latitude: this.gMapContext.location.lat(),
30316             longitude: this.gMapContext.location.lng()
30317         };
30318     },
30319     
30320     getAddressName: function() 
30321     {
30322         return this.gMapContext.locationName;
30323     },
30324     
30325     getAddressComponents: function() 
30326     {
30327         return this.gMapContext.addressComponents;
30328     },
30329     
30330     address_component_from_google_geocode: function(address_components) 
30331     {
30332         var result = {};
30333         
30334         for (var i = 0; i < address_components.length; i++) {
30335             var component = address_components[i];
30336             if (component.types.indexOf("postal_code") >= 0) {
30337                 result.postalCode = component.short_name;
30338             } else if (component.types.indexOf("street_number") >= 0) {
30339                 result.streetNumber = component.short_name;
30340             } else if (component.types.indexOf("route") >= 0) {
30341                 result.streetName = component.short_name;
30342             } else if (component.types.indexOf("neighborhood") >= 0) {
30343                 result.city = component.short_name;
30344             } else if (component.types.indexOf("locality") >= 0) {
30345                 result.city = component.short_name;
30346             } else if (component.types.indexOf("sublocality") >= 0) {
30347                 result.district = component.short_name;
30348             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30349                 result.stateOrProvince = component.short_name;
30350             } else if (component.types.indexOf("country") >= 0) {
30351                 result.country = component.short_name;
30352             }
30353         }
30354         
30355         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30356         result.addressLine2 = "";
30357         return result;
30358     },
30359     
30360     setZoomLevel: function(zoom)
30361     {
30362         this.gMapContext.map.setZoom(zoom);
30363     },
30364     
30365     show: function()
30366     {
30367         if(!this.el){
30368             return;
30369         }
30370         
30371         this.el.show();
30372         
30373         this.resize();
30374         
30375         this.fireEvent('show', this);
30376     },
30377     
30378     hide: function()
30379     {
30380         if(!this.el){
30381             return;
30382         }
30383         
30384         this.el.hide();
30385         
30386         this.fireEvent('hide', this);
30387     }
30388     
30389 });
30390
30391 Roo.apply(Roo.bootstrap.LocationPicker, {
30392     
30393     OverlayView : function(map, options)
30394     {
30395         options = options || {};
30396         
30397         this.setMap(map);
30398     }
30399     
30400     
30401 });/**
30402  * @class Roo.bootstrap.Alert
30403  * @extends Roo.bootstrap.Component
30404  * Bootstrap Alert class - shows an alert area box
30405  * eg
30406  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30407   Enter a valid email address
30408 </div>
30409  * @licence LGPL
30410  * @cfg {String} title The title of alert
30411  * @cfg {String} html The content of alert
30412  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30413  * @cfg {String} fa font-awesomeicon
30414  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30415  * @cfg {Boolean} close true to show a x closer
30416  * 
30417  * 
30418  * @constructor
30419  * Create a new alert
30420  * @param {Object} config The config object
30421  */
30422
30423
30424 Roo.bootstrap.Alert = function(config){
30425     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30426     
30427 };
30428
30429 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30430     
30431     title: '',
30432     html: '',
30433     weight: false,
30434     fa: false,
30435     faicon: false, // BC
30436     close : false,
30437     
30438     
30439     getAutoCreate : function()
30440     {
30441         
30442         var cfg = {
30443             tag : 'div',
30444             cls : 'alert',
30445             cn : [
30446                 {
30447                     tag: 'button',
30448                     type :  "button",
30449                     cls: "close",
30450                     html : '×',
30451                     style : this.close ? '' : 'display:none'
30452                 },
30453                 {
30454                     tag : 'i',
30455                     cls : 'roo-alert-icon'
30456                     
30457                 },
30458                 {
30459                     tag : 'b',
30460                     cls : 'roo-alert-title',
30461                     html : this.title
30462                 },
30463                 {
30464                     tag : 'span',
30465                     cls : 'roo-alert-text',
30466                     html : this.html
30467                 }
30468             ]
30469         };
30470         
30471         if(this.faicon){
30472             cfg.cn[0].cls += ' fa ' + this.faicon;
30473         }
30474         if(this.fa){
30475             cfg.cn[0].cls += ' fa ' + this.fa;
30476         }
30477         
30478         if(this.weight){
30479             cfg.cls += ' alert-' + this.weight;
30480         }
30481         
30482         return cfg;
30483     },
30484     
30485     initEvents: function() 
30486     {
30487         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30488         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30489         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30490         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30491         if (this.seconds > 0) {
30492             this.hide.defer(this.seconds, this);
30493         }
30494     },
30495     /**
30496      * Set the Title Message HTML
30497      * @param {String} html
30498      */
30499     setTitle : function(str)
30500     {
30501         this.titleEl.dom.innerHTML = str;
30502     },
30503      
30504      /**
30505      * Set the Body Message HTML
30506      * @param {String} html
30507      */
30508     setHtml : function(str)
30509     {
30510         this.htmlEl.dom.innerHTML = str;
30511     },
30512     /**
30513      * Set the Weight of the alert
30514      * @param {String} (success|info|warning|danger) weight
30515      */
30516     
30517     setWeight : function(weight)
30518     {
30519         if(this.weight){
30520             this.el.removeClass('alert-' + this.weight);
30521         }
30522         
30523         this.weight = weight;
30524         
30525         this.el.addClass('alert-' + this.weight);
30526     },
30527       /**
30528      * Set the Icon of the alert
30529      * @param {String} see fontawsome names (name without the 'fa-' bit)
30530      */
30531     setIcon : function(icon)
30532     {
30533         if(this.faicon){
30534             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30535         }
30536         
30537         this.faicon = icon;
30538         
30539         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30540     },
30541     /**
30542      * Hide the Alert
30543      */
30544     hide: function() 
30545     {
30546         this.el.hide();   
30547     },
30548     /**
30549      * Show the Alert
30550      */
30551     show: function() 
30552     {  
30553         this.el.show();   
30554     }
30555     
30556 });
30557
30558  
30559 /*
30560 * Licence: LGPL
30561 */
30562
30563 /**
30564  * @class Roo.bootstrap.UploadCropbox
30565  * @extends Roo.bootstrap.Component
30566  * Bootstrap UploadCropbox class
30567  * @cfg {String} emptyText show when image has been loaded
30568  * @cfg {String} rotateNotify show when image too small to rotate
30569  * @cfg {Number} errorTimeout default 3000
30570  * @cfg {Number} minWidth default 300
30571  * @cfg {Number} minHeight default 300
30572  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30573  * @cfg {Boolean} isDocument (true|false) default false
30574  * @cfg {String} url action url
30575  * @cfg {String} paramName default 'imageUpload'
30576  * @cfg {String} method default POST
30577  * @cfg {Boolean} loadMask (true|false) default true
30578  * @cfg {Boolean} loadingText default 'Loading...'
30579  * 
30580  * @constructor
30581  * Create a new UploadCropbox
30582  * @param {Object} config The config object
30583  */
30584
30585 Roo.bootstrap.UploadCropbox = function(config){
30586     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30587     
30588     this.addEvents({
30589         /**
30590          * @event beforeselectfile
30591          * Fire before select file
30592          * @param {Roo.bootstrap.UploadCropbox} this
30593          */
30594         "beforeselectfile" : true,
30595         /**
30596          * @event initial
30597          * Fire after initEvent
30598          * @param {Roo.bootstrap.UploadCropbox} this
30599          */
30600         "initial" : true,
30601         /**
30602          * @event crop
30603          * Fire after initEvent
30604          * @param {Roo.bootstrap.UploadCropbox} this
30605          * @param {String} data
30606          */
30607         "crop" : true,
30608         /**
30609          * @event prepare
30610          * Fire when preparing the file data
30611          * @param {Roo.bootstrap.UploadCropbox} this
30612          * @param {Object} file
30613          */
30614         "prepare" : true,
30615         /**
30616          * @event exception
30617          * Fire when get exception
30618          * @param {Roo.bootstrap.UploadCropbox} this
30619          * @param {XMLHttpRequest} xhr
30620          */
30621         "exception" : true,
30622         /**
30623          * @event beforeloadcanvas
30624          * Fire before load the canvas
30625          * @param {Roo.bootstrap.UploadCropbox} this
30626          * @param {String} src
30627          */
30628         "beforeloadcanvas" : true,
30629         /**
30630          * @event trash
30631          * Fire when trash image
30632          * @param {Roo.bootstrap.UploadCropbox} this
30633          */
30634         "trash" : true,
30635         /**
30636          * @event download
30637          * Fire when download the image
30638          * @param {Roo.bootstrap.UploadCropbox} this
30639          */
30640         "download" : true,
30641         /**
30642          * @event footerbuttonclick
30643          * Fire when footerbuttonclick
30644          * @param {Roo.bootstrap.UploadCropbox} this
30645          * @param {String} type
30646          */
30647         "footerbuttonclick" : true,
30648         /**
30649          * @event resize
30650          * Fire when resize
30651          * @param {Roo.bootstrap.UploadCropbox} this
30652          */
30653         "resize" : true,
30654         /**
30655          * @event rotate
30656          * Fire when rotate the image
30657          * @param {Roo.bootstrap.UploadCropbox} this
30658          * @param {String} pos
30659          */
30660         "rotate" : true,
30661         /**
30662          * @event inspect
30663          * Fire when inspect the file
30664          * @param {Roo.bootstrap.UploadCropbox} this
30665          * @param {Object} file
30666          */
30667         "inspect" : true,
30668         /**
30669          * @event upload
30670          * Fire when xhr upload the file
30671          * @param {Roo.bootstrap.UploadCropbox} this
30672          * @param {Object} data
30673          */
30674         "upload" : true,
30675         /**
30676          * @event arrange
30677          * Fire when arrange the file data
30678          * @param {Roo.bootstrap.UploadCropbox} this
30679          * @param {Object} formData
30680          */
30681         "arrange" : true
30682     });
30683     
30684     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30685 };
30686
30687 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30688     
30689     emptyText : 'Click to upload image',
30690     rotateNotify : 'Image is too small to rotate',
30691     errorTimeout : 3000,
30692     scale : 0,
30693     baseScale : 1,
30694     rotate : 0,
30695     dragable : false,
30696     pinching : false,
30697     mouseX : 0,
30698     mouseY : 0,
30699     cropData : false,
30700     minWidth : 300,
30701     minHeight : 300,
30702     file : false,
30703     exif : {},
30704     baseRotate : 1,
30705     cropType : 'image/jpeg',
30706     buttons : false,
30707     canvasLoaded : false,
30708     isDocument : false,
30709     method : 'POST',
30710     paramName : 'imageUpload',
30711     loadMask : true,
30712     loadingText : 'Loading...',
30713     maskEl : false,
30714     
30715     getAutoCreate : function()
30716     {
30717         var cfg = {
30718             tag : 'div',
30719             cls : 'roo-upload-cropbox',
30720             cn : [
30721                 {
30722                     tag : 'input',
30723                     cls : 'roo-upload-cropbox-selector',
30724                     type : 'file'
30725                 },
30726                 {
30727                     tag : 'div',
30728                     cls : 'roo-upload-cropbox-body',
30729                     style : 'cursor:pointer',
30730                     cn : [
30731                         {
30732                             tag : 'div',
30733                             cls : 'roo-upload-cropbox-preview'
30734                         },
30735                         {
30736                             tag : 'div',
30737                             cls : 'roo-upload-cropbox-thumb'
30738                         },
30739                         {
30740                             tag : 'div',
30741                             cls : 'roo-upload-cropbox-empty-notify',
30742                             html : this.emptyText
30743                         },
30744                         {
30745                             tag : 'div',
30746                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30747                             html : this.rotateNotify
30748                         }
30749                     ]
30750                 },
30751                 {
30752                     tag : 'div',
30753                     cls : 'roo-upload-cropbox-footer',
30754                     cn : {
30755                         tag : 'div',
30756                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30757                         cn : []
30758                     }
30759                 }
30760             ]
30761         };
30762         
30763         return cfg;
30764     },
30765     
30766     onRender : function(ct, position)
30767     {
30768         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30769         
30770         if (this.buttons.length) {
30771             
30772             Roo.each(this.buttons, function(bb) {
30773                 
30774                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30775                 
30776                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30777                 
30778             }, this);
30779         }
30780         
30781         if(this.loadMask){
30782             this.maskEl = this.el;
30783         }
30784     },
30785     
30786     initEvents : function()
30787     {
30788         this.urlAPI = (window.createObjectURL && window) || 
30789                                 (window.URL && URL.revokeObjectURL && URL) || 
30790                                 (window.webkitURL && webkitURL);
30791                         
30792         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30793         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30794         
30795         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30796         this.selectorEl.hide();
30797         
30798         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30799         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30800         
30801         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30802         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30803         this.thumbEl.hide();
30804         
30805         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30806         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30807         
30808         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30809         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30810         this.errorEl.hide();
30811         
30812         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30813         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30814         this.footerEl.hide();
30815         
30816         this.setThumbBoxSize();
30817         
30818         this.bind();
30819         
30820         this.resize();
30821         
30822         this.fireEvent('initial', this);
30823     },
30824
30825     bind : function()
30826     {
30827         var _this = this;
30828         
30829         window.addEventListener("resize", function() { _this.resize(); } );
30830         
30831         this.bodyEl.on('click', this.beforeSelectFile, this);
30832         
30833         if(Roo.isTouch){
30834             this.bodyEl.on('touchstart', this.onTouchStart, this);
30835             this.bodyEl.on('touchmove', this.onTouchMove, this);
30836             this.bodyEl.on('touchend', this.onTouchEnd, this);
30837         }
30838         
30839         if(!Roo.isTouch){
30840             this.bodyEl.on('mousedown', this.onMouseDown, this);
30841             this.bodyEl.on('mousemove', this.onMouseMove, this);
30842             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30843             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30844             Roo.get(document).on('mouseup', this.onMouseUp, this);
30845         }
30846         
30847         this.selectorEl.on('change', this.onFileSelected, this);
30848     },
30849     
30850     reset : function()
30851     {    
30852         this.scale = 0;
30853         this.baseScale = 1;
30854         this.rotate = 0;
30855         this.baseRotate = 1;
30856         this.dragable = false;
30857         this.pinching = false;
30858         this.mouseX = 0;
30859         this.mouseY = 0;
30860         this.cropData = false;
30861         this.notifyEl.dom.innerHTML = this.emptyText;
30862         
30863         this.selectorEl.dom.value = '';
30864         
30865     },
30866     
30867     resize : function()
30868     {
30869         if(this.fireEvent('resize', this) != false){
30870             this.setThumbBoxPosition();
30871             this.setCanvasPosition();
30872         }
30873     },
30874     
30875     onFooterButtonClick : function(e, el, o, type)
30876     {
30877         switch (type) {
30878             case 'rotate-left' :
30879                 this.onRotateLeft(e);
30880                 break;
30881             case 'rotate-right' :
30882                 this.onRotateRight(e);
30883                 break;
30884             case 'picture' :
30885                 this.beforeSelectFile(e);
30886                 break;
30887             case 'trash' :
30888                 this.trash(e);
30889                 break;
30890             case 'crop' :
30891                 this.crop(e);
30892                 break;
30893             case 'download' :
30894                 this.download(e);
30895                 break;
30896             default :
30897                 break;
30898         }
30899         
30900         this.fireEvent('footerbuttonclick', this, type);
30901     },
30902     
30903     beforeSelectFile : function(e)
30904     {
30905         e.preventDefault();
30906         
30907         if(this.fireEvent('beforeselectfile', this) != false){
30908             this.selectorEl.dom.click();
30909         }
30910     },
30911     
30912     onFileSelected : function(e)
30913     {
30914         e.preventDefault();
30915         
30916         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30917             return;
30918         }
30919         
30920         var file = this.selectorEl.dom.files[0];
30921         
30922         if(this.fireEvent('inspect', this, file) != false){
30923             this.prepare(file);
30924         }
30925         
30926     },
30927     
30928     trash : function(e)
30929     {
30930         this.fireEvent('trash', this);
30931     },
30932     
30933     download : function(e)
30934     {
30935         this.fireEvent('download', this);
30936     },
30937     
30938     loadCanvas : function(src)
30939     {   
30940         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30941             
30942             this.reset();
30943             
30944             this.imageEl = document.createElement('img');
30945             
30946             var _this = this;
30947             
30948             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30949             
30950             this.imageEl.src = src;
30951         }
30952     },
30953     
30954     onLoadCanvas : function()
30955     {   
30956         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30957         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30958         
30959         this.bodyEl.un('click', this.beforeSelectFile, this);
30960         
30961         this.notifyEl.hide();
30962         this.thumbEl.show();
30963         this.footerEl.show();
30964         
30965         this.baseRotateLevel();
30966         
30967         if(this.isDocument){
30968             this.setThumbBoxSize();
30969         }
30970         
30971         this.setThumbBoxPosition();
30972         
30973         this.baseScaleLevel();
30974         
30975         this.draw();
30976         
30977         this.resize();
30978         
30979         this.canvasLoaded = true;
30980         
30981         if(this.loadMask){
30982             this.maskEl.unmask();
30983         }
30984         
30985     },
30986     
30987     setCanvasPosition : function()
30988     {   
30989         if(!this.canvasEl){
30990             return;
30991         }
30992         
30993         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30994         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30995         
30996         this.previewEl.setLeft(pw);
30997         this.previewEl.setTop(ph);
30998         
30999     },
31000     
31001     onMouseDown : function(e)
31002     {   
31003         e.stopEvent();
31004         
31005         this.dragable = true;
31006         this.pinching = false;
31007         
31008         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31009             this.dragable = false;
31010             return;
31011         }
31012         
31013         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31014         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31015         
31016     },
31017     
31018     onMouseMove : function(e)
31019     {   
31020         e.stopEvent();
31021         
31022         if(!this.canvasLoaded){
31023             return;
31024         }
31025         
31026         if (!this.dragable){
31027             return;
31028         }
31029         
31030         var minX = Math.ceil(this.thumbEl.getLeft(true));
31031         var minY = Math.ceil(this.thumbEl.getTop(true));
31032         
31033         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31034         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31035         
31036         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31037         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31038         
31039         x = x - this.mouseX;
31040         y = y - this.mouseY;
31041         
31042         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31043         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31044         
31045         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31046         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31047         
31048         this.previewEl.setLeft(bgX);
31049         this.previewEl.setTop(bgY);
31050         
31051         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31052         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31053     },
31054     
31055     onMouseUp : function(e)
31056     {   
31057         e.stopEvent();
31058         
31059         this.dragable = false;
31060     },
31061     
31062     onMouseWheel : function(e)
31063     {   
31064         e.stopEvent();
31065         
31066         this.startScale = this.scale;
31067         
31068         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31069         
31070         if(!this.zoomable()){
31071             this.scale = this.startScale;
31072             return;
31073         }
31074         
31075         this.draw();
31076         
31077         return;
31078     },
31079     
31080     zoomable : function()
31081     {
31082         var minScale = this.thumbEl.getWidth() / this.minWidth;
31083         
31084         if(this.minWidth < this.minHeight){
31085             minScale = this.thumbEl.getHeight() / this.minHeight;
31086         }
31087         
31088         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31089         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31090         
31091         if(
31092                 this.isDocument &&
31093                 (this.rotate == 0 || this.rotate == 180) && 
31094                 (
31095                     width > this.imageEl.OriginWidth || 
31096                     height > this.imageEl.OriginHeight ||
31097                     (width < this.minWidth && height < this.minHeight)
31098                 )
31099         ){
31100             return false;
31101         }
31102         
31103         if(
31104                 this.isDocument &&
31105                 (this.rotate == 90 || this.rotate == 270) && 
31106                 (
31107                     width > this.imageEl.OriginWidth || 
31108                     height > this.imageEl.OriginHeight ||
31109                     (width < this.minHeight && height < this.minWidth)
31110                 )
31111         ){
31112             return false;
31113         }
31114         
31115         if(
31116                 !this.isDocument &&
31117                 (this.rotate == 0 || this.rotate == 180) && 
31118                 (
31119                     width < this.minWidth || 
31120                     width > this.imageEl.OriginWidth || 
31121                     height < this.minHeight || 
31122                     height > this.imageEl.OriginHeight
31123                 )
31124         ){
31125             return false;
31126         }
31127         
31128         if(
31129                 !this.isDocument &&
31130                 (this.rotate == 90 || this.rotate == 270) && 
31131                 (
31132                     width < this.minHeight || 
31133                     width > this.imageEl.OriginWidth || 
31134                     height < this.minWidth || 
31135                     height > this.imageEl.OriginHeight
31136                 )
31137         ){
31138             return false;
31139         }
31140         
31141         return true;
31142         
31143     },
31144     
31145     onRotateLeft : function(e)
31146     {   
31147         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31148             
31149             var minScale = this.thumbEl.getWidth() / this.minWidth;
31150             
31151             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31152             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31153             
31154             this.startScale = this.scale;
31155             
31156             while (this.getScaleLevel() < minScale){
31157             
31158                 this.scale = this.scale + 1;
31159                 
31160                 if(!this.zoomable()){
31161                     break;
31162                 }
31163                 
31164                 if(
31165                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31166                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31167                 ){
31168                     continue;
31169                 }
31170                 
31171                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31172
31173                 this.draw();
31174                 
31175                 return;
31176             }
31177             
31178             this.scale = this.startScale;
31179             
31180             this.onRotateFail();
31181             
31182             return false;
31183         }
31184         
31185         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31186
31187         if(this.isDocument){
31188             this.setThumbBoxSize();
31189             this.setThumbBoxPosition();
31190             this.setCanvasPosition();
31191         }
31192         
31193         this.draw();
31194         
31195         this.fireEvent('rotate', this, 'left');
31196         
31197     },
31198     
31199     onRotateRight : function(e)
31200     {
31201         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31202             
31203             var minScale = this.thumbEl.getWidth() / this.minWidth;
31204         
31205             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31206             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31207             
31208             this.startScale = this.scale;
31209             
31210             while (this.getScaleLevel() < minScale){
31211             
31212                 this.scale = this.scale + 1;
31213                 
31214                 if(!this.zoomable()){
31215                     break;
31216                 }
31217                 
31218                 if(
31219                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31220                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31221                 ){
31222                     continue;
31223                 }
31224                 
31225                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31226
31227                 this.draw();
31228                 
31229                 return;
31230             }
31231             
31232             this.scale = this.startScale;
31233             
31234             this.onRotateFail();
31235             
31236             return false;
31237         }
31238         
31239         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31240
31241         if(this.isDocument){
31242             this.setThumbBoxSize();
31243             this.setThumbBoxPosition();
31244             this.setCanvasPosition();
31245         }
31246         
31247         this.draw();
31248         
31249         this.fireEvent('rotate', this, 'right');
31250     },
31251     
31252     onRotateFail : function()
31253     {
31254         this.errorEl.show(true);
31255         
31256         var _this = this;
31257         
31258         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31259     },
31260     
31261     draw : function()
31262     {
31263         this.previewEl.dom.innerHTML = '';
31264         
31265         var canvasEl = document.createElement("canvas");
31266         
31267         var contextEl = canvasEl.getContext("2d");
31268         
31269         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31270         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31271         var center = this.imageEl.OriginWidth / 2;
31272         
31273         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31274             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31275             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31276             center = this.imageEl.OriginHeight / 2;
31277         }
31278         
31279         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31280         
31281         contextEl.translate(center, center);
31282         contextEl.rotate(this.rotate * Math.PI / 180);
31283
31284         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31285         
31286         this.canvasEl = document.createElement("canvas");
31287         
31288         this.contextEl = this.canvasEl.getContext("2d");
31289         
31290         switch (this.rotate) {
31291             case 0 :
31292                 
31293                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31294                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31295                 
31296                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31297                 
31298                 break;
31299             case 90 : 
31300                 
31301                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31302                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31303                 
31304                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31305                     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);
31306                     break;
31307                 }
31308                 
31309                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31310                 
31311                 break;
31312             case 180 :
31313                 
31314                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31315                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31316                 
31317                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31318                     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);
31319                     break;
31320                 }
31321                 
31322                 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);
31323                 
31324                 break;
31325             case 270 :
31326                 
31327                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31328                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31329         
31330                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31331                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31332                     break;
31333                 }
31334                 
31335                 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);
31336                 
31337                 break;
31338             default : 
31339                 break;
31340         }
31341         
31342         this.previewEl.appendChild(this.canvasEl);
31343         
31344         this.setCanvasPosition();
31345     },
31346     
31347     crop : function()
31348     {
31349         if(!this.canvasLoaded){
31350             return;
31351         }
31352         
31353         var imageCanvas = document.createElement("canvas");
31354         
31355         var imageContext = imageCanvas.getContext("2d");
31356         
31357         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31358         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31359         
31360         var center = imageCanvas.width / 2;
31361         
31362         imageContext.translate(center, center);
31363         
31364         imageContext.rotate(this.rotate * Math.PI / 180);
31365         
31366         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31367         
31368         var canvas = document.createElement("canvas");
31369         
31370         var context = canvas.getContext("2d");
31371                 
31372         canvas.width = this.minWidth;
31373         canvas.height = this.minHeight;
31374
31375         switch (this.rotate) {
31376             case 0 :
31377                 
31378                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31379                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31380                 
31381                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31382                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31383                 
31384                 var targetWidth = this.minWidth - 2 * x;
31385                 var targetHeight = this.minHeight - 2 * y;
31386                 
31387                 var scale = 1;
31388                 
31389                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31390                     scale = targetWidth / width;
31391                 }
31392                 
31393                 if(x > 0 && y == 0){
31394                     scale = targetHeight / height;
31395                 }
31396                 
31397                 if(x > 0 && y > 0){
31398                     scale = targetWidth / width;
31399                     
31400                     if(width < height){
31401                         scale = targetHeight / height;
31402                     }
31403                 }
31404                 
31405                 context.scale(scale, scale);
31406                 
31407                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31408                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31409
31410                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31411                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31412
31413                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31414                 
31415                 break;
31416             case 90 : 
31417                 
31418                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31419                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31420                 
31421                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31422                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31423                 
31424                 var targetWidth = this.minWidth - 2 * x;
31425                 var targetHeight = this.minHeight - 2 * y;
31426                 
31427                 var scale = 1;
31428                 
31429                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31430                     scale = targetWidth / width;
31431                 }
31432                 
31433                 if(x > 0 && y == 0){
31434                     scale = targetHeight / height;
31435                 }
31436                 
31437                 if(x > 0 && y > 0){
31438                     scale = targetWidth / width;
31439                     
31440                     if(width < height){
31441                         scale = targetHeight / height;
31442                     }
31443                 }
31444                 
31445                 context.scale(scale, scale);
31446                 
31447                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31448                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31449
31450                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31451                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31452                 
31453                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31454                 
31455                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31456                 
31457                 break;
31458             case 180 :
31459                 
31460                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31461                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31462                 
31463                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31464                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31465                 
31466                 var targetWidth = this.minWidth - 2 * x;
31467                 var targetHeight = this.minHeight - 2 * y;
31468                 
31469                 var scale = 1;
31470                 
31471                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31472                     scale = targetWidth / width;
31473                 }
31474                 
31475                 if(x > 0 && y == 0){
31476                     scale = targetHeight / height;
31477                 }
31478                 
31479                 if(x > 0 && y > 0){
31480                     scale = targetWidth / width;
31481                     
31482                     if(width < height){
31483                         scale = targetHeight / height;
31484                     }
31485                 }
31486                 
31487                 context.scale(scale, scale);
31488                 
31489                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31490                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31491
31492                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31493                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31494
31495                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31496                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31497                 
31498                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31499                 
31500                 break;
31501             case 270 :
31502                 
31503                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31504                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31505                 
31506                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31507                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31508                 
31509                 var targetWidth = this.minWidth - 2 * x;
31510                 var targetHeight = this.minHeight - 2 * y;
31511                 
31512                 var scale = 1;
31513                 
31514                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31515                     scale = targetWidth / width;
31516                 }
31517                 
31518                 if(x > 0 && y == 0){
31519                     scale = targetHeight / height;
31520                 }
31521                 
31522                 if(x > 0 && y > 0){
31523                     scale = targetWidth / width;
31524                     
31525                     if(width < height){
31526                         scale = targetHeight / height;
31527                     }
31528                 }
31529                 
31530                 context.scale(scale, scale);
31531                 
31532                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31533                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31534
31535                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31536                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31537                 
31538                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31539                 
31540                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31541                 
31542                 break;
31543             default : 
31544                 break;
31545         }
31546         
31547         this.cropData = canvas.toDataURL(this.cropType);
31548         
31549         if(this.fireEvent('crop', this, this.cropData) !== false){
31550             this.process(this.file, this.cropData);
31551         }
31552         
31553         return;
31554         
31555     },
31556     
31557     setThumbBoxSize : function()
31558     {
31559         var width, height;
31560         
31561         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31562             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31563             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31564             
31565             this.minWidth = width;
31566             this.minHeight = height;
31567             
31568             if(this.rotate == 90 || this.rotate == 270){
31569                 this.minWidth = height;
31570                 this.minHeight = width;
31571             }
31572         }
31573         
31574         height = 300;
31575         width = Math.ceil(this.minWidth * height / this.minHeight);
31576         
31577         if(this.minWidth > this.minHeight){
31578             width = 300;
31579             height = Math.ceil(this.minHeight * width / this.minWidth);
31580         }
31581         
31582         this.thumbEl.setStyle({
31583             width : width + 'px',
31584             height : height + 'px'
31585         });
31586
31587         return;
31588             
31589     },
31590     
31591     setThumbBoxPosition : function()
31592     {
31593         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31594         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31595         
31596         this.thumbEl.setLeft(x);
31597         this.thumbEl.setTop(y);
31598         
31599     },
31600     
31601     baseRotateLevel : function()
31602     {
31603         this.baseRotate = 1;
31604         
31605         if(
31606                 typeof(this.exif) != 'undefined' &&
31607                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31608                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31609         ){
31610             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31611         }
31612         
31613         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31614         
31615     },
31616     
31617     baseScaleLevel : function()
31618     {
31619         var width, height;
31620         
31621         if(this.isDocument){
31622             
31623             if(this.baseRotate == 6 || this.baseRotate == 8){
31624             
31625                 height = this.thumbEl.getHeight();
31626                 this.baseScale = height / this.imageEl.OriginWidth;
31627
31628                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31629                     width = this.thumbEl.getWidth();
31630                     this.baseScale = width / this.imageEl.OriginHeight;
31631                 }
31632
31633                 return;
31634             }
31635
31636             height = this.thumbEl.getHeight();
31637             this.baseScale = height / this.imageEl.OriginHeight;
31638
31639             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31640                 width = this.thumbEl.getWidth();
31641                 this.baseScale = width / this.imageEl.OriginWidth;
31642             }
31643
31644             return;
31645         }
31646         
31647         if(this.baseRotate == 6 || this.baseRotate == 8){
31648             
31649             width = this.thumbEl.getHeight();
31650             this.baseScale = width / this.imageEl.OriginHeight;
31651             
31652             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31653                 height = this.thumbEl.getWidth();
31654                 this.baseScale = height / this.imageEl.OriginHeight;
31655             }
31656             
31657             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31658                 height = this.thumbEl.getWidth();
31659                 this.baseScale = height / this.imageEl.OriginHeight;
31660                 
31661                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31662                     width = this.thumbEl.getHeight();
31663                     this.baseScale = width / this.imageEl.OriginWidth;
31664                 }
31665             }
31666             
31667             return;
31668         }
31669         
31670         width = this.thumbEl.getWidth();
31671         this.baseScale = width / this.imageEl.OriginWidth;
31672         
31673         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31674             height = this.thumbEl.getHeight();
31675             this.baseScale = height / this.imageEl.OriginHeight;
31676         }
31677         
31678         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31679             
31680             height = this.thumbEl.getHeight();
31681             this.baseScale = height / this.imageEl.OriginHeight;
31682             
31683             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31684                 width = this.thumbEl.getWidth();
31685                 this.baseScale = width / this.imageEl.OriginWidth;
31686             }
31687             
31688         }
31689         
31690         return;
31691     },
31692     
31693     getScaleLevel : function()
31694     {
31695         return this.baseScale * Math.pow(1.1, this.scale);
31696     },
31697     
31698     onTouchStart : function(e)
31699     {
31700         if(!this.canvasLoaded){
31701             this.beforeSelectFile(e);
31702             return;
31703         }
31704         
31705         var touches = e.browserEvent.touches;
31706         
31707         if(!touches){
31708             return;
31709         }
31710         
31711         if(touches.length == 1){
31712             this.onMouseDown(e);
31713             return;
31714         }
31715         
31716         if(touches.length != 2){
31717             return;
31718         }
31719         
31720         var coords = [];
31721         
31722         for(var i = 0, finger; finger = touches[i]; i++){
31723             coords.push(finger.pageX, finger.pageY);
31724         }
31725         
31726         var x = Math.pow(coords[0] - coords[2], 2);
31727         var y = Math.pow(coords[1] - coords[3], 2);
31728         
31729         this.startDistance = Math.sqrt(x + y);
31730         
31731         this.startScale = this.scale;
31732         
31733         this.pinching = true;
31734         this.dragable = false;
31735         
31736     },
31737     
31738     onTouchMove : function(e)
31739     {
31740         if(!this.pinching && !this.dragable){
31741             return;
31742         }
31743         
31744         var touches = e.browserEvent.touches;
31745         
31746         if(!touches){
31747             return;
31748         }
31749         
31750         if(this.dragable){
31751             this.onMouseMove(e);
31752             return;
31753         }
31754         
31755         var coords = [];
31756         
31757         for(var i = 0, finger; finger = touches[i]; i++){
31758             coords.push(finger.pageX, finger.pageY);
31759         }
31760         
31761         var x = Math.pow(coords[0] - coords[2], 2);
31762         var y = Math.pow(coords[1] - coords[3], 2);
31763         
31764         this.endDistance = Math.sqrt(x + y);
31765         
31766         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31767         
31768         if(!this.zoomable()){
31769             this.scale = this.startScale;
31770             return;
31771         }
31772         
31773         this.draw();
31774         
31775     },
31776     
31777     onTouchEnd : function(e)
31778     {
31779         this.pinching = false;
31780         this.dragable = false;
31781         
31782     },
31783     
31784     process : function(file, crop)
31785     {
31786         if(this.loadMask){
31787             this.maskEl.mask(this.loadingText);
31788         }
31789         
31790         this.xhr = new XMLHttpRequest();
31791         
31792         file.xhr = this.xhr;
31793
31794         this.xhr.open(this.method, this.url, true);
31795         
31796         var headers = {
31797             "Accept": "application/json",
31798             "Cache-Control": "no-cache",
31799             "X-Requested-With": "XMLHttpRequest"
31800         };
31801         
31802         for (var headerName in headers) {
31803             var headerValue = headers[headerName];
31804             if (headerValue) {
31805                 this.xhr.setRequestHeader(headerName, headerValue);
31806             }
31807         }
31808         
31809         var _this = this;
31810         
31811         this.xhr.onload = function()
31812         {
31813             _this.xhrOnLoad(_this.xhr);
31814         }
31815         
31816         this.xhr.onerror = function()
31817         {
31818             _this.xhrOnError(_this.xhr);
31819         }
31820         
31821         var formData = new FormData();
31822
31823         formData.append('returnHTML', 'NO');
31824         
31825         if(crop){
31826             formData.append('crop', crop);
31827         }
31828         
31829         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31830             formData.append(this.paramName, file, file.name);
31831         }
31832         
31833         if(typeof(file.filename) != 'undefined'){
31834             formData.append('filename', file.filename);
31835         }
31836         
31837         if(typeof(file.mimetype) != 'undefined'){
31838             formData.append('mimetype', file.mimetype);
31839         }
31840         
31841         if(this.fireEvent('arrange', this, formData) != false){
31842             this.xhr.send(formData);
31843         };
31844     },
31845     
31846     xhrOnLoad : function(xhr)
31847     {
31848         if(this.loadMask){
31849             this.maskEl.unmask();
31850         }
31851         
31852         if (xhr.readyState !== 4) {
31853             this.fireEvent('exception', this, xhr);
31854             return;
31855         }
31856
31857         var response = Roo.decode(xhr.responseText);
31858         
31859         if(!response.success){
31860             this.fireEvent('exception', this, xhr);
31861             return;
31862         }
31863         
31864         var response = Roo.decode(xhr.responseText);
31865         
31866         this.fireEvent('upload', this, response);
31867         
31868     },
31869     
31870     xhrOnError : function()
31871     {
31872         if(this.loadMask){
31873             this.maskEl.unmask();
31874         }
31875         
31876         Roo.log('xhr on error');
31877         
31878         var response = Roo.decode(xhr.responseText);
31879           
31880         Roo.log(response);
31881         
31882     },
31883     
31884     prepare : function(file)
31885     {   
31886         if(this.loadMask){
31887             this.maskEl.mask(this.loadingText);
31888         }
31889         
31890         this.file = false;
31891         this.exif = {};
31892         
31893         if(typeof(file) === 'string'){
31894             this.loadCanvas(file);
31895             return;
31896         }
31897         
31898         if(!file || !this.urlAPI){
31899             return;
31900         }
31901         
31902         this.file = file;
31903         this.cropType = file.type;
31904         
31905         var _this = this;
31906         
31907         if(this.fireEvent('prepare', this, this.file) != false){
31908             
31909             var reader = new FileReader();
31910             
31911             reader.onload = function (e) {
31912                 if (e.target.error) {
31913                     Roo.log(e.target.error);
31914                     return;
31915                 }
31916                 
31917                 var buffer = e.target.result,
31918                     dataView = new DataView(buffer),
31919                     offset = 2,
31920                     maxOffset = dataView.byteLength - 4,
31921                     markerBytes,
31922                     markerLength;
31923                 
31924                 if (dataView.getUint16(0) === 0xffd8) {
31925                     while (offset < maxOffset) {
31926                         markerBytes = dataView.getUint16(offset);
31927                         
31928                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31929                             markerLength = dataView.getUint16(offset + 2) + 2;
31930                             if (offset + markerLength > dataView.byteLength) {
31931                                 Roo.log('Invalid meta data: Invalid segment size.');
31932                                 break;
31933                             }
31934                             
31935                             if(markerBytes == 0xffe1){
31936                                 _this.parseExifData(
31937                                     dataView,
31938                                     offset,
31939                                     markerLength
31940                                 );
31941                             }
31942                             
31943                             offset += markerLength;
31944                             
31945                             continue;
31946                         }
31947                         
31948                         break;
31949                     }
31950                     
31951                 }
31952                 
31953                 var url = _this.urlAPI.createObjectURL(_this.file);
31954                 
31955                 _this.loadCanvas(url);
31956                 
31957                 return;
31958             }
31959             
31960             reader.readAsArrayBuffer(this.file);
31961             
31962         }
31963         
31964     },
31965     
31966     parseExifData : function(dataView, offset, length)
31967     {
31968         var tiffOffset = offset + 10,
31969             littleEndian,
31970             dirOffset;
31971     
31972         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31973             // No Exif data, might be XMP data instead
31974             return;
31975         }
31976         
31977         // Check for the ASCII code for "Exif" (0x45786966):
31978         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31979             // No Exif data, might be XMP data instead
31980             return;
31981         }
31982         if (tiffOffset + 8 > dataView.byteLength) {
31983             Roo.log('Invalid Exif data: Invalid segment size.');
31984             return;
31985         }
31986         // Check for the two null bytes:
31987         if (dataView.getUint16(offset + 8) !== 0x0000) {
31988             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31989             return;
31990         }
31991         // Check the byte alignment:
31992         switch (dataView.getUint16(tiffOffset)) {
31993         case 0x4949:
31994             littleEndian = true;
31995             break;
31996         case 0x4D4D:
31997             littleEndian = false;
31998             break;
31999         default:
32000             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32001             return;
32002         }
32003         // Check for the TIFF tag marker (0x002A):
32004         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32005             Roo.log('Invalid Exif data: Missing TIFF marker.');
32006             return;
32007         }
32008         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32009         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32010         
32011         this.parseExifTags(
32012             dataView,
32013             tiffOffset,
32014             tiffOffset + dirOffset,
32015             littleEndian
32016         );
32017     },
32018     
32019     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32020     {
32021         var tagsNumber,
32022             dirEndOffset,
32023             i;
32024         if (dirOffset + 6 > dataView.byteLength) {
32025             Roo.log('Invalid Exif data: Invalid directory offset.');
32026             return;
32027         }
32028         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32029         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32030         if (dirEndOffset + 4 > dataView.byteLength) {
32031             Roo.log('Invalid Exif data: Invalid directory size.');
32032             return;
32033         }
32034         for (i = 0; i < tagsNumber; i += 1) {
32035             this.parseExifTag(
32036                 dataView,
32037                 tiffOffset,
32038                 dirOffset + 2 + 12 * i, // tag offset
32039                 littleEndian
32040             );
32041         }
32042         // Return the offset to the next directory:
32043         return dataView.getUint32(dirEndOffset, littleEndian);
32044     },
32045     
32046     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32047     {
32048         var tag = dataView.getUint16(offset, littleEndian);
32049         
32050         this.exif[tag] = this.getExifValue(
32051             dataView,
32052             tiffOffset,
32053             offset,
32054             dataView.getUint16(offset + 2, littleEndian), // tag type
32055             dataView.getUint32(offset + 4, littleEndian), // tag length
32056             littleEndian
32057         );
32058     },
32059     
32060     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32061     {
32062         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32063             tagSize,
32064             dataOffset,
32065             values,
32066             i,
32067             str,
32068             c;
32069     
32070         if (!tagType) {
32071             Roo.log('Invalid Exif data: Invalid tag type.');
32072             return;
32073         }
32074         
32075         tagSize = tagType.size * length;
32076         // Determine if the value is contained in the dataOffset bytes,
32077         // or if the value at the dataOffset is a pointer to the actual data:
32078         dataOffset = tagSize > 4 ?
32079                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32080         if (dataOffset + tagSize > dataView.byteLength) {
32081             Roo.log('Invalid Exif data: Invalid data offset.');
32082             return;
32083         }
32084         if (length === 1) {
32085             return tagType.getValue(dataView, dataOffset, littleEndian);
32086         }
32087         values = [];
32088         for (i = 0; i < length; i += 1) {
32089             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32090         }
32091         
32092         if (tagType.ascii) {
32093             str = '';
32094             // Concatenate the chars:
32095             for (i = 0; i < values.length; i += 1) {
32096                 c = values[i];
32097                 // Ignore the terminating NULL byte(s):
32098                 if (c === '\u0000') {
32099                     break;
32100                 }
32101                 str += c;
32102             }
32103             return str;
32104         }
32105         return values;
32106     }
32107     
32108 });
32109
32110 Roo.apply(Roo.bootstrap.UploadCropbox, {
32111     tags : {
32112         'Orientation': 0x0112
32113     },
32114     
32115     Orientation: {
32116             1: 0, //'top-left',
32117 //            2: 'top-right',
32118             3: 180, //'bottom-right',
32119 //            4: 'bottom-left',
32120 //            5: 'left-top',
32121             6: 90, //'right-top',
32122 //            7: 'right-bottom',
32123             8: 270 //'left-bottom'
32124     },
32125     
32126     exifTagTypes : {
32127         // byte, 8-bit unsigned int:
32128         1: {
32129             getValue: function (dataView, dataOffset) {
32130                 return dataView.getUint8(dataOffset);
32131             },
32132             size: 1
32133         },
32134         // ascii, 8-bit byte:
32135         2: {
32136             getValue: function (dataView, dataOffset) {
32137                 return String.fromCharCode(dataView.getUint8(dataOffset));
32138             },
32139             size: 1,
32140             ascii: true
32141         },
32142         // short, 16 bit int:
32143         3: {
32144             getValue: function (dataView, dataOffset, littleEndian) {
32145                 return dataView.getUint16(dataOffset, littleEndian);
32146             },
32147             size: 2
32148         },
32149         // long, 32 bit int:
32150         4: {
32151             getValue: function (dataView, dataOffset, littleEndian) {
32152                 return dataView.getUint32(dataOffset, littleEndian);
32153             },
32154             size: 4
32155         },
32156         // rational = two long values, first is numerator, second is denominator:
32157         5: {
32158             getValue: function (dataView, dataOffset, littleEndian) {
32159                 return dataView.getUint32(dataOffset, littleEndian) /
32160                     dataView.getUint32(dataOffset + 4, littleEndian);
32161             },
32162             size: 8
32163         },
32164         // slong, 32 bit signed int:
32165         9: {
32166             getValue: function (dataView, dataOffset, littleEndian) {
32167                 return dataView.getInt32(dataOffset, littleEndian);
32168             },
32169             size: 4
32170         },
32171         // srational, two slongs, first is numerator, second is denominator:
32172         10: {
32173             getValue: function (dataView, dataOffset, littleEndian) {
32174                 return dataView.getInt32(dataOffset, littleEndian) /
32175                     dataView.getInt32(dataOffset + 4, littleEndian);
32176             },
32177             size: 8
32178         }
32179     },
32180     
32181     footer : {
32182         STANDARD : [
32183             {
32184                 tag : 'div',
32185                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32186                 action : 'rotate-left',
32187                 cn : [
32188                     {
32189                         tag : 'button',
32190                         cls : 'btn btn-default',
32191                         html : '<i class="fa fa-undo"></i>'
32192                     }
32193                 ]
32194             },
32195             {
32196                 tag : 'div',
32197                 cls : 'btn-group roo-upload-cropbox-picture',
32198                 action : 'picture',
32199                 cn : [
32200                     {
32201                         tag : 'button',
32202                         cls : 'btn btn-default',
32203                         html : '<i class="fa fa-picture-o"></i>'
32204                     }
32205                 ]
32206             },
32207             {
32208                 tag : 'div',
32209                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32210                 action : 'rotate-right',
32211                 cn : [
32212                     {
32213                         tag : 'button',
32214                         cls : 'btn btn-default',
32215                         html : '<i class="fa fa-repeat"></i>'
32216                     }
32217                 ]
32218             }
32219         ],
32220         DOCUMENT : [
32221             {
32222                 tag : 'div',
32223                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32224                 action : 'rotate-left',
32225                 cn : [
32226                     {
32227                         tag : 'button',
32228                         cls : 'btn btn-default',
32229                         html : '<i class="fa fa-undo"></i>'
32230                     }
32231                 ]
32232             },
32233             {
32234                 tag : 'div',
32235                 cls : 'btn-group roo-upload-cropbox-download',
32236                 action : 'download',
32237                 cn : [
32238                     {
32239                         tag : 'button',
32240                         cls : 'btn btn-default',
32241                         html : '<i class="fa fa-download"></i>'
32242                     }
32243                 ]
32244             },
32245             {
32246                 tag : 'div',
32247                 cls : 'btn-group roo-upload-cropbox-crop',
32248                 action : 'crop',
32249                 cn : [
32250                     {
32251                         tag : 'button',
32252                         cls : 'btn btn-default',
32253                         html : '<i class="fa fa-crop"></i>'
32254                     }
32255                 ]
32256             },
32257             {
32258                 tag : 'div',
32259                 cls : 'btn-group roo-upload-cropbox-trash',
32260                 action : 'trash',
32261                 cn : [
32262                     {
32263                         tag : 'button',
32264                         cls : 'btn btn-default',
32265                         html : '<i class="fa fa-trash"></i>'
32266                     }
32267                 ]
32268             },
32269             {
32270                 tag : 'div',
32271                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32272                 action : 'rotate-right',
32273                 cn : [
32274                     {
32275                         tag : 'button',
32276                         cls : 'btn btn-default',
32277                         html : '<i class="fa fa-repeat"></i>'
32278                     }
32279                 ]
32280             }
32281         ],
32282         ROTATOR : [
32283             {
32284                 tag : 'div',
32285                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32286                 action : 'rotate-left',
32287                 cn : [
32288                     {
32289                         tag : 'button',
32290                         cls : 'btn btn-default',
32291                         html : '<i class="fa fa-undo"></i>'
32292                     }
32293                 ]
32294             },
32295             {
32296                 tag : 'div',
32297                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32298                 action : 'rotate-right',
32299                 cn : [
32300                     {
32301                         tag : 'button',
32302                         cls : 'btn btn-default',
32303                         html : '<i class="fa fa-repeat"></i>'
32304                     }
32305                 ]
32306             }
32307         ]
32308     }
32309 });
32310
32311 /*
32312 * Licence: LGPL
32313 */
32314
32315 /**
32316  * @class Roo.bootstrap.DocumentManager
32317  * @extends Roo.bootstrap.Component
32318  * Bootstrap DocumentManager class
32319  * @cfg {String} paramName default 'imageUpload'
32320  * @cfg {String} toolTipName default 'filename'
32321  * @cfg {String} method default POST
32322  * @cfg {String} url action url
32323  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32324  * @cfg {Boolean} multiple multiple upload default true
32325  * @cfg {Number} thumbSize default 300
32326  * @cfg {String} fieldLabel
32327  * @cfg {Number} labelWidth default 4
32328  * @cfg {String} labelAlign (left|top) default left
32329  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32330 * @cfg {Number} labellg set the width of label (1-12)
32331  * @cfg {Number} labelmd set the width of label (1-12)
32332  * @cfg {Number} labelsm set the width of label (1-12)
32333  * @cfg {Number} labelxs set the width of label (1-12)
32334  * 
32335  * @constructor
32336  * Create a new DocumentManager
32337  * @param {Object} config The config object
32338  */
32339
32340 Roo.bootstrap.DocumentManager = function(config){
32341     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32342     
32343     this.files = [];
32344     this.delegates = [];
32345     
32346     this.addEvents({
32347         /**
32348          * @event initial
32349          * Fire when initial the DocumentManager
32350          * @param {Roo.bootstrap.DocumentManager} this
32351          */
32352         "initial" : true,
32353         /**
32354          * @event inspect
32355          * inspect selected file
32356          * @param {Roo.bootstrap.DocumentManager} this
32357          * @param {File} file
32358          */
32359         "inspect" : true,
32360         /**
32361          * @event exception
32362          * Fire when xhr load exception
32363          * @param {Roo.bootstrap.DocumentManager} this
32364          * @param {XMLHttpRequest} xhr
32365          */
32366         "exception" : true,
32367         /**
32368          * @event afterupload
32369          * Fire when xhr load exception
32370          * @param {Roo.bootstrap.DocumentManager} this
32371          * @param {XMLHttpRequest} xhr
32372          */
32373         "afterupload" : true,
32374         /**
32375          * @event prepare
32376          * prepare the form data
32377          * @param {Roo.bootstrap.DocumentManager} this
32378          * @param {Object} formData
32379          */
32380         "prepare" : true,
32381         /**
32382          * @event remove
32383          * Fire when remove the file
32384          * @param {Roo.bootstrap.DocumentManager} this
32385          * @param {Object} file
32386          */
32387         "remove" : true,
32388         /**
32389          * @event refresh
32390          * Fire after refresh the file
32391          * @param {Roo.bootstrap.DocumentManager} this
32392          */
32393         "refresh" : true,
32394         /**
32395          * @event click
32396          * Fire after click the image
32397          * @param {Roo.bootstrap.DocumentManager} this
32398          * @param {Object} file
32399          */
32400         "click" : true,
32401         /**
32402          * @event edit
32403          * Fire when upload a image and editable set to true
32404          * @param {Roo.bootstrap.DocumentManager} this
32405          * @param {Object} file
32406          */
32407         "edit" : true,
32408         /**
32409          * @event beforeselectfile
32410          * Fire before select file
32411          * @param {Roo.bootstrap.DocumentManager} this
32412          */
32413         "beforeselectfile" : true,
32414         /**
32415          * @event process
32416          * Fire before process file
32417          * @param {Roo.bootstrap.DocumentManager} this
32418          * @param {Object} file
32419          */
32420         "process" : true,
32421         /**
32422          * @event previewrendered
32423          * Fire when preview rendered
32424          * @param {Roo.bootstrap.DocumentManager} this
32425          * @param {Object} file
32426          */
32427         "previewrendered" : true,
32428         /**
32429          */
32430         "previewResize" : true
32431         
32432     });
32433 };
32434
32435 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32436     
32437     boxes : 0,
32438     inputName : '',
32439     thumbSize : 300,
32440     multiple : true,
32441     files : false,
32442     method : 'POST',
32443     url : '',
32444     paramName : 'imageUpload',
32445     toolTipName : 'filename',
32446     fieldLabel : '',
32447     labelWidth : 4,
32448     labelAlign : 'left',
32449     editable : true,
32450     delegates : false,
32451     xhr : false, 
32452     
32453     labellg : 0,
32454     labelmd : 0,
32455     labelsm : 0,
32456     labelxs : 0,
32457     
32458     getAutoCreate : function()
32459     {   
32460         var managerWidget = {
32461             tag : 'div',
32462             cls : 'roo-document-manager',
32463             cn : [
32464                 {
32465                     tag : 'input',
32466                     cls : 'roo-document-manager-selector',
32467                     type : 'file'
32468                 },
32469                 {
32470                     tag : 'div',
32471                     cls : 'roo-document-manager-uploader',
32472                     cn : [
32473                         {
32474                             tag : 'div',
32475                             cls : 'roo-document-manager-upload-btn',
32476                             html : '<i class="fa fa-plus"></i>'
32477                         }
32478                     ]
32479                     
32480                 }
32481             ]
32482         };
32483         
32484         var content = [
32485             {
32486                 tag : 'div',
32487                 cls : 'column col-md-12',
32488                 cn : managerWidget
32489             }
32490         ];
32491         
32492         if(this.fieldLabel.length){
32493             
32494             content = [
32495                 {
32496                     tag : 'div',
32497                     cls : 'column col-md-12',
32498                     html : this.fieldLabel
32499                 },
32500                 {
32501                     tag : 'div',
32502                     cls : 'column col-md-12',
32503                     cn : managerWidget
32504                 }
32505             ];
32506
32507             if(this.labelAlign == 'left'){
32508                 content = [
32509                     {
32510                         tag : 'div',
32511                         cls : 'column',
32512                         html : this.fieldLabel
32513                     },
32514                     {
32515                         tag : 'div',
32516                         cls : 'column',
32517                         cn : managerWidget
32518                     }
32519                 ];
32520                 
32521                 if(this.labelWidth > 12){
32522                     content[0].style = "width: " + this.labelWidth + 'px';
32523                 }
32524
32525                 if(this.labelWidth < 13 && this.labelmd == 0){
32526                     this.labelmd = this.labelWidth;
32527                 }
32528
32529                 if(this.labellg > 0){
32530                     content[0].cls += ' col-lg-' + this.labellg;
32531                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32532                 }
32533
32534                 if(this.labelmd > 0){
32535                     content[0].cls += ' col-md-' + this.labelmd;
32536                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32537                 }
32538
32539                 if(this.labelsm > 0){
32540                     content[0].cls += ' col-sm-' + this.labelsm;
32541                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32542                 }
32543
32544                 if(this.labelxs > 0){
32545                     content[0].cls += ' col-xs-' + this.labelxs;
32546                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32547                 }
32548                 
32549             }
32550         }
32551         
32552         var cfg = {
32553             tag : 'div',
32554             cls : 'row clearfix',
32555             cn : content
32556         };
32557         
32558         return cfg;
32559         
32560     },
32561     
32562     initEvents : function()
32563     {
32564         this.managerEl = this.el.select('.roo-document-manager', true).first();
32565         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32566         
32567         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32568         this.selectorEl.hide();
32569         
32570         if(this.multiple){
32571             this.selectorEl.attr('multiple', 'multiple');
32572         }
32573         
32574         this.selectorEl.on('change', this.onFileSelected, this);
32575         
32576         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32577         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32578         
32579         this.uploader.on('click', this.onUploaderClick, this);
32580         
32581         this.renderProgressDialog();
32582         
32583         var _this = this;
32584         
32585         window.addEventListener("resize", function() { _this.refresh(); } );
32586         
32587         this.fireEvent('initial', this);
32588     },
32589     
32590     renderProgressDialog : function()
32591     {
32592         var _this = this;
32593         
32594         this.progressDialog = new Roo.bootstrap.Modal({
32595             cls : 'roo-document-manager-progress-dialog',
32596             allow_close : false,
32597             animate : false,
32598             title : '',
32599             buttons : [
32600                 {
32601                     name  :'cancel',
32602                     weight : 'danger',
32603                     html : 'Cancel'
32604                 }
32605             ], 
32606             listeners : { 
32607                 btnclick : function() {
32608                     _this.uploadCancel();
32609                     this.hide();
32610                 }
32611             }
32612         });
32613          
32614         this.progressDialog.render(Roo.get(document.body));
32615          
32616         this.progress = new Roo.bootstrap.Progress({
32617             cls : 'roo-document-manager-progress',
32618             active : true,
32619             striped : true
32620         });
32621         
32622         this.progress.render(this.progressDialog.getChildContainer());
32623         
32624         this.progressBar = new Roo.bootstrap.ProgressBar({
32625             cls : 'roo-document-manager-progress-bar',
32626             aria_valuenow : 0,
32627             aria_valuemin : 0,
32628             aria_valuemax : 12,
32629             panel : 'success'
32630         });
32631         
32632         this.progressBar.render(this.progress.getChildContainer());
32633     },
32634     
32635     onUploaderClick : function(e)
32636     {
32637         e.preventDefault();
32638      
32639         if(this.fireEvent('beforeselectfile', this) != false){
32640             this.selectorEl.dom.click();
32641         }
32642         
32643     },
32644     
32645     onFileSelected : function(e)
32646     {
32647         e.preventDefault();
32648         
32649         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32650             return;
32651         }
32652         
32653         Roo.each(this.selectorEl.dom.files, function(file){
32654             if(this.fireEvent('inspect', this, file) != false){
32655                 this.files.push(file);
32656             }
32657         }, this);
32658         
32659         this.queue();
32660         
32661     },
32662     
32663     queue : function()
32664     {
32665         this.selectorEl.dom.value = '';
32666         
32667         if(!this.files || !this.files.length){
32668             return;
32669         }
32670         
32671         if(this.boxes > 0 && this.files.length > this.boxes){
32672             this.files = this.files.slice(0, this.boxes);
32673         }
32674         
32675         this.uploader.show();
32676         
32677         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32678             this.uploader.hide();
32679         }
32680         
32681         var _this = this;
32682         
32683         var files = [];
32684         
32685         var docs = [];
32686         
32687         Roo.each(this.files, function(file){
32688             
32689             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32690                 var f = this.renderPreview(file);
32691                 files.push(f);
32692                 return;
32693             }
32694             
32695             if(file.type.indexOf('image') != -1){
32696                 this.delegates.push(
32697                     (function(){
32698                         _this.process(file);
32699                     }).createDelegate(this)
32700                 );
32701         
32702                 return;
32703             }
32704             
32705             docs.push(
32706                 (function(){
32707                     _this.process(file);
32708                 }).createDelegate(this)
32709             );
32710             
32711         }, this);
32712         
32713         this.files = files;
32714         
32715         this.delegates = this.delegates.concat(docs);
32716         
32717         if(!this.delegates.length){
32718             this.refresh();
32719             return;
32720         }
32721         
32722         this.progressBar.aria_valuemax = this.delegates.length;
32723         
32724         this.arrange();
32725         
32726         return;
32727     },
32728     
32729     arrange : function()
32730     {
32731         if(!this.delegates.length){
32732             this.progressDialog.hide();
32733             this.refresh();
32734             return;
32735         }
32736         
32737         var delegate = this.delegates.shift();
32738         
32739         this.progressDialog.show();
32740         
32741         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32742         
32743         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32744         
32745         delegate();
32746     },
32747     
32748     refresh : function()
32749     {
32750         this.uploader.show();
32751         
32752         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32753             this.uploader.hide();
32754         }
32755         
32756         Roo.isTouch ? this.closable(false) : this.closable(true);
32757         
32758         this.fireEvent('refresh', this);
32759     },
32760     
32761     onRemove : function(e, el, o)
32762     {
32763         e.preventDefault();
32764         
32765         this.fireEvent('remove', this, o);
32766         
32767     },
32768     
32769     remove : function(o)
32770     {
32771         var files = [];
32772         
32773         Roo.each(this.files, function(file){
32774             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32775                 files.push(file);
32776                 return;
32777             }
32778
32779             o.target.remove();
32780
32781         }, this);
32782         
32783         this.files = files;
32784         
32785         this.refresh();
32786     },
32787     
32788     clear : function()
32789     {
32790         Roo.each(this.files, function(file){
32791             if(!file.target){
32792                 return;
32793             }
32794             
32795             file.target.remove();
32796
32797         }, this);
32798         
32799         this.files = [];
32800         
32801         this.refresh();
32802     },
32803     
32804     onClick : function(e, el, o)
32805     {
32806         e.preventDefault();
32807         
32808         this.fireEvent('click', this, o);
32809         
32810     },
32811     
32812     closable : function(closable)
32813     {
32814         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32815             
32816             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32817             
32818             if(closable){
32819                 el.show();
32820                 return;
32821             }
32822             
32823             el.hide();
32824             
32825         }, this);
32826     },
32827     
32828     xhrOnLoad : function(xhr)
32829     {
32830         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32831             el.remove();
32832         }, this);
32833         
32834         if (xhr.readyState !== 4) {
32835             this.arrange();
32836             this.fireEvent('exception', this, xhr);
32837             return;
32838         }
32839
32840         var response = Roo.decode(xhr.responseText);
32841         
32842         if(!response.success){
32843             this.arrange();
32844             this.fireEvent('exception', this, xhr);
32845             return;
32846         }
32847         
32848         var file = this.renderPreview(response.data);
32849         
32850         this.files.push(file);
32851         
32852         this.arrange();
32853         
32854         this.fireEvent('afterupload', this, xhr);
32855         
32856     },
32857     
32858     xhrOnError : function(xhr)
32859     {
32860         Roo.log('xhr on error');
32861         
32862         var response = Roo.decode(xhr.responseText);
32863           
32864         Roo.log(response);
32865         
32866         this.arrange();
32867     },
32868     
32869     process : function(file)
32870     {
32871         if(this.fireEvent('process', this, file) !== false){
32872             if(this.editable && file.type.indexOf('image') != -1){
32873                 this.fireEvent('edit', this, file);
32874                 return;
32875             }
32876
32877             this.uploadStart(file, false);
32878
32879             return;
32880         }
32881         
32882     },
32883     
32884     uploadStart : function(file, crop)
32885     {
32886         this.xhr = new XMLHttpRequest();
32887         
32888         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32889             this.arrange();
32890             return;
32891         }
32892         
32893         file.xhr = this.xhr;
32894             
32895         this.managerEl.createChild({
32896             tag : 'div',
32897             cls : 'roo-document-manager-loading',
32898             cn : [
32899                 {
32900                     tag : 'div',
32901                     tooltip : file.name,
32902                     cls : 'roo-document-manager-thumb',
32903                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32904                 }
32905             ]
32906
32907         });
32908
32909         this.xhr.open(this.method, this.url, true);
32910         
32911         var headers = {
32912             "Accept": "application/json",
32913             "Cache-Control": "no-cache",
32914             "X-Requested-With": "XMLHttpRequest"
32915         };
32916         
32917         for (var headerName in headers) {
32918             var headerValue = headers[headerName];
32919             if (headerValue) {
32920                 this.xhr.setRequestHeader(headerName, headerValue);
32921             }
32922         }
32923         
32924         var _this = this;
32925         
32926         this.xhr.onload = function()
32927         {
32928             _this.xhrOnLoad(_this.xhr);
32929         }
32930         
32931         this.xhr.onerror = function()
32932         {
32933             _this.xhrOnError(_this.xhr);
32934         }
32935         
32936         var formData = new FormData();
32937
32938         formData.append('returnHTML', 'NO');
32939         
32940         if(crop){
32941             formData.append('crop', crop);
32942         }
32943         
32944         formData.append(this.paramName, file, file.name);
32945         
32946         var options = {
32947             file : file, 
32948             manually : false
32949         };
32950         
32951         if(this.fireEvent('prepare', this, formData, options) != false){
32952             
32953             if(options.manually){
32954                 return;
32955             }
32956             
32957             this.xhr.send(formData);
32958             return;
32959         };
32960         
32961         this.uploadCancel();
32962     },
32963     
32964     uploadCancel : function()
32965     {
32966         if (this.xhr) {
32967             this.xhr.abort();
32968         }
32969         
32970         this.delegates = [];
32971         
32972         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32973             el.remove();
32974         }, this);
32975         
32976         this.arrange();
32977     },
32978     
32979     renderPreview : function(file)
32980     {
32981         if(typeof(file.target) != 'undefined' && file.target){
32982             return file;
32983         }
32984         
32985         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32986         
32987         var previewEl = this.managerEl.createChild({
32988             tag : 'div',
32989             cls : 'roo-document-manager-preview',
32990             cn : [
32991                 {
32992                     tag : 'div',
32993                     tooltip : file[this.toolTipName],
32994                     cls : 'roo-document-manager-thumb',
32995                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32996                 },
32997                 {
32998                     tag : 'button',
32999                     cls : 'close',
33000                     html : '<i class="fa fa-times-circle"></i>'
33001                 }
33002             ]
33003         });
33004
33005         var close = previewEl.select('button.close', true).first();
33006
33007         close.on('click', this.onRemove, this, file);
33008
33009         file.target = previewEl;
33010
33011         var image = previewEl.select('img', true).first();
33012         
33013         var _this = this;
33014         
33015         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33016         
33017         image.on('click', this.onClick, this, file);
33018         
33019         this.fireEvent('previewrendered', this, file);
33020         
33021         return file;
33022         
33023     },
33024     
33025     onPreviewLoad : function(file, image)
33026     {
33027         if(typeof(file.target) == 'undefined' || !file.target){
33028             return;
33029         }
33030         
33031         var width = image.dom.naturalWidth || image.dom.width;
33032         var height = image.dom.naturalHeight || image.dom.height;
33033         
33034         if(!this.previewResize) {
33035             return;
33036         }
33037         
33038         if(width > height){
33039             file.target.addClass('wide');
33040             return;
33041         }
33042         
33043         file.target.addClass('tall');
33044         return;
33045         
33046     },
33047     
33048     uploadFromSource : function(file, crop)
33049     {
33050         this.xhr = new XMLHttpRequest();
33051         
33052         this.managerEl.createChild({
33053             tag : 'div',
33054             cls : 'roo-document-manager-loading',
33055             cn : [
33056                 {
33057                     tag : 'div',
33058                     tooltip : file.name,
33059                     cls : 'roo-document-manager-thumb',
33060                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33061                 }
33062             ]
33063
33064         });
33065
33066         this.xhr.open(this.method, this.url, true);
33067         
33068         var headers = {
33069             "Accept": "application/json",
33070             "Cache-Control": "no-cache",
33071             "X-Requested-With": "XMLHttpRequest"
33072         };
33073         
33074         for (var headerName in headers) {
33075             var headerValue = headers[headerName];
33076             if (headerValue) {
33077                 this.xhr.setRequestHeader(headerName, headerValue);
33078             }
33079         }
33080         
33081         var _this = this;
33082         
33083         this.xhr.onload = function()
33084         {
33085             _this.xhrOnLoad(_this.xhr);
33086         }
33087         
33088         this.xhr.onerror = function()
33089         {
33090             _this.xhrOnError(_this.xhr);
33091         }
33092         
33093         var formData = new FormData();
33094
33095         formData.append('returnHTML', 'NO');
33096         
33097         formData.append('crop', crop);
33098         
33099         if(typeof(file.filename) != 'undefined'){
33100             formData.append('filename', file.filename);
33101         }
33102         
33103         if(typeof(file.mimetype) != 'undefined'){
33104             formData.append('mimetype', file.mimetype);
33105         }
33106         
33107         Roo.log(formData);
33108         
33109         if(this.fireEvent('prepare', this, formData) != false){
33110             this.xhr.send(formData);
33111         };
33112     }
33113 });
33114
33115 /*
33116 * Licence: LGPL
33117 */
33118
33119 /**
33120  * @class Roo.bootstrap.DocumentViewer
33121  * @extends Roo.bootstrap.Component
33122  * Bootstrap DocumentViewer class
33123  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33124  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33125  * 
33126  * @constructor
33127  * Create a new DocumentViewer
33128  * @param {Object} config The config object
33129  */
33130
33131 Roo.bootstrap.DocumentViewer = function(config){
33132     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33133     
33134     this.addEvents({
33135         /**
33136          * @event initial
33137          * Fire after initEvent
33138          * @param {Roo.bootstrap.DocumentViewer} this
33139          */
33140         "initial" : true,
33141         /**
33142          * @event click
33143          * Fire after click
33144          * @param {Roo.bootstrap.DocumentViewer} this
33145          */
33146         "click" : true,
33147         /**
33148          * @event download
33149          * Fire after download button
33150          * @param {Roo.bootstrap.DocumentViewer} this
33151          */
33152         "download" : true,
33153         /**
33154          * @event trash
33155          * Fire after trash button
33156          * @param {Roo.bootstrap.DocumentViewer} this
33157          */
33158         "trash" : true
33159         
33160     });
33161 };
33162
33163 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33164     
33165     showDownload : true,
33166     
33167     showTrash : true,
33168     
33169     getAutoCreate : function()
33170     {
33171         var cfg = {
33172             tag : 'div',
33173             cls : 'roo-document-viewer',
33174             cn : [
33175                 {
33176                     tag : 'div',
33177                     cls : 'roo-document-viewer-body',
33178                     cn : [
33179                         {
33180                             tag : 'div',
33181                             cls : 'roo-document-viewer-thumb',
33182                             cn : [
33183                                 {
33184                                     tag : 'img',
33185                                     cls : 'roo-document-viewer-image'
33186                                 }
33187                             ]
33188                         }
33189                     ]
33190                 },
33191                 {
33192                     tag : 'div',
33193                     cls : 'roo-document-viewer-footer',
33194                     cn : {
33195                         tag : 'div',
33196                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33197                         cn : [
33198                             {
33199                                 tag : 'div',
33200                                 cls : 'btn-group roo-document-viewer-download',
33201                                 cn : [
33202                                     {
33203                                         tag : 'button',
33204                                         cls : 'btn btn-default',
33205                                         html : '<i class="fa fa-download"></i>'
33206                                     }
33207                                 ]
33208                             },
33209                             {
33210                                 tag : 'div',
33211                                 cls : 'btn-group roo-document-viewer-trash',
33212                                 cn : [
33213                                     {
33214                                         tag : 'button',
33215                                         cls : 'btn btn-default',
33216                                         html : '<i class="fa fa-trash"></i>'
33217                                     }
33218                                 ]
33219                             }
33220                         ]
33221                     }
33222                 }
33223             ]
33224         };
33225         
33226         return cfg;
33227     },
33228     
33229     initEvents : function()
33230     {
33231         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33232         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33233         
33234         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33235         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33236         
33237         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33238         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33239         
33240         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33241         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33242         
33243         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33244         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33245         
33246         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33247         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33248         
33249         this.bodyEl.on('click', this.onClick, this);
33250         this.downloadBtn.on('click', this.onDownload, this);
33251         this.trashBtn.on('click', this.onTrash, this);
33252         
33253         this.downloadBtn.hide();
33254         this.trashBtn.hide();
33255         
33256         if(this.showDownload){
33257             this.downloadBtn.show();
33258         }
33259         
33260         if(this.showTrash){
33261             this.trashBtn.show();
33262         }
33263         
33264         if(!this.showDownload && !this.showTrash) {
33265             this.footerEl.hide();
33266         }
33267         
33268     },
33269     
33270     initial : function()
33271     {
33272         this.fireEvent('initial', this);
33273         
33274     },
33275     
33276     onClick : function(e)
33277     {
33278         e.preventDefault();
33279         
33280         this.fireEvent('click', this);
33281     },
33282     
33283     onDownload : function(e)
33284     {
33285         e.preventDefault();
33286         
33287         this.fireEvent('download', this);
33288     },
33289     
33290     onTrash : function(e)
33291     {
33292         e.preventDefault();
33293         
33294         this.fireEvent('trash', this);
33295     }
33296     
33297 });
33298 /*
33299  * - LGPL
33300  *
33301  * nav progress bar
33302  * 
33303  */
33304
33305 /**
33306  * @class Roo.bootstrap.NavProgressBar
33307  * @extends Roo.bootstrap.Component
33308  * Bootstrap NavProgressBar class
33309  * 
33310  * @constructor
33311  * Create a new nav progress bar
33312  * @param {Object} config The config object
33313  */
33314
33315 Roo.bootstrap.NavProgressBar = function(config){
33316     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33317
33318     this.bullets = this.bullets || [];
33319    
33320 //    Roo.bootstrap.NavProgressBar.register(this);
33321      this.addEvents({
33322         /**
33323              * @event changed
33324              * Fires when the active item changes
33325              * @param {Roo.bootstrap.NavProgressBar} this
33326              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33327              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33328          */
33329         'changed': true
33330      });
33331     
33332 };
33333
33334 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33335     
33336     bullets : [],
33337     barItems : [],
33338     
33339     getAutoCreate : function()
33340     {
33341         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33342         
33343         cfg = {
33344             tag : 'div',
33345             cls : 'roo-navigation-bar-group',
33346             cn : [
33347                 {
33348                     tag : 'div',
33349                     cls : 'roo-navigation-top-bar'
33350                 },
33351                 {
33352                     tag : 'div',
33353                     cls : 'roo-navigation-bullets-bar',
33354                     cn : [
33355                         {
33356                             tag : 'ul',
33357                             cls : 'roo-navigation-bar'
33358                         }
33359                     ]
33360                 },
33361                 
33362                 {
33363                     tag : 'div',
33364                     cls : 'roo-navigation-bottom-bar'
33365                 }
33366             ]
33367             
33368         };
33369         
33370         return cfg;
33371         
33372     },
33373     
33374     initEvents: function() 
33375     {
33376         
33377     },
33378     
33379     onRender : function(ct, position) 
33380     {
33381         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33382         
33383         if(this.bullets.length){
33384             Roo.each(this.bullets, function(b){
33385                this.addItem(b);
33386             }, this);
33387         }
33388         
33389         this.format();
33390         
33391     },
33392     
33393     addItem : function(cfg)
33394     {
33395         var item = new Roo.bootstrap.NavProgressItem(cfg);
33396         
33397         item.parentId = this.id;
33398         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33399         
33400         if(cfg.html){
33401             var top = new Roo.bootstrap.Element({
33402                 tag : 'div',
33403                 cls : 'roo-navigation-bar-text'
33404             });
33405             
33406             var bottom = new Roo.bootstrap.Element({
33407                 tag : 'div',
33408                 cls : 'roo-navigation-bar-text'
33409             });
33410             
33411             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33412             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33413             
33414             var topText = new Roo.bootstrap.Element({
33415                 tag : 'span',
33416                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33417             });
33418             
33419             var bottomText = new Roo.bootstrap.Element({
33420                 tag : 'span',
33421                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33422             });
33423             
33424             topText.onRender(top.el, null);
33425             bottomText.onRender(bottom.el, null);
33426             
33427             item.topEl = top;
33428             item.bottomEl = bottom;
33429         }
33430         
33431         this.barItems.push(item);
33432         
33433         return item;
33434     },
33435     
33436     getActive : function()
33437     {
33438         var active = false;
33439         
33440         Roo.each(this.barItems, function(v){
33441             
33442             if (!v.isActive()) {
33443                 return;
33444             }
33445             
33446             active = v;
33447             return false;
33448             
33449         });
33450         
33451         return active;
33452     },
33453     
33454     setActiveItem : function(item)
33455     {
33456         var prev = false;
33457         
33458         Roo.each(this.barItems, function(v){
33459             if (v.rid == item.rid) {
33460                 return ;
33461             }
33462             
33463             if (v.isActive()) {
33464                 v.setActive(false);
33465                 prev = v;
33466             }
33467         });
33468
33469         item.setActive(true);
33470         
33471         this.fireEvent('changed', this, item, prev);
33472     },
33473     
33474     getBarItem: function(rid)
33475     {
33476         var ret = false;
33477         
33478         Roo.each(this.barItems, function(e) {
33479             if (e.rid != rid) {
33480                 return;
33481             }
33482             
33483             ret =  e;
33484             return false;
33485         });
33486         
33487         return ret;
33488     },
33489     
33490     indexOfItem : function(item)
33491     {
33492         var index = false;
33493         
33494         Roo.each(this.barItems, function(v, i){
33495             
33496             if (v.rid != item.rid) {
33497                 return;
33498             }
33499             
33500             index = i;
33501             return false
33502         });
33503         
33504         return index;
33505     },
33506     
33507     setActiveNext : function()
33508     {
33509         var i = this.indexOfItem(this.getActive());
33510         
33511         if (i > this.barItems.length) {
33512             return;
33513         }
33514         
33515         this.setActiveItem(this.barItems[i+1]);
33516     },
33517     
33518     setActivePrev : function()
33519     {
33520         var i = this.indexOfItem(this.getActive());
33521         
33522         if (i  < 1) {
33523             return;
33524         }
33525         
33526         this.setActiveItem(this.barItems[i-1]);
33527     },
33528     
33529     format : function()
33530     {
33531         if(!this.barItems.length){
33532             return;
33533         }
33534      
33535         var width = 100 / this.barItems.length;
33536         
33537         Roo.each(this.barItems, function(i){
33538             i.el.setStyle('width', width + '%');
33539             i.topEl.el.setStyle('width', width + '%');
33540             i.bottomEl.el.setStyle('width', width + '%');
33541         }, this);
33542         
33543     }
33544     
33545 });
33546 /*
33547  * - LGPL
33548  *
33549  * Nav Progress Item
33550  * 
33551  */
33552
33553 /**
33554  * @class Roo.bootstrap.NavProgressItem
33555  * @extends Roo.bootstrap.Component
33556  * Bootstrap NavProgressItem class
33557  * @cfg {String} rid the reference id
33558  * @cfg {Boolean} active (true|false) Is item active default false
33559  * @cfg {Boolean} disabled (true|false) Is item active default false
33560  * @cfg {String} html
33561  * @cfg {String} position (top|bottom) text position default bottom
33562  * @cfg {String} icon show icon instead of number
33563  * 
33564  * @constructor
33565  * Create a new NavProgressItem
33566  * @param {Object} config The config object
33567  */
33568 Roo.bootstrap.NavProgressItem = function(config){
33569     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33570     this.addEvents({
33571         // raw events
33572         /**
33573          * @event click
33574          * The raw click event for the entire grid.
33575          * @param {Roo.bootstrap.NavProgressItem} this
33576          * @param {Roo.EventObject} e
33577          */
33578         "click" : true
33579     });
33580    
33581 };
33582
33583 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33584     
33585     rid : '',
33586     active : false,
33587     disabled : false,
33588     html : '',
33589     position : 'bottom',
33590     icon : false,
33591     
33592     getAutoCreate : function()
33593     {
33594         var iconCls = 'roo-navigation-bar-item-icon';
33595         
33596         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33597         
33598         var cfg = {
33599             tag: 'li',
33600             cls: 'roo-navigation-bar-item',
33601             cn : [
33602                 {
33603                     tag : 'i',
33604                     cls : iconCls
33605                 }
33606             ]
33607         };
33608         
33609         if(this.active){
33610             cfg.cls += ' active';
33611         }
33612         if(this.disabled){
33613             cfg.cls += ' disabled';
33614         }
33615         
33616         return cfg;
33617     },
33618     
33619     disable : function()
33620     {
33621         this.setDisabled(true);
33622     },
33623     
33624     enable : function()
33625     {
33626         this.setDisabled(false);
33627     },
33628     
33629     initEvents: function() 
33630     {
33631         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33632         
33633         this.iconEl.on('click', this.onClick, this);
33634     },
33635     
33636     onClick : function(e)
33637     {
33638         e.preventDefault();
33639         
33640         if(this.disabled){
33641             return;
33642         }
33643         
33644         if(this.fireEvent('click', this, e) === false){
33645             return;
33646         };
33647         
33648         this.parent().setActiveItem(this);
33649     },
33650     
33651     isActive: function () 
33652     {
33653         return this.active;
33654     },
33655     
33656     setActive : function(state)
33657     {
33658         if(this.active == state){
33659             return;
33660         }
33661         
33662         this.active = state;
33663         
33664         if (state) {
33665             this.el.addClass('active');
33666             return;
33667         }
33668         
33669         this.el.removeClass('active');
33670         
33671         return;
33672     },
33673     
33674     setDisabled : function(state)
33675     {
33676         if(this.disabled == state){
33677             return;
33678         }
33679         
33680         this.disabled = state;
33681         
33682         if (state) {
33683             this.el.addClass('disabled');
33684             return;
33685         }
33686         
33687         this.el.removeClass('disabled');
33688     },
33689     
33690     tooltipEl : function()
33691     {
33692         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33693     }
33694 });
33695  
33696
33697  /*
33698  * - LGPL
33699  *
33700  * FieldLabel
33701  * 
33702  */
33703
33704 /**
33705  * @class Roo.bootstrap.FieldLabel
33706  * @extends Roo.bootstrap.Component
33707  * Bootstrap FieldLabel class
33708  * @cfg {String} html contents of the element
33709  * @cfg {String} tag tag of the element default label
33710  * @cfg {String} cls class of the element
33711  * @cfg {String} target label target 
33712  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33713  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33714  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33715  * @cfg {String} iconTooltip default "This field is required"
33716  * @cfg {String} indicatorpos (left|right) default left
33717  * 
33718  * @constructor
33719  * Create a new FieldLabel
33720  * @param {Object} config The config object
33721  */
33722
33723 Roo.bootstrap.FieldLabel = function(config){
33724     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33725     
33726     this.addEvents({
33727             /**
33728              * @event invalid
33729              * Fires after the field has been marked as invalid.
33730              * @param {Roo.form.FieldLabel} this
33731              * @param {String} msg The validation message
33732              */
33733             invalid : true,
33734             /**
33735              * @event valid
33736              * Fires after the field has been validated with no errors.
33737              * @param {Roo.form.FieldLabel} this
33738              */
33739             valid : true
33740         });
33741 };
33742
33743 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33744     
33745     tag: 'label',
33746     cls: '',
33747     html: '',
33748     target: '',
33749     allowBlank : true,
33750     invalidClass : 'has-warning',
33751     validClass : 'has-success',
33752     iconTooltip : 'This field is required',
33753     indicatorpos : 'left',
33754     
33755     getAutoCreate : function(){
33756         
33757         var cls = "";
33758         if (!this.allowBlank) {
33759             cls  = "visible";
33760         }
33761         
33762         var cfg = {
33763             tag : this.tag,
33764             cls : 'roo-bootstrap-field-label ' + this.cls,
33765             for : this.target,
33766             cn : [
33767                 {
33768                     tag : 'i',
33769                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33770                     tooltip : this.iconTooltip
33771                 },
33772                 {
33773                     tag : 'span',
33774                     html : this.html
33775                 }
33776             ] 
33777         };
33778         
33779         if(this.indicatorpos == 'right'){
33780             var cfg = {
33781                 tag : this.tag,
33782                 cls : 'roo-bootstrap-field-label ' + this.cls,
33783                 for : this.target,
33784                 cn : [
33785                     {
33786                         tag : 'span',
33787                         html : this.html
33788                     },
33789                     {
33790                         tag : 'i',
33791                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33792                         tooltip : this.iconTooltip
33793                     }
33794                 ] 
33795             };
33796         }
33797         
33798         return cfg;
33799     },
33800     
33801     initEvents: function() 
33802     {
33803         Roo.bootstrap.Element.superclass.initEvents.call(this);
33804         
33805         this.indicator = this.indicatorEl();
33806         
33807         if(this.indicator){
33808             this.indicator.removeClass('visible');
33809             this.indicator.addClass('invisible');
33810         }
33811         
33812         Roo.bootstrap.FieldLabel.register(this);
33813     },
33814     
33815     indicatorEl : function()
33816     {
33817         var indicator = this.el.select('i.roo-required-indicator',true).first();
33818         
33819         if(!indicator){
33820             return false;
33821         }
33822         
33823         return indicator;
33824         
33825     },
33826     
33827     /**
33828      * Mark this field as valid
33829      */
33830     markValid : function()
33831     {
33832         if(this.indicator){
33833             this.indicator.removeClass('visible');
33834             this.indicator.addClass('invisible');
33835         }
33836         if (Roo.bootstrap.version == 3) {
33837             this.el.removeClass(this.invalidClass);
33838             this.el.addClass(this.validClass);
33839         } else {
33840             this.el.removeClass('is-invalid');
33841             this.el.addClass('is-valid');
33842         }
33843         
33844         
33845         this.fireEvent('valid', this);
33846     },
33847     
33848     /**
33849      * Mark this field as invalid
33850      * @param {String} msg The validation message
33851      */
33852     markInvalid : function(msg)
33853     {
33854         if(this.indicator){
33855             this.indicator.removeClass('invisible');
33856             this.indicator.addClass('visible');
33857         }
33858           if (Roo.bootstrap.version == 3) {
33859             this.el.removeClass(this.validClass);
33860             this.el.addClass(this.invalidClass);
33861         } else {
33862             this.el.removeClass('is-valid');
33863             this.el.addClass('is-invalid');
33864         }
33865         
33866         
33867         this.fireEvent('invalid', this, msg);
33868     }
33869     
33870    
33871 });
33872
33873 Roo.apply(Roo.bootstrap.FieldLabel, {
33874     
33875     groups: {},
33876     
33877      /**
33878     * register a FieldLabel Group
33879     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33880     */
33881     register : function(label)
33882     {
33883         if(this.groups.hasOwnProperty(label.target)){
33884             return;
33885         }
33886      
33887         this.groups[label.target] = label;
33888         
33889     },
33890     /**
33891     * fetch a FieldLabel Group based on the target
33892     * @param {string} target
33893     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33894     */
33895     get: function(target) {
33896         if (typeof(this.groups[target]) == 'undefined') {
33897             return false;
33898         }
33899         
33900         return this.groups[target] ;
33901     }
33902 });
33903
33904  
33905
33906  /*
33907  * - LGPL
33908  *
33909  * page DateSplitField.
33910  * 
33911  */
33912
33913
33914 /**
33915  * @class Roo.bootstrap.DateSplitField
33916  * @extends Roo.bootstrap.Component
33917  * Bootstrap DateSplitField class
33918  * @cfg {string} fieldLabel - the label associated
33919  * @cfg {Number} labelWidth set the width of label (0-12)
33920  * @cfg {String} labelAlign (top|left)
33921  * @cfg {Boolean} dayAllowBlank (true|false) default false
33922  * @cfg {Boolean} monthAllowBlank (true|false) default false
33923  * @cfg {Boolean} yearAllowBlank (true|false) default false
33924  * @cfg {string} dayPlaceholder 
33925  * @cfg {string} monthPlaceholder
33926  * @cfg {string} yearPlaceholder
33927  * @cfg {string} dayFormat default 'd'
33928  * @cfg {string} monthFormat default 'm'
33929  * @cfg {string} yearFormat default 'Y'
33930  * @cfg {Number} labellg set the width of label (1-12)
33931  * @cfg {Number} labelmd set the width of label (1-12)
33932  * @cfg {Number} labelsm set the width of label (1-12)
33933  * @cfg {Number} labelxs set the width of label (1-12)
33934
33935  *     
33936  * @constructor
33937  * Create a new DateSplitField
33938  * @param {Object} config The config object
33939  */
33940
33941 Roo.bootstrap.DateSplitField = function(config){
33942     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33943     
33944     this.addEvents({
33945         // raw events
33946          /**
33947          * @event years
33948          * getting the data of years
33949          * @param {Roo.bootstrap.DateSplitField} this
33950          * @param {Object} years
33951          */
33952         "years" : true,
33953         /**
33954          * @event days
33955          * getting the data of days
33956          * @param {Roo.bootstrap.DateSplitField} this
33957          * @param {Object} days
33958          */
33959         "days" : true,
33960         /**
33961          * @event invalid
33962          * Fires after the field has been marked as invalid.
33963          * @param {Roo.form.Field} this
33964          * @param {String} msg The validation message
33965          */
33966         invalid : true,
33967        /**
33968          * @event valid
33969          * Fires after the field has been validated with no errors.
33970          * @param {Roo.form.Field} this
33971          */
33972         valid : true
33973     });
33974 };
33975
33976 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33977     
33978     fieldLabel : '',
33979     labelAlign : 'top',
33980     labelWidth : 3,
33981     dayAllowBlank : false,
33982     monthAllowBlank : false,
33983     yearAllowBlank : false,
33984     dayPlaceholder : '',
33985     monthPlaceholder : '',
33986     yearPlaceholder : '',
33987     dayFormat : 'd',
33988     monthFormat : 'm',
33989     yearFormat : 'Y',
33990     isFormField : true,
33991     labellg : 0,
33992     labelmd : 0,
33993     labelsm : 0,
33994     labelxs : 0,
33995     
33996     getAutoCreate : function()
33997     {
33998         var cfg = {
33999             tag : 'div',
34000             cls : 'row roo-date-split-field-group',
34001             cn : [
34002                 {
34003                     tag : 'input',
34004                     type : 'hidden',
34005                     cls : 'form-hidden-field roo-date-split-field-group-value',
34006                     name : this.name
34007                 }
34008             ]
34009         };
34010         
34011         var labelCls = 'col-md-12';
34012         var contentCls = 'col-md-4';
34013         
34014         if(this.fieldLabel){
34015             
34016             var label = {
34017                 tag : 'div',
34018                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34019                 cn : [
34020                     {
34021                         tag : 'label',
34022                         html : this.fieldLabel
34023                     }
34024                 ]
34025             };
34026             
34027             if(this.labelAlign == 'left'){
34028             
34029                 if(this.labelWidth > 12){
34030                     label.style = "width: " + this.labelWidth + 'px';
34031                 }
34032
34033                 if(this.labelWidth < 13 && this.labelmd == 0){
34034                     this.labelmd = this.labelWidth;
34035                 }
34036
34037                 if(this.labellg > 0){
34038                     labelCls = ' col-lg-' + this.labellg;
34039                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34040                 }
34041
34042                 if(this.labelmd > 0){
34043                     labelCls = ' col-md-' + this.labelmd;
34044                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34045                 }
34046
34047                 if(this.labelsm > 0){
34048                     labelCls = ' col-sm-' + this.labelsm;
34049                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34050                 }
34051
34052                 if(this.labelxs > 0){
34053                     labelCls = ' col-xs-' + this.labelxs;
34054                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34055                 }
34056             }
34057             
34058             label.cls += ' ' + labelCls;
34059             
34060             cfg.cn.push(label);
34061         }
34062         
34063         Roo.each(['day', 'month', 'year'], function(t){
34064             cfg.cn.push({
34065                 tag : 'div',
34066                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34067             });
34068         }, this);
34069         
34070         return cfg;
34071     },
34072     
34073     inputEl: function ()
34074     {
34075         return this.el.select('.roo-date-split-field-group-value', true).first();
34076     },
34077     
34078     onRender : function(ct, position) 
34079     {
34080         var _this = this;
34081         
34082         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34083         
34084         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34085         
34086         this.dayField = new Roo.bootstrap.ComboBox({
34087             allowBlank : this.dayAllowBlank,
34088             alwaysQuery : true,
34089             displayField : 'value',
34090             editable : false,
34091             fieldLabel : '',
34092             forceSelection : true,
34093             mode : 'local',
34094             placeholder : this.dayPlaceholder,
34095             selectOnFocus : true,
34096             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34097             triggerAction : 'all',
34098             typeAhead : true,
34099             valueField : 'value',
34100             store : new Roo.data.SimpleStore({
34101                 data : (function() {    
34102                     var days = [];
34103                     _this.fireEvent('days', _this, days);
34104                     return days;
34105                 })(),
34106                 fields : [ 'value' ]
34107             }),
34108             listeners : {
34109                 select : function (_self, record, index)
34110                 {
34111                     _this.setValue(_this.getValue());
34112                 }
34113             }
34114         });
34115
34116         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34117         
34118         this.monthField = new Roo.bootstrap.MonthField({
34119             after : '<i class=\"fa fa-calendar\"></i>',
34120             allowBlank : this.monthAllowBlank,
34121             placeholder : this.monthPlaceholder,
34122             readOnly : true,
34123             listeners : {
34124                 render : function (_self)
34125                 {
34126                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34127                         e.preventDefault();
34128                         _self.focus();
34129                     });
34130                 },
34131                 select : function (_self, oldvalue, newvalue)
34132                 {
34133                     _this.setValue(_this.getValue());
34134                 }
34135             }
34136         });
34137         
34138         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34139         
34140         this.yearField = new Roo.bootstrap.ComboBox({
34141             allowBlank : this.yearAllowBlank,
34142             alwaysQuery : true,
34143             displayField : 'value',
34144             editable : false,
34145             fieldLabel : '',
34146             forceSelection : true,
34147             mode : 'local',
34148             placeholder : this.yearPlaceholder,
34149             selectOnFocus : true,
34150             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34151             triggerAction : 'all',
34152             typeAhead : true,
34153             valueField : 'value',
34154             store : new Roo.data.SimpleStore({
34155                 data : (function() {
34156                     var years = [];
34157                     _this.fireEvent('years', _this, years);
34158                     return years;
34159                 })(),
34160                 fields : [ 'value' ]
34161             }),
34162             listeners : {
34163                 select : function (_self, record, index)
34164                 {
34165                     _this.setValue(_this.getValue());
34166                 }
34167             }
34168         });
34169
34170         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34171     },
34172     
34173     setValue : function(v, format)
34174     {
34175         this.inputEl.dom.value = v;
34176         
34177         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34178         
34179         var d = Date.parseDate(v, f);
34180         
34181         if(!d){
34182             this.validate();
34183             return;
34184         }
34185         
34186         this.setDay(d.format(this.dayFormat));
34187         this.setMonth(d.format(this.monthFormat));
34188         this.setYear(d.format(this.yearFormat));
34189         
34190         this.validate();
34191         
34192         return;
34193     },
34194     
34195     setDay : function(v)
34196     {
34197         this.dayField.setValue(v);
34198         this.inputEl.dom.value = this.getValue();
34199         this.validate();
34200         return;
34201     },
34202     
34203     setMonth : function(v)
34204     {
34205         this.monthField.setValue(v, true);
34206         this.inputEl.dom.value = this.getValue();
34207         this.validate();
34208         return;
34209     },
34210     
34211     setYear : function(v)
34212     {
34213         this.yearField.setValue(v);
34214         this.inputEl.dom.value = this.getValue();
34215         this.validate();
34216         return;
34217     },
34218     
34219     getDay : function()
34220     {
34221         return this.dayField.getValue();
34222     },
34223     
34224     getMonth : function()
34225     {
34226         return this.monthField.getValue();
34227     },
34228     
34229     getYear : function()
34230     {
34231         return this.yearField.getValue();
34232     },
34233     
34234     getValue : function()
34235     {
34236         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34237         
34238         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34239         
34240         return date;
34241     },
34242     
34243     reset : function()
34244     {
34245         this.setDay('');
34246         this.setMonth('');
34247         this.setYear('');
34248         this.inputEl.dom.value = '';
34249         this.validate();
34250         return;
34251     },
34252     
34253     validate : function()
34254     {
34255         var d = this.dayField.validate();
34256         var m = this.monthField.validate();
34257         var y = this.yearField.validate();
34258         
34259         var valid = true;
34260         
34261         if(
34262                 (!this.dayAllowBlank && !d) ||
34263                 (!this.monthAllowBlank && !m) ||
34264                 (!this.yearAllowBlank && !y)
34265         ){
34266             valid = false;
34267         }
34268         
34269         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34270             return valid;
34271         }
34272         
34273         if(valid){
34274             this.markValid();
34275             return valid;
34276         }
34277         
34278         this.markInvalid();
34279         
34280         return valid;
34281     },
34282     
34283     markValid : function()
34284     {
34285         
34286         var label = this.el.select('label', true).first();
34287         var icon = this.el.select('i.fa-star', true).first();
34288
34289         if(label && icon){
34290             icon.remove();
34291         }
34292         
34293         this.fireEvent('valid', this);
34294     },
34295     
34296      /**
34297      * Mark this field as invalid
34298      * @param {String} msg The validation message
34299      */
34300     markInvalid : function(msg)
34301     {
34302         
34303         var label = this.el.select('label', true).first();
34304         var icon = this.el.select('i.fa-star', true).first();
34305
34306         if(label && !icon){
34307             this.el.select('.roo-date-split-field-label', true).createChild({
34308                 tag : 'i',
34309                 cls : 'text-danger fa fa-lg fa-star',
34310                 tooltip : 'This field is required',
34311                 style : 'margin-right:5px;'
34312             }, label, true);
34313         }
34314         
34315         this.fireEvent('invalid', this, msg);
34316     },
34317     
34318     clearInvalid : function()
34319     {
34320         var label = this.el.select('label', true).first();
34321         var icon = this.el.select('i.fa-star', true).first();
34322
34323         if(label && icon){
34324             icon.remove();
34325         }
34326         
34327         this.fireEvent('valid', this);
34328     },
34329     
34330     getName: function()
34331     {
34332         return this.name;
34333     }
34334     
34335 });
34336
34337  /**
34338  *
34339  * This is based on 
34340  * http://masonry.desandro.com
34341  *
34342  * The idea is to render all the bricks based on vertical width...
34343  *
34344  * The original code extends 'outlayer' - we might need to use that....
34345  * 
34346  */
34347
34348
34349 /**
34350  * @class Roo.bootstrap.LayoutMasonry
34351  * @extends Roo.bootstrap.Component
34352  * Bootstrap Layout Masonry class
34353  * 
34354  * @constructor
34355  * Create a new Element
34356  * @param {Object} config The config object
34357  */
34358
34359 Roo.bootstrap.LayoutMasonry = function(config){
34360     
34361     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34362     
34363     this.bricks = [];
34364     
34365     Roo.bootstrap.LayoutMasonry.register(this);
34366     
34367     this.addEvents({
34368         // raw events
34369         /**
34370          * @event layout
34371          * Fire after layout the items
34372          * @param {Roo.bootstrap.LayoutMasonry} this
34373          * @param {Roo.EventObject} e
34374          */
34375         "layout" : true
34376     });
34377     
34378 };
34379
34380 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34381     
34382     /**
34383      * @cfg {Boolean} isLayoutInstant = no animation?
34384      */   
34385     isLayoutInstant : false, // needed?
34386    
34387     /**
34388      * @cfg {Number} boxWidth  width of the columns
34389      */   
34390     boxWidth : 450,
34391     
34392       /**
34393      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34394      */   
34395     boxHeight : 0,
34396     
34397     /**
34398      * @cfg {Number} padWidth padding below box..
34399      */   
34400     padWidth : 10, 
34401     
34402     /**
34403      * @cfg {Number} gutter gutter width..
34404      */   
34405     gutter : 10,
34406     
34407      /**
34408      * @cfg {Number} maxCols maximum number of columns
34409      */   
34410     
34411     maxCols: 0,
34412     
34413     /**
34414      * @cfg {Boolean} isAutoInitial defalut true
34415      */   
34416     isAutoInitial : true, 
34417     
34418     containerWidth: 0,
34419     
34420     /**
34421      * @cfg {Boolean} isHorizontal defalut false
34422      */   
34423     isHorizontal : false, 
34424
34425     currentSize : null,
34426     
34427     tag: 'div',
34428     
34429     cls: '',
34430     
34431     bricks: null, //CompositeElement
34432     
34433     cols : 1,
34434     
34435     _isLayoutInited : false,
34436     
34437 //    isAlternative : false, // only use for vertical layout...
34438     
34439     /**
34440      * @cfg {Number} alternativePadWidth padding below box..
34441      */   
34442     alternativePadWidth : 50,
34443     
34444     selectedBrick : [],
34445     
34446     getAutoCreate : function(){
34447         
34448         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34449         
34450         var cfg = {
34451             tag: this.tag,
34452             cls: 'blog-masonary-wrapper ' + this.cls,
34453             cn : {
34454                 cls : 'mas-boxes masonary'
34455             }
34456         };
34457         
34458         return cfg;
34459     },
34460     
34461     getChildContainer: function( )
34462     {
34463         if (this.boxesEl) {
34464             return this.boxesEl;
34465         }
34466         
34467         this.boxesEl = this.el.select('.mas-boxes').first();
34468         
34469         return this.boxesEl;
34470     },
34471     
34472     
34473     initEvents : function()
34474     {
34475         var _this = this;
34476         
34477         if(this.isAutoInitial){
34478             Roo.log('hook children rendered');
34479             this.on('childrenrendered', function() {
34480                 Roo.log('children rendered');
34481                 _this.initial();
34482             } ,this);
34483         }
34484     },
34485     
34486     initial : function()
34487     {
34488         this.selectedBrick = [];
34489         
34490         this.currentSize = this.el.getBox(true);
34491         
34492         Roo.EventManager.onWindowResize(this.resize, this); 
34493
34494         if(!this.isAutoInitial){
34495             this.layout();
34496             return;
34497         }
34498         
34499         this.layout();
34500         
34501         return;
34502         //this.layout.defer(500,this);
34503         
34504     },
34505     
34506     resize : function()
34507     {
34508         var cs = this.el.getBox(true);
34509         
34510         if (
34511                 this.currentSize.width == cs.width && 
34512                 this.currentSize.x == cs.x && 
34513                 this.currentSize.height == cs.height && 
34514                 this.currentSize.y == cs.y 
34515         ) {
34516             Roo.log("no change in with or X or Y");
34517             return;
34518         }
34519         
34520         this.currentSize = cs;
34521         
34522         this.layout();
34523         
34524     },
34525     
34526     layout : function()
34527     {   
34528         this._resetLayout();
34529         
34530         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34531         
34532         this.layoutItems( isInstant );
34533       
34534         this._isLayoutInited = true;
34535         
34536         this.fireEvent('layout', this);
34537         
34538     },
34539     
34540     _resetLayout : function()
34541     {
34542         if(this.isHorizontal){
34543             this.horizontalMeasureColumns();
34544             return;
34545         }
34546         
34547         this.verticalMeasureColumns();
34548         
34549     },
34550     
34551     verticalMeasureColumns : function()
34552     {
34553         this.getContainerWidth();
34554         
34555 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34556 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34557 //            return;
34558 //        }
34559         
34560         var boxWidth = this.boxWidth + this.padWidth;
34561         
34562         if(this.containerWidth < this.boxWidth){
34563             boxWidth = this.containerWidth
34564         }
34565         
34566         var containerWidth = this.containerWidth;
34567         
34568         var cols = Math.floor(containerWidth / boxWidth);
34569         
34570         this.cols = Math.max( cols, 1 );
34571         
34572         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34573         
34574         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34575         
34576         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34577         
34578         this.colWidth = boxWidth + avail - this.padWidth;
34579         
34580         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34581         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34582     },
34583     
34584     horizontalMeasureColumns : function()
34585     {
34586         this.getContainerWidth();
34587         
34588         var boxWidth = this.boxWidth;
34589         
34590         if(this.containerWidth < boxWidth){
34591             boxWidth = this.containerWidth;
34592         }
34593         
34594         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34595         
34596         this.el.setHeight(boxWidth);
34597         
34598     },
34599     
34600     getContainerWidth : function()
34601     {
34602         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34603     },
34604     
34605     layoutItems : function( isInstant )
34606     {
34607         Roo.log(this.bricks);
34608         
34609         var items = Roo.apply([], this.bricks);
34610         
34611         if(this.isHorizontal){
34612             this._horizontalLayoutItems( items , isInstant );
34613             return;
34614         }
34615         
34616 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34617 //            this._verticalAlternativeLayoutItems( items , isInstant );
34618 //            return;
34619 //        }
34620         
34621         this._verticalLayoutItems( items , isInstant );
34622         
34623     },
34624     
34625     _verticalLayoutItems : function ( items , isInstant)
34626     {
34627         if ( !items || !items.length ) {
34628             return;
34629         }
34630         
34631         var standard = [
34632             ['xs', 'xs', 'xs', 'tall'],
34633             ['xs', 'xs', 'tall'],
34634             ['xs', 'xs', 'sm'],
34635             ['xs', 'xs', 'xs'],
34636             ['xs', 'tall'],
34637             ['xs', 'sm'],
34638             ['xs', 'xs'],
34639             ['xs'],
34640             
34641             ['sm', 'xs', 'xs'],
34642             ['sm', 'xs'],
34643             ['sm'],
34644             
34645             ['tall', 'xs', 'xs', 'xs'],
34646             ['tall', 'xs', 'xs'],
34647             ['tall', 'xs'],
34648             ['tall']
34649             
34650         ];
34651         
34652         var queue = [];
34653         
34654         var boxes = [];
34655         
34656         var box = [];
34657         
34658         Roo.each(items, function(item, k){
34659             
34660             switch (item.size) {
34661                 // these layouts take up a full box,
34662                 case 'md' :
34663                 case 'md-left' :
34664                 case 'md-right' :
34665                 case 'wide' :
34666                     
34667                     if(box.length){
34668                         boxes.push(box);
34669                         box = [];
34670                     }
34671                     
34672                     boxes.push([item]);
34673                     
34674                     break;
34675                     
34676                 case 'xs' :
34677                 case 'sm' :
34678                 case 'tall' :
34679                     
34680                     box.push(item);
34681                     
34682                     break;
34683                 default :
34684                     break;
34685                     
34686             }
34687             
34688         }, this);
34689         
34690         if(box.length){
34691             boxes.push(box);
34692             box = [];
34693         }
34694         
34695         var filterPattern = function(box, length)
34696         {
34697             if(!box.length){
34698                 return;
34699             }
34700             
34701             var match = false;
34702             
34703             var pattern = box.slice(0, length);
34704             
34705             var format = [];
34706             
34707             Roo.each(pattern, function(i){
34708                 format.push(i.size);
34709             }, this);
34710             
34711             Roo.each(standard, function(s){
34712                 
34713                 if(String(s) != String(format)){
34714                     return;
34715                 }
34716                 
34717                 match = true;
34718                 return false;
34719                 
34720             }, this);
34721             
34722             if(!match && length == 1){
34723                 return;
34724             }
34725             
34726             if(!match){
34727                 filterPattern(box, length - 1);
34728                 return;
34729             }
34730                 
34731             queue.push(pattern);
34732
34733             box = box.slice(length, box.length);
34734
34735             filterPattern(box, 4);
34736
34737             return;
34738             
34739         }
34740         
34741         Roo.each(boxes, function(box, k){
34742             
34743             if(!box.length){
34744                 return;
34745             }
34746             
34747             if(box.length == 1){
34748                 queue.push(box);
34749                 return;
34750             }
34751             
34752             filterPattern(box, 4);
34753             
34754         }, this);
34755         
34756         this._processVerticalLayoutQueue( queue, isInstant );
34757         
34758     },
34759     
34760 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34761 //    {
34762 //        if ( !items || !items.length ) {
34763 //            return;
34764 //        }
34765 //
34766 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34767 //        
34768 //    },
34769     
34770     _horizontalLayoutItems : function ( items , isInstant)
34771     {
34772         if ( !items || !items.length || items.length < 3) {
34773             return;
34774         }
34775         
34776         items.reverse();
34777         
34778         var eItems = items.slice(0, 3);
34779         
34780         items = items.slice(3, items.length);
34781         
34782         var standard = [
34783             ['xs', 'xs', 'xs', 'wide'],
34784             ['xs', 'xs', 'wide'],
34785             ['xs', 'xs', 'sm'],
34786             ['xs', 'xs', 'xs'],
34787             ['xs', 'wide'],
34788             ['xs', 'sm'],
34789             ['xs', 'xs'],
34790             ['xs'],
34791             
34792             ['sm', 'xs', 'xs'],
34793             ['sm', 'xs'],
34794             ['sm'],
34795             
34796             ['wide', 'xs', 'xs', 'xs'],
34797             ['wide', 'xs', 'xs'],
34798             ['wide', 'xs'],
34799             ['wide'],
34800             
34801             ['wide-thin']
34802         ];
34803         
34804         var queue = [];
34805         
34806         var boxes = [];
34807         
34808         var box = [];
34809         
34810         Roo.each(items, function(item, k){
34811             
34812             switch (item.size) {
34813                 case 'md' :
34814                 case 'md-left' :
34815                 case 'md-right' :
34816                 case 'tall' :
34817                     
34818                     if(box.length){
34819                         boxes.push(box);
34820                         box = [];
34821                     }
34822                     
34823                     boxes.push([item]);
34824                     
34825                     break;
34826                     
34827                 case 'xs' :
34828                 case 'sm' :
34829                 case 'wide' :
34830                 case 'wide-thin' :
34831                     
34832                     box.push(item);
34833                     
34834                     break;
34835                 default :
34836                     break;
34837                     
34838             }
34839             
34840         }, this);
34841         
34842         if(box.length){
34843             boxes.push(box);
34844             box = [];
34845         }
34846         
34847         var filterPattern = function(box, length)
34848         {
34849             if(!box.length){
34850                 return;
34851             }
34852             
34853             var match = false;
34854             
34855             var pattern = box.slice(0, length);
34856             
34857             var format = [];
34858             
34859             Roo.each(pattern, function(i){
34860                 format.push(i.size);
34861             }, this);
34862             
34863             Roo.each(standard, function(s){
34864                 
34865                 if(String(s) != String(format)){
34866                     return;
34867                 }
34868                 
34869                 match = true;
34870                 return false;
34871                 
34872             }, this);
34873             
34874             if(!match && length == 1){
34875                 return;
34876             }
34877             
34878             if(!match){
34879                 filterPattern(box, length - 1);
34880                 return;
34881             }
34882                 
34883             queue.push(pattern);
34884
34885             box = box.slice(length, box.length);
34886
34887             filterPattern(box, 4);
34888
34889             return;
34890             
34891         }
34892         
34893         Roo.each(boxes, function(box, k){
34894             
34895             if(!box.length){
34896                 return;
34897             }
34898             
34899             if(box.length == 1){
34900                 queue.push(box);
34901                 return;
34902             }
34903             
34904             filterPattern(box, 4);
34905             
34906         }, this);
34907         
34908         
34909         var prune = [];
34910         
34911         var pos = this.el.getBox(true);
34912         
34913         var minX = pos.x;
34914         
34915         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34916         
34917         var hit_end = false;
34918         
34919         Roo.each(queue, function(box){
34920             
34921             if(hit_end){
34922                 
34923                 Roo.each(box, function(b){
34924                 
34925                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34926                     b.el.hide();
34927
34928                 }, this);
34929
34930                 return;
34931             }
34932             
34933             var mx = 0;
34934             
34935             Roo.each(box, function(b){
34936                 
34937                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34938                 b.el.show();
34939
34940                 mx = Math.max(mx, b.x);
34941                 
34942             }, this);
34943             
34944             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34945             
34946             if(maxX < minX){
34947                 
34948                 Roo.each(box, function(b){
34949                 
34950                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34951                     b.el.hide();
34952                     
34953                 }, this);
34954                 
34955                 hit_end = true;
34956                 
34957                 return;
34958             }
34959             
34960             prune.push(box);
34961             
34962         }, this);
34963         
34964         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34965     },
34966     
34967     /** Sets position of item in DOM
34968     * @param {Element} item
34969     * @param {Number} x - horizontal position
34970     * @param {Number} y - vertical position
34971     * @param {Boolean} isInstant - disables transitions
34972     */
34973     _processVerticalLayoutQueue : function( queue, isInstant )
34974     {
34975         var pos = this.el.getBox(true);
34976         var x = pos.x;
34977         var y = pos.y;
34978         var maxY = [];
34979         
34980         for (var i = 0; i < this.cols; i++){
34981             maxY[i] = pos.y;
34982         }
34983         
34984         Roo.each(queue, function(box, k){
34985             
34986             var col = k % this.cols;
34987             
34988             Roo.each(box, function(b,kk){
34989                 
34990                 b.el.position('absolute');
34991                 
34992                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34993                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34994                 
34995                 if(b.size == 'md-left' || b.size == 'md-right'){
34996                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34997                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34998                 }
34999                 
35000                 b.el.setWidth(width);
35001                 b.el.setHeight(height);
35002                 // iframe?
35003                 b.el.select('iframe',true).setSize(width,height);
35004                 
35005             }, this);
35006             
35007             for (var i = 0; i < this.cols; i++){
35008                 
35009                 if(maxY[i] < maxY[col]){
35010                     col = i;
35011                     continue;
35012                 }
35013                 
35014                 col = Math.min(col, i);
35015                 
35016             }
35017             
35018             x = pos.x + col * (this.colWidth + this.padWidth);
35019             
35020             y = maxY[col];
35021             
35022             var positions = [];
35023             
35024             switch (box.length){
35025                 case 1 :
35026                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35027                     break;
35028                 case 2 :
35029                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35030                     break;
35031                 case 3 :
35032                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35033                     break;
35034                 case 4 :
35035                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35036                     break;
35037                 default :
35038                     break;
35039             }
35040             
35041             Roo.each(box, function(b,kk){
35042                 
35043                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35044                 
35045                 var sz = b.el.getSize();
35046                 
35047                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35048                 
35049             }, this);
35050             
35051         }, this);
35052         
35053         var mY = 0;
35054         
35055         for (var i = 0; i < this.cols; i++){
35056             mY = Math.max(mY, maxY[i]);
35057         }
35058         
35059         this.el.setHeight(mY - pos.y);
35060         
35061     },
35062     
35063 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35064 //    {
35065 //        var pos = this.el.getBox(true);
35066 //        var x = pos.x;
35067 //        var y = pos.y;
35068 //        var maxX = pos.right;
35069 //        
35070 //        var maxHeight = 0;
35071 //        
35072 //        Roo.each(items, function(item, k){
35073 //            
35074 //            var c = k % 2;
35075 //            
35076 //            item.el.position('absolute');
35077 //                
35078 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35079 //
35080 //            item.el.setWidth(width);
35081 //
35082 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35083 //
35084 //            item.el.setHeight(height);
35085 //            
35086 //            if(c == 0){
35087 //                item.el.setXY([x, y], isInstant ? false : true);
35088 //            } else {
35089 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35090 //            }
35091 //            
35092 //            y = y + height + this.alternativePadWidth;
35093 //            
35094 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35095 //            
35096 //        }, this);
35097 //        
35098 //        this.el.setHeight(maxHeight);
35099 //        
35100 //    },
35101     
35102     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35103     {
35104         var pos = this.el.getBox(true);
35105         
35106         var minX = pos.x;
35107         var minY = pos.y;
35108         
35109         var maxX = pos.right;
35110         
35111         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35112         
35113         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35114         
35115         Roo.each(queue, function(box, k){
35116             
35117             Roo.each(box, function(b, kk){
35118                 
35119                 b.el.position('absolute');
35120                 
35121                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35122                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35123                 
35124                 if(b.size == 'md-left' || b.size == 'md-right'){
35125                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35126                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35127                 }
35128                 
35129                 b.el.setWidth(width);
35130                 b.el.setHeight(height);
35131                 
35132             }, this);
35133             
35134             if(!box.length){
35135                 return;
35136             }
35137             
35138             var positions = [];
35139             
35140             switch (box.length){
35141                 case 1 :
35142                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35143                     break;
35144                 case 2 :
35145                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35146                     break;
35147                 case 3 :
35148                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35149                     break;
35150                 case 4 :
35151                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35152                     break;
35153                 default :
35154                     break;
35155             }
35156             
35157             Roo.each(box, function(b,kk){
35158                 
35159                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35160                 
35161                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35162                 
35163             }, this);
35164             
35165         }, this);
35166         
35167     },
35168     
35169     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35170     {
35171         Roo.each(eItems, function(b,k){
35172             
35173             b.size = (k == 0) ? 'sm' : 'xs';
35174             b.x = (k == 0) ? 2 : 1;
35175             b.y = (k == 0) ? 2 : 1;
35176             
35177             b.el.position('absolute');
35178             
35179             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35180                 
35181             b.el.setWidth(width);
35182             
35183             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35184             
35185             b.el.setHeight(height);
35186             
35187         }, this);
35188
35189         var positions = [];
35190         
35191         positions.push({
35192             x : maxX - this.unitWidth * 2 - this.gutter,
35193             y : minY
35194         });
35195         
35196         positions.push({
35197             x : maxX - this.unitWidth,
35198             y : minY + (this.unitWidth + this.gutter) * 2
35199         });
35200         
35201         positions.push({
35202             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35203             y : minY
35204         });
35205         
35206         Roo.each(eItems, function(b,k){
35207             
35208             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35209
35210         }, this);
35211         
35212     },
35213     
35214     getVerticalOneBoxColPositions : function(x, y, box)
35215     {
35216         var pos = [];
35217         
35218         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35219         
35220         if(box[0].size == 'md-left'){
35221             rand = 0;
35222         }
35223         
35224         if(box[0].size == 'md-right'){
35225             rand = 1;
35226         }
35227         
35228         pos.push({
35229             x : x + (this.unitWidth + this.gutter) * rand,
35230             y : y
35231         });
35232         
35233         return pos;
35234     },
35235     
35236     getVerticalTwoBoxColPositions : function(x, y, box)
35237     {
35238         var pos = [];
35239         
35240         if(box[0].size == 'xs'){
35241             
35242             pos.push({
35243                 x : x,
35244                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35245             });
35246
35247             pos.push({
35248                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35249                 y : y
35250             });
35251             
35252             return pos;
35253             
35254         }
35255         
35256         pos.push({
35257             x : x,
35258             y : y
35259         });
35260
35261         pos.push({
35262             x : x + (this.unitWidth + this.gutter) * 2,
35263             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35264         });
35265         
35266         return pos;
35267         
35268     },
35269     
35270     getVerticalThreeBoxColPositions : function(x, y, box)
35271     {
35272         var pos = [];
35273         
35274         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35275             
35276             pos.push({
35277                 x : x,
35278                 y : y
35279             });
35280
35281             pos.push({
35282                 x : x + (this.unitWidth + this.gutter) * 1,
35283                 y : y
35284             });
35285             
35286             pos.push({
35287                 x : x + (this.unitWidth + this.gutter) * 2,
35288                 y : y
35289             });
35290             
35291             return pos;
35292             
35293         }
35294         
35295         if(box[0].size == 'xs' && box[1].size == 'xs'){
35296             
35297             pos.push({
35298                 x : x,
35299                 y : y
35300             });
35301
35302             pos.push({
35303                 x : x,
35304                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35305             });
35306             
35307             pos.push({
35308                 x : x + (this.unitWidth + this.gutter) * 1,
35309                 y : y
35310             });
35311             
35312             return pos;
35313             
35314         }
35315         
35316         pos.push({
35317             x : x,
35318             y : y
35319         });
35320
35321         pos.push({
35322             x : x + (this.unitWidth + this.gutter) * 2,
35323             y : y
35324         });
35325
35326         pos.push({
35327             x : x + (this.unitWidth + this.gutter) * 2,
35328             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35329         });
35330             
35331         return pos;
35332         
35333     },
35334     
35335     getVerticalFourBoxColPositions : function(x, y, box)
35336     {
35337         var pos = [];
35338         
35339         if(box[0].size == 'xs'){
35340             
35341             pos.push({
35342                 x : x,
35343                 y : y
35344             });
35345
35346             pos.push({
35347                 x : x,
35348                 y : y + (this.unitHeight + this.gutter) * 1
35349             });
35350             
35351             pos.push({
35352                 x : x,
35353                 y : y + (this.unitHeight + this.gutter) * 2
35354             });
35355             
35356             pos.push({
35357                 x : x + (this.unitWidth + this.gutter) * 1,
35358                 y : y
35359             });
35360             
35361             return pos;
35362             
35363         }
35364         
35365         pos.push({
35366             x : x,
35367             y : y
35368         });
35369
35370         pos.push({
35371             x : x + (this.unitWidth + this.gutter) * 2,
35372             y : y
35373         });
35374
35375         pos.push({
35376             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35377             y : y + (this.unitHeight + this.gutter) * 1
35378         });
35379
35380         pos.push({
35381             x : x + (this.unitWidth + this.gutter) * 2,
35382             y : y + (this.unitWidth + this.gutter) * 2
35383         });
35384
35385         return pos;
35386         
35387     },
35388     
35389     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35390     {
35391         var pos = [];
35392         
35393         if(box[0].size == 'md-left'){
35394             pos.push({
35395                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35396                 y : minY
35397             });
35398             
35399             return pos;
35400         }
35401         
35402         if(box[0].size == 'md-right'){
35403             pos.push({
35404                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35405                 y : minY + (this.unitWidth + this.gutter) * 1
35406             });
35407             
35408             return pos;
35409         }
35410         
35411         var rand = Math.floor(Math.random() * (4 - box[0].y));
35412         
35413         pos.push({
35414             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35415             y : minY + (this.unitWidth + this.gutter) * rand
35416         });
35417         
35418         return pos;
35419         
35420     },
35421     
35422     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35423     {
35424         var pos = [];
35425         
35426         if(box[0].size == 'xs'){
35427             
35428             pos.push({
35429                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35430                 y : minY
35431             });
35432
35433             pos.push({
35434                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35435                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35436             });
35437             
35438             return pos;
35439             
35440         }
35441         
35442         pos.push({
35443             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35444             y : minY
35445         });
35446
35447         pos.push({
35448             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35449             y : minY + (this.unitWidth + this.gutter) * 2
35450         });
35451         
35452         return pos;
35453         
35454     },
35455     
35456     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35457     {
35458         var pos = [];
35459         
35460         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35461             
35462             pos.push({
35463                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35464                 y : minY
35465             });
35466
35467             pos.push({
35468                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35469                 y : minY + (this.unitWidth + this.gutter) * 1
35470             });
35471             
35472             pos.push({
35473                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35474                 y : minY + (this.unitWidth + this.gutter) * 2
35475             });
35476             
35477             return pos;
35478             
35479         }
35480         
35481         if(box[0].size == 'xs' && box[1].size == 'xs'){
35482             
35483             pos.push({
35484                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35485                 y : minY
35486             });
35487
35488             pos.push({
35489                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35490                 y : minY
35491             });
35492             
35493             pos.push({
35494                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35495                 y : minY + (this.unitWidth + this.gutter) * 1
35496             });
35497             
35498             return pos;
35499             
35500         }
35501         
35502         pos.push({
35503             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35504             y : minY
35505         });
35506
35507         pos.push({
35508             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35509             y : minY + (this.unitWidth + this.gutter) * 2
35510         });
35511
35512         pos.push({
35513             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35514             y : minY + (this.unitWidth + this.gutter) * 2
35515         });
35516             
35517         return pos;
35518         
35519     },
35520     
35521     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35522     {
35523         var pos = [];
35524         
35525         if(box[0].size == 'xs'){
35526             
35527             pos.push({
35528                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35529                 y : minY
35530             });
35531
35532             pos.push({
35533                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35534                 y : minY
35535             });
35536             
35537             pos.push({
35538                 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),
35539                 y : minY
35540             });
35541             
35542             pos.push({
35543                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35544                 y : minY + (this.unitWidth + this.gutter) * 1
35545             });
35546             
35547             return pos;
35548             
35549         }
35550         
35551         pos.push({
35552             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35553             y : minY
35554         });
35555         
35556         pos.push({
35557             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35558             y : minY + (this.unitWidth + this.gutter) * 2
35559         });
35560         
35561         pos.push({
35562             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35563             y : minY + (this.unitWidth + this.gutter) * 2
35564         });
35565         
35566         pos.push({
35567             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),
35568             y : minY + (this.unitWidth + this.gutter) * 2
35569         });
35570
35571         return pos;
35572         
35573     },
35574     
35575     /**
35576     * remove a Masonry Brick
35577     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35578     */
35579     removeBrick : function(brick_id)
35580     {
35581         if (!brick_id) {
35582             return;
35583         }
35584         
35585         for (var i = 0; i<this.bricks.length; i++) {
35586             if (this.bricks[i].id == brick_id) {
35587                 this.bricks.splice(i,1);
35588                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35589                 this.initial();
35590             }
35591         }
35592     },
35593     
35594     /**
35595     * adds a Masonry Brick
35596     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35597     */
35598     addBrick : function(cfg)
35599     {
35600         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35601         //this.register(cn);
35602         cn.parentId = this.id;
35603         cn.render(this.el);
35604         return cn;
35605     },
35606     
35607     /**
35608     * register a Masonry Brick
35609     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35610     */
35611     
35612     register : function(brick)
35613     {
35614         this.bricks.push(brick);
35615         brick.masonryId = this.id;
35616     },
35617     
35618     /**
35619     * clear all the Masonry Brick
35620     */
35621     clearAll : function()
35622     {
35623         this.bricks = [];
35624         //this.getChildContainer().dom.innerHTML = "";
35625         this.el.dom.innerHTML = '';
35626     },
35627     
35628     getSelected : function()
35629     {
35630         if (!this.selectedBrick) {
35631             return false;
35632         }
35633         
35634         return this.selectedBrick;
35635     }
35636 });
35637
35638 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35639     
35640     groups: {},
35641      /**
35642     * register a Masonry Layout
35643     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35644     */
35645     
35646     register : function(layout)
35647     {
35648         this.groups[layout.id] = layout;
35649     },
35650     /**
35651     * fetch a  Masonry Layout based on the masonry layout ID
35652     * @param {string} the masonry layout to add
35653     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35654     */
35655     
35656     get: function(layout_id) {
35657         if (typeof(this.groups[layout_id]) == 'undefined') {
35658             return false;
35659         }
35660         return this.groups[layout_id] ;
35661     }
35662     
35663     
35664     
35665 });
35666
35667  
35668
35669  /**
35670  *
35671  * This is based on 
35672  * http://masonry.desandro.com
35673  *
35674  * The idea is to render all the bricks based on vertical width...
35675  *
35676  * The original code extends 'outlayer' - we might need to use that....
35677  * 
35678  */
35679
35680
35681 /**
35682  * @class Roo.bootstrap.LayoutMasonryAuto
35683  * @extends Roo.bootstrap.Component
35684  * Bootstrap Layout Masonry class
35685  * 
35686  * @constructor
35687  * Create a new Element
35688  * @param {Object} config The config object
35689  */
35690
35691 Roo.bootstrap.LayoutMasonryAuto = function(config){
35692     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35693 };
35694
35695 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35696     
35697       /**
35698      * @cfg {Boolean} isFitWidth  - resize the width..
35699      */   
35700     isFitWidth : false,  // options..
35701     /**
35702      * @cfg {Boolean} isOriginLeft = left align?
35703      */   
35704     isOriginLeft : true,
35705     /**
35706      * @cfg {Boolean} isOriginTop = top align?
35707      */   
35708     isOriginTop : false,
35709     /**
35710      * @cfg {Boolean} isLayoutInstant = no animation?
35711      */   
35712     isLayoutInstant : false, // needed?
35713     /**
35714      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35715      */   
35716     isResizingContainer : true,
35717     /**
35718      * @cfg {Number} columnWidth  width of the columns 
35719      */   
35720     
35721     columnWidth : 0,
35722     
35723     /**
35724      * @cfg {Number} maxCols maximum number of columns
35725      */   
35726     
35727     maxCols: 0,
35728     /**
35729      * @cfg {Number} padHeight padding below box..
35730      */   
35731     
35732     padHeight : 10, 
35733     
35734     /**
35735      * @cfg {Boolean} isAutoInitial defalut true
35736      */   
35737     
35738     isAutoInitial : true, 
35739     
35740     // private?
35741     gutter : 0,
35742     
35743     containerWidth: 0,
35744     initialColumnWidth : 0,
35745     currentSize : null,
35746     
35747     colYs : null, // array.
35748     maxY : 0,
35749     padWidth: 10,
35750     
35751     
35752     tag: 'div',
35753     cls: '',
35754     bricks: null, //CompositeElement
35755     cols : 0, // array?
35756     // element : null, // wrapped now this.el
35757     _isLayoutInited : null, 
35758     
35759     
35760     getAutoCreate : function(){
35761         
35762         var cfg = {
35763             tag: this.tag,
35764             cls: 'blog-masonary-wrapper ' + this.cls,
35765             cn : {
35766                 cls : 'mas-boxes masonary'
35767             }
35768         };
35769         
35770         return cfg;
35771     },
35772     
35773     getChildContainer: function( )
35774     {
35775         if (this.boxesEl) {
35776             return this.boxesEl;
35777         }
35778         
35779         this.boxesEl = this.el.select('.mas-boxes').first();
35780         
35781         return this.boxesEl;
35782     },
35783     
35784     
35785     initEvents : function()
35786     {
35787         var _this = this;
35788         
35789         if(this.isAutoInitial){
35790             Roo.log('hook children rendered');
35791             this.on('childrenrendered', function() {
35792                 Roo.log('children rendered');
35793                 _this.initial();
35794             } ,this);
35795         }
35796         
35797     },
35798     
35799     initial : function()
35800     {
35801         this.reloadItems();
35802
35803         this.currentSize = this.el.getBox(true);
35804
35805         /// was window resize... - let's see if this works..
35806         Roo.EventManager.onWindowResize(this.resize, this); 
35807
35808         if(!this.isAutoInitial){
35809             this.layout();
35810             return;
35811         }
35812         
35813         this.layout.defer(500,this);
35814     },
35815     
35816     reloadItems: function()
35817     {
35818         this.bricks = this.el.select('.masonry-brick', true);
35819         
35820         this.bricks.each(function(b) {
35821             //Roo.log(b.getSize());
35822             if (!b.attr('originalwidth')) {
35823                 b.attr('originalwidth',  b.getSize().width);
35824             }
35825             
35826         });
35827         
35828         Roo.log(this.bricks.elements.length);
35829     },
35830     
35831     resize : function()
35832     {
35833         Roo.log('resize');
35834         var cs = this.el.getBox(true);
35835         
35836         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35837             Roo.log("no change in with or X");
35838             return;
35839         }
35840         this.currentSize = cs;
35841         this.layout();
35842     },
35843     
35844     layout : function()
35845     {
35846          Roo.log('layout');
35847         this._resetLayout();
35848         //this._manageStamps();
35849       
35850         // don't animate first layout
35851         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35852         this.layoutItems( isInstant );
35853       
35854         // flag for initalized
35855         this._isLayoutInited = true;
35856     },
35857     
35858     layoutItems : function( isInstant )
35859     {
35860         //var items = this._getItemsForLayout( this.items );
35861         // original code supports filtering layout items.. we just ignore it..
35862         
35863         this._layoutItems( this.bricks , isInstant );
35864       
35865         this._postLayout();
35866     },
35867     _layoutItems : function ( items , isInstant)
35868     {
35869        //this.fireEvent( 'layout', this, items );
35870     
35871
35872         if ( !items || !items.elements.length ) {
35873           // no items, emit event with empty array
35874             return;
35875         }
35876
35877         var queue = [];
35878         items.each(function(item) {
35879             Roo.log("layout item");
35880             Roo.log(item);
35881             // get x/y object from method
35882             var position = this._getItemLayoutPosition( item );
35883             // enqueue
35884             position.item = item;
35885             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35886             queue.push( position );
35887         }, this);
35888       
35889         this._processLayoutQueue( queue );
35890     },
35891     /** Sets position of item in DOM
35892     * @param {Element} item
35893     * @param {Number} x - horizontal position
35894     * @param {Number} y - vertical position
35895     * @param {Boolean} isInstant - disables transitions
35896     */
35897     _processLayoutQueue : function( queue )
35898     {
35899         for ( var i=0, len = queue.length; i < len; i++ ) {
35900             var obj = queue[i];
35901             obj.item.position('absolute');
35902             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35903         }
35904     },
35905       
35906     
35907     /**
35908     * Any logic you want to do after each layout,
35909     * i.e. size the container
35910     */
35911     _postLayout : function()
35912     {
35913         this.resizeContainer();
35914     },
35915     
35916     resizeContainer : function()
35917     {
35918         if ( !this.isResizingContainer ) {
35919             return;
35920         }
35921         var size = this._getContainerSize();
35922         if ( size ) {
35923             this.el.setSize(size.width,size.height);
35924             this.boxesEl.setSize(size.width,size.height);
35925         }
35926     },
35927     
35928     
35929     
35930     _resetLayout : function()
35931     {
35932         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35933         this.colWidth = this.el.getWidth();
35934         //this.gutter = this.el.getWidth(); 
35935         
35936         this.measureColumns();
35937
35938         // reset column Y
35939         var i = this.cols;
35940         this.colYs = [];
35941         while (i--) {
35942             this.colYs.push( 0 );
35943         }
35944     
35945         this.maxY = 0;
35946     },
35947
35948     measureColumns : function()
35949     {
35950         this.getContainerWidth();
35951       // if columnWidth is 0, default to outerWidth of first item
35952         if ( !this.columnWidth ) {
35953             var firstItem = this.bricks.first();
35954             Roo.log(firstItem);
35955             this.columnWidth  = this.containerWidth;
35956             if (firstItem && firstItem.attr('originalwidth') ) {
35957                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35958             }
35959             // columnWidth fall back to item of first element
35960             Roo.log("set column width?");
35961                         this.initialColumnWidth = this.columnWidth  ;
35962
35963             // if first elem has no width, default to size of container
35964             
35965         }
35966         
35967         
35968         if (this.initialColumnWidth) {
35969             this.columnWidth = this.initialColumnWidth;
35970         }
35971         
35972         
35973             
35974         // column width is fixed at the top - however if container width get's smaller we should
35975         // reduce it...
35976         
35977         // this bit calcs how man columns..
35978             
35979         var columnWidth = this.columnWidth += this.gutter;
35980       
35981         // calculate columns
35982         var containerWidth = this.containerWidth + this.gutter;
35983         
35984         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35985         // fix rounding errors, typically with gutters
35986         var excess = columnWidth - containerWidth % columnWidth;
35987         
35988         
35989         // if overshoot is less than a pixel, round up, otherwise floor it
35990         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35991         cols = Math[ mathMethod ]( cols );
35992         this.cols = Math.max( cols, 1 );
35993         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35994         
35995          // padding positioning..
35996         var totalColWidth = this.cols * this.columnWidth;
35997         var padavail = this.containerWidth - totalColWidth;
35998         // so for 2 columns - we need 3 'pads'
35999         
36000         var padNeeded = (1+this.cols) * this.padWidth;
36001         
36002         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36003         
36004         this.columnWidth += padExtra
36005         //this.padWidth = Math.floor(padavail /  ( this.cols));
36006         
36007         // adjust colum width so that padding is fixed??
36008         
36009         // we have 3 columns ... total = width * 3
36010         // we have X left over... that should be used by 
36011         
36012         //if (this.expandC) {
36013             
36014         //}
36015         
36016         
36017         
36018     },
36019     
36020     getContainerWidth : function()
36021     {
36022        /* // container is parent if fit width
36023         var container = this.isFitWidth ? this.element.parentNode : this.element;
36024         // check that this.size and size are there
36025         // IE8 triggers resize on body size change, so they might not be
36026         
36027         var size = getSize( container );  //FIXME
36028         this.containerWidth = size && size.innerWidth; //FIXME
36029         */
36030          
36031         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36032         
36033     },
36034     
36035     _getItemLayoutPosition : function( item )  // what is item?
36036     {
36037         // we resize the item to our columnWidth..
36038       
36039         item.setWidth(this.columnWidth);
36040         item.autoBoxAdjust  = false;
36041         
36042         var sz = item.getSize();
36043  
36044         // how many columns does this brick span
36045         var remainder = this.containerWidth % this.columnWidth;
36046         
36047         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36048         // round if off by 1 pixel, otherwise use ceil
36049         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36050         colSpan = Math.min( colSpan, this.cols );
36051         
36052         // normally this should be '1' as we dont' currently allow multi width columns..
36053         
36054         var colGroup = this._getColGroup( colSpan );
36055         // get the minimum Y value from the columns
36056         var minimumY = Math.min.apply( Math, colGroup );
36057         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36058         
36059         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36060          
36061         // position the brick
36062         var position = {
36063             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36064             y: this.currentSize.y + minimumY + this.padHeight
36065         };
36066         
36067         Roo.log(position);
36068         // apply setHeight to necessary columns
36069         var setHeight = minimumY + sz.height + this.padHeight;
36070         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36071         
36072         var setSpan = this.cols + 1 - colGroup.length;
36073         for ( var i = 0; i < setSpan; i++ ) {
36074           this.colYs[ shortColIndex + i ] = setHeight ;
36075         }
36076       
36077         return position;
36078     },
36079     
36080     /**
36081      * @param {Number} colSpan - number of columns the element spans
36082      * @returns {Array} colGroup
36083      */
36084     _getColGroup : function( colSpan )
36085     {
36086         if ( colSpan < 2 ) {
36087           // if brick spans only one column, use all the column Ys
36088           return this.colYs;
36089         }
36090       
36091         var colGroup = [];
36092         // how many different places could this brick fit horizontally
36093         var groupCount = this.cols + 1 - colSpan;
36094         // for each group potential horizontal position
36095         for ( var i = 0; i < groupCount; i++ ) {
36096           // make an array of colY values for that one group
36097           var groupColYs = this.colYs.slice( i, i + colSpan );
36098           // and get the max value of the array
36099           colGroup[i] = Math.max.apply( Math, groupColYs );
36100         }
36101         return colGroup;
36102     },
36103     /*
36104     _manageStamp : function( stamp )
36105     {
36106         var stampSize =  stamp.getSize();
36107         var offset = stamp.getBox();
36108         // get the columns that this stamp affects
36109         var firstX = this.isOriginLeft ? offset.x : offset.right;
36110         var lastX = firstX + stampSize.width;
36111         var firstCol = Math.floor( firstX / this.columnWidth );
36112         firstCol = Math.max( 0, firstCol );
36113         
36114         var lastCol = Math.floor( lastX / this.columnWidth );
36115         // lastCol should not go over if multiple of columnWidth #425
36116         lastCol -= lastX % this.columnWidth ? 0 : 1;
36117         lastCol = Math.min( this.cols - 1, lastCol );
36118         
36119         // set colYs to bottom of the stamp
36120         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36121             stampSize.height;
36122             
36123         for ( var i = firstCol; i <= lastCol; i++ ) {
36124           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36125         }
36126     },
36127     */
36128     
36129     _getContainerSize : function()
36130     {
36131         this.maxY = Math.max.apply( Math, this.colYs );
36132         var size = {
36133             height: this.maxY
36134         };
36135       
36136         if ( this.isFitWidth ) {
36137             size.width = this._getContainerFitWidth();
36138         }
36139       
36140         return size;
36141     },
36142     
36143     _getContainerFitWidth : function()
36144     {
36145         var unusedCols = 0;
36146         // count unused columns
36147         var i = this.cols;
36148         while ( --i ) {
36149           if ( this.colYs[i] !== 0 ) {
36150             break;
36151           }
36152           unusedCols++;
36153         }
36154         // fit container to columns that have been used
36155         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36156     },
36157     
36158     needsResizeLayout : function()
36159     {
36160         var previousWidth = this.containerWidth;
36161         this.getContainerWidth();
36162         return previousWidth !== this.containerWidth;
36163     }
36164  
36165 });
36166
36167  
36168
36169  /*
36170  * - LGPL
36171  *
36172  * element
36173  * 
36174  */
36175
36176 /**
36177  * @class Roo.bootstrap.MasonryBrick
36178  * @extends Roo.bootstrap.Component
36179  * Bootstrap MasonryBrick class
36180  * 
36181  * @constructor
36182  * Create a new MasonryBrick
36183  * @param {Object} config The config object
36184  */
36185
36186 Roo.bootstrap.MasonryBrick = function(config){
36187     
36188     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36189     
36190     Roo.bootstrap.MasonryBrick.register(this);
36191     
36192     this.addEvents({
36193         // raw events
36194         /**
36195          * @event click
36196          * When a MasonryBrick is clcik
36197          * @param {Roo.bootstrap.MasonryBrick} this
36198          * @param {Roo.EventObject} e
36199          */
36200         "click" : true
36201     });
36202 };
36203
36204 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36205     
36206     /**
36207      * @cfg {String} title
36208      */   
36209     title : '',
36210     /**
36211      * @cfg {String} html
36212      */   
36213     html : '',
36214     /**
36215      * @cfg {String} bgimage
36216      */   
36217     bgimage : '',
36218     /**
36219      * @cfg {String} videourl
36220      */   
36221     videourl : '',
36222     /**
36223      * @cfg {String} cls
36224      */   
36225     cls : '',
36226     /**
36227      * @cfg {String} href
36228      */   
36229     href : '',
36230     /**
36231      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36232      */   
36233     size : 'xs',
36234     
36235     /**
36236      * @cfg {String} placetitle (center|bottom)
36237      */   
36238     placetitle : '',
36239     
36240     /**
36241      * @cfg {Boolean} isFitContainer defalut true
36242      */   
36243     isFitContainer : true, 
36244     
36245     /**
36246      * @cfg {Boolean} preventDefault defalut false
36247      */   
36248     preventDefault : false, 
36249     
36250     /**
36251      * @cfg {Boolean} inverse defalut false
36252      */   
36253     maskInverse : false, 
36254     
36255     getAutoCreate : function()
36256     {
36257         if(!this.isFitContainer){
36258             return this.getSplitAutoCreate();
36259         }
36260         
36261         var cls = 'masonry-brick masonry-brick-full';
36262         
36263         if(this.href.length){
36264             cls += ' masonry-brick-link';
36265         }
36266         
36267         if(this.bgimage.length){
36268             cls += ' masonry-brick-image';
36269         }
36270         
36271         if(this.maskInverse){
36272             cls += ' mask-inverse';
36273         }
36274         
36275         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36276             cls += ' enable-mask';
36277         }
36278         
36279         if(this.size){
36280             cls += ' masonry-' + this.size + '-brick';
36281         }
36282         
36283         if(this.placetitle.length){
36284             
36285             switch (this.placetitle) {
36286                 case 'center' :
36287                     cls += ' masonry-center-title';
36288                     break;
36289                 case 'bottom' :
36290                     cls += ' masonry-bottom-title';
36291                     break;
36292                 default:
36293                     break;
36294             }
36295             
36296         } else {
36297             if(!this.html.length && !this.bgimage.length){
36298                 cls += ' masonry-center-title';
36299             }
36300
36301             if(!this.html.length && this.bgimage.length){
36302                 cls += ' masonry-bottom-title';
36303             }
36304         }
36305         
36306         if(this.cls){
36307             cls += ' ' + this.cls;
36308         }
36309         
36310         var cfg = {
36311             tag: (this.href.length) ? 'a' : 'div',
36312             cls: cls,
36313             cn: [
36314                 {
36315                     tag: 'div',
36316                     cls: 'masonry-brick-mask'
36317                 },
36318                 {
36319                     tag: 'div',
36320                     cls: 'masonry-brick-paragraph',
36321                     cn: []
36322                 }
36323             ]
36324         };
36325         
36326         if(this.href.length){
36327             cfg.href = this.href;
36328         }
36329         
36330         var cn = cfg.cn[1].cn;
36331         
36332         if(this.title.length){
36333             cn.push({
36334                 tag: 'h4',
36335                 cls: 'masonry-brick-title',
36336                 html: this.title
36337             });
36338         }
36339         
36340         if(this.html.length){
36341             cn.push({
36342                 tag: 'p',
36343                 cls: 'masonry-brick-text',
36344                 html: this.html
36345             });
36346         }
36347         
36348         if (!this.title.length && !this.html.length) {
36349             cfg.cn[1].cls += ' hide';
36350         }
36351         
36352         if(this.bgimage.length){
36353             cfg.cn.push({
36354                 tag: 'img',
36355                 cls: 'masonry-brick-image-view',
36356                 src: this.bgimage
36357             });
36358         }
36359         
36360         if(this.videourl.length){
36361             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36362             // youtube support only?
36363             cfg.cn.push({
36364                 tag: 'iframe',
36365                 cls: 'masonry-brick-image-view',
36366                 src: vurl,
36367                 frameborder : 0,
36368                 allowfullscreen : true
36369             });
36370         }
36371         
36372         return cfg;
36373         
36374     },
36375     
36376     getSplitAutoCreate : function()
36377     {
36378         var cls = 'masonry-brick masonry-brick-split';
36379         
36380         if(this.href.length){
36381             cls += ' masonry-brick-link';
36382         }
36383         
36384         if(this.bgimage.length){
36385             cls += ' masonry-brick-image';
36386         }
36387         
36388         if(this.size){
36389             cls += ' masonry-' + this.size + '-brick';
36390         }
36391         
36392         switch (this.placetitle) {
36393             case 'center' :
36394                 cls += ' masonry-center-title';
36395                 break;
36396             case 'bottom' :
36397                 cls += ' masonry-bottom-title';
36398                 break;
36399             default:
36400                 if(!this.bgimage.length){
36401                     cls += ' masonry-center-title';
36402                 }
36403
36404                 if(this.bgimage.length){
36405                     cls += ' masonry-bottom-title';
36406                 }
36407                 break;
36408         }
36409         
36410         if(this.cls){
36411             cls += ' ' + this.cls;
36412         }
36413         
36414         var cfg = {
36415             tag: (this.href.length) ? 'a' : 'div',
36416             cls: cls,
36417             cn: [
36418                 {
36419                     tag: 'div',
36420                     cls: 'masonry-brick-split-head',
36421                     cn: [
36422                         {
36423                             tag: 'div',
36424                             cls: 'masonry-brick-paragraph',
36425                             cn: []
36426                         }
36427                     ]
36428                 },
36429                 {
36430                     tag: 'div',
36431                     cls: 'masonry-brick-split-body',
36432                     cn: []
36433                 }
36434             ]
36435         };
36436         
36437         if(this.href.length){
36438             cfg.href = this.href;
36439         }
36440         
36441         if(this.title.length){
36442             cfg.cn[0].cn[0].cn.push({
36443                 tag: 'h4',
36444                 cls: 'masonry-brick-title',
36445                 html: this.title
36446             });
36447         }
36448         
36449         if(this.html.length){
36450             cfg.cn[1].cn.push({
36451                 tag: 'p',
36452                 cls: 'masonry-brick-text',
36453                 html: this.html
36454             });
36455         }
36456
36457         if(this.bgimage.length){
36458             cfg.cn[0].cn.push({
36459                 tag: 'img',
36460                 cls: 'masonry-brick-image-view',
36461                 src: this.bgimage
36462             });
36463         }
36464         
36465         if(this.videourl.length){
36466             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36467             // youtube support only?
36468             cfg.cn[0].cn.cn.push({
36469                 tag: 'iframe',
36470                 cls: 'masonry-brick-image-view',
36471                 src: vurl,
36472                 frameborder : 0,
36473                 allowfullscreen : true
36474             });
36475         }
36476         
36477         return cfg;
36478     },
36479     
36480     initEvents: function() 
36481     {
36482         switch (this.size) {
36483             case 'xs' :
36484                 this.x = 1;
36485                 this.y = 1;
36486                 break;
36487             case 'sm' :
36488                 this.x = 2;
36489                 this.y = 2;
36490                 break;
36491             case 'md' :
36492             case 'md-left' :
36493             case 'md-right' :
36494                 this.x = 3;
36495                 this.y = 3;
36496                 break;
36497             case 'tall' :
36498                 this.x = 2;
36499                 this.y = 3;
36500                 break;
36501             case 'wide' :
36502                 this.x = 3;
36503                 this.y = 2;
36504                 break;
36505             case 'wide-thin' :
36506                 this.x = 3;
36507                 this.y = 1;
36508                 break;
36509                         
36510             default :
36511                 break;
36512         }
36513         
36514         if(Roo.isTouch){
36515             this.el.on('touchstart', this.onTouchStart, this);
36516             this.el.on('touchmove', this.onTouchMove, this);
36517             this.el.on('touchend', this.onTouchEnd, this);
36518             this.el.on('contextmenu', this.onContextMenu, this);
36519         } else {
36520             this.el.on('mouseenter'  ,this.enter, this);
36521             this.el.on('mouseleave', this.leave, this);
36522             this.el.on('click', this.onClick, this);
36523         }
36524         
36525         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36526             this.parent().bricks.push(this);   
36527         }
36528         
36529     },
36530     
36531     onClick: function(e, el)
36532     {
36533         var time = this.endTimer - this.startTimer;
36534         // Roo.log(e.preventDefault());
36535         if(Roo.isTouch){
36536             if(time > 1000){
36537                 e.preventDefault();
36538                 return;
36539             }
36540         }
36541         
36542         if(!this.preventDefault){
36543             return;
36544         }
36545         
36546         e.preventDefault();
36547         
36548         if (this.activeClass != '') {
36549             this.selectBrick();
36550         }
36551         
36552         this.fireEvent('click', this, e);
36553     },
36554     
36555     enter: function(e, el)
36556     {
36557         e.preventDefault();
36558         
36559         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36560             return;
36561         }
36562         
36563         if(this.bgimage.length && this.html.length){
36564             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36565         }
36566     },
36567     
36568     leave: function(e, el)
36569     {
36570         e.preventDefault();
36571         
36572         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36573             return;
36574         }
36575         
36576         if(this.bgimage.length && this.html.length){
36577             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36578         }
36579     },
36580     
36581     onTouchStart: function(e, el)
36582     {
36583 //        e.preventDefault();
36584         
36585         this.touchmoved = false;
36586         
36587         if(!this.isFitContainer){
36588             return;
36589         }
36590         
36591         if(!this.bgimage.length || !this.html.length){
36592             return;
36593         }
36594         
36595         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36596         
36597         this.timer = new Date().getTime();
36598         
36599     },
36600     
36601     onTouchMove: function(e, el)
36602     {
36603         this.touchmoved = true;
36604     },
36605     
36606     onContextMenu : function(e,el)
36607     {
36608         e.preventDefault();
36609         e.stopPropagation();
36610         return false;
36611     },
36612     
36613     onTouchEnd: function(e, el)
36614     {
36615 //        e.preventDefault();
36616         
36617         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36618         
36619             this.leave(e,el);
36620             
36621             return;
36622         }
36623         
36624         if(!this.bgimage.length || !this.html.length){
36625             
36626             if(this.href.length){
36627                 window.location.href = this.href;
36628             }
36629             
36630             return;
36631         }
36632         
36633         if(!this.isFitContainer){
36634             return;
36635         }
36636         
36637         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36638         
36639         window.location.href = this.href;
36640     },
36641     
36642     //selection on single brick only
36643     selectBrick : function() {
36644         
36645         if (!this.parentId) {
36646             return;
36647         }
36648         
36649         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36650         var index = m.selectedBrick.indexOf(this.id);
36651         
36652         if ( index > -1) {
36653             m.selectedBrick.splice(index,1);
36654             this.el.removeClass(this.activeClass);
36655             return;
36656         }
36657         
36658         for(var i = 0; i < m.selectedBrick.length; i++) {
36659             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36660             b.el.removeClass(b.activeClass);
36661         }
36662         
36663         m.selectedBrick = [];
36664         
36665         m.selectedBrick.push(this.id);
36666         this.el.addClass(this.activeClass);
36667         return;
36668     },
36669     
36670     isSelected : function(){
36671         return this.el.hasClass(this.activeClass);
36672         
36673     }
36674 });
36675
36676 Roo.apply(Roo.bootstrap.MasonryBrick, {
36677     
36678     //groups: {},
36679     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36680      /**
36681     * register a Masonry Brick
36682     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36683     */
36684     
36685     register : function(brick)
36686     {
36687         //this.groups[brick.id] = brick;
36688         this.groups.add(brick.id, brick);
36689     },
36690     /**
36691     * fetch a  masonry brick based on the masonry brick ID
36692     * @param {string} the masonry brick to add
36693     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36694     */
36695     
36696     get: function(brick_id) 
36697     {
36698         // if (typeof(this.groups[brick_id]) == 'undefined') {
36699         //     return false;
36700         // }
36701         // return this.groups[brick_id] ;
36702         
36703         if(this.groups.key(brick_id)) {
36704             return this.groups.key(brick_id);
36705         }
36706         
36707         return false;
36708     }
36709     
36710     
36711     
36712 });
36713
36714  /*
36715  * - LGPL
36716  *
36717  * element
36718  * 
36719  */
36720
36721 /**
36722  * @class Roo.bootstrap.Brick
36723  * @extends Roo.bootstrap.Component
36724  * Bootstrap Brick class
36725  * 
36726  * @constructor
36727  * Create a new Brick
36728  * @param {Object} config The config object
36729  */
36730
36731 Roo.bootstrap.Brick = function(config){
36732     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36733     
36734     this.addEvents({
36735         // raw events
36736         /**
36737          * @event click
36738          * When a Brick is click
36739          * @param {Roo.bootstrap.Brick} this
36740          * @param {Roo.EventObject} e
36741          */
36742         "click" : true
36743     });
36744 };
36745
36746 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36747     
36748     /**
36749      * @cfg {String} title
36750      */   
36751     title : '',
36752     /**
36753      * @cfg {String} html
36754      */   
36755     html : '',
36756     /**
36757      * @cfg {String} bgimage
36758      */   
36759     bgimage : '',
36760     /**
36761      * @cfg {String} cls
36762      */   
36763     cls : '',
36764     /**
36765      * @cfg {String} href
36766      */   
36767     href : '',
36768     /**
36769      * @cfg {String} video
36770      */   
36771     video : '',
36772     /**
36773      * @cfg {Boolean} square
36774      */   
36775     square : true,
36776     
36777     getAutoCreate : function()
36778     {
36779         var cls = 'roo-brick';
36780         
36781         if(this.href.length){
36782             cls += ' roo-brick-link';
36783         }
36784         
36785         if(this.bgimage.length){
36786             cls += ' roo-brick-image';
36787         }
36788         
36789         if(!this.html.length && !this.bgimage.length){
36790             cls += ' roo-brick-center-title';
36791         }
36792         
36793         if(!this.html.length && this.bgimage.length){
36794             cls += ' roo-brick-bottom-title';
36795         }
36796         
36797         if(this.cls){
36798             cls += ' ' + this.cls;
36799         }
36800         
36801         var cfg = {
36802             tag: (this.href.length) ? 'a' : 'div',
36803             cls: cls,
36804             cn: [
36805                 {
36806                     tag: 'div',
36807                     cls: 'roo-brick-paragraph',
36808                     cn: []
36809                 }
36810             ]
36811         };
36812         
36813         if(this.href.length){
36814             cfg.href = this.href;
36815         }
36816         
36817         var cn = cfg.cn[0].cn;
36818         
36819         if(this.title.length){
36820             cn.push({
36821                 tag: 'h4',
36822                 cls: 'roo-brick-title',
36823                 html: this.title
36824             });
36825         }
36826         
36827         if(this.html.length){
36828             cn.push({
36829                 tag: 'p',
36830                 cls: 'roo-brick-text',
36831                 html: this.html
36832             });
36833         } else {
36834             cn.cls += ' hide';
36835         }
36836         
36837         if(this.bgimage.length){
36838             cfg.cn.push({
36839                 tag: 'img',
36840                 cls: 'roo-brick-image-view',
36841                 src: this.bgimage
36842             });
36843         }
36844         
36845         return cfg;
36846     },
36847     
36848     initEvents: function() 
36849     {
36850         if(this.title.length || this.html.length){
36851             this.el.on('mouseenter'  ,this.enter, this);
36852             this.el.on('mouseleave', this.leave, this);
36853         }
36854         
36855         Roo.EventManager.onWindowResize(this.resize, this); 
36856         
36857         if(this.bgimage.length){
36858             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36859             this.imageEl.on('load', this.onImageLoad, this);
36860             return;
36861         }
36862         
36863         this.resize();
36864     },
36865     
36866     onImageLoad : function()
36867     {
36868         this.resize();
36869     },
36870     
36871     resize : function()
36872     {
36873         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36874         
36875         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36876         
36877         if(this.bgimage.length){
36878             var image = this.el.select('.roo-brick-image-view', true).first();
36879             
36880             image.setWidth(paragraph.getWidth());
36881             
36882             if(this.square){
36883                 image.setHeight(paragraph.getWidth());
36884             }
36885             
36886             this.el.setHeight(image.getHeight());
36887             paragraph.setHeight(image.getHeight());
36888             
36889         }
36890         
36891     },
36892     
36893     enter: function(e, el)
36894     {
36895         e.preventDefault();
36896         
36897         if(this.bgimage.length){
36898             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36899             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36900         }
36901     },
36902     
36903     leave: function(e, el)
36904     {
36905         e.preventDefault();
36906         
36907         if(this.bgimage.length){
36908             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36909             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36910         }
36911     }
36912     
36913 });
36914
36915  
36916
36917  /*
36918  * - LGPL
36919  *
36920  * Number field 
36921  */
36922
36923 /**
36924  * @class Roo.bootstrap.NumberField
36925  * @extends Roo.bootstrap.Input
36926  * Bootstrap NumberField class
36927  * 
36928  * 
36929  * 
36930  * 
36931  * @constructor
36932  * Create a new NumberField
36933  * @param {Object} config The config object
36934  */
36935
36936 Roo.bootstrap.NumberField = function(config){
36937     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36938 };
36939
36940 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36941     
36942     /**
36943      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36944      */
36945     allowDecimals : true,
36946     /**
36947      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36948      */
36949     decimalSeparator : ".",
36950     /**
36951      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36952      */
36953     decimalPrecision : 2,
36954     /**
36955      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36956      */
36957     allowNegative : true,
36958     
36959     /**
36960      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36961      */
36962     allowZero: true,
36963     /**
36964      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36965      */
36966     minValue : Number.NEGATIVE_INFINITY,
36967     /**
36968      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36969      */
36970     maxValue : Number.MAX_VALUE,
36971     /**
36972      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36973      */
36974     minText : "The minimum value for this field is {0}",
36975     /**
36976      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36977      */
36978     maxText : "The maximum value for this field is {0}",
36979     /**
36980      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36981      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36982      */
36983     nanText : "{0} is not a valid number",
36984     /**
36985      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36986      */
36987     thousandsDelimiter : false,
36988     /**
36989      * @cfg {String} valueAlign alignment of value
36990      */
36991     valueAlign : "left",
36992
36993     getAutoCreate : function()
36994     {
36995         var hiddenInput = {
36996             tag: 'input',
36997             type: 'hidden',
36998             id: Roo.id(),
36999             cls: 'hidden-number-input'
37000         };
37001         
37002         if (this.name) {
37003             hiddenInput.name = this.name;
37004         }
37005         
37006         this.name = '';
37007         
37008         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37009         
37010         this.name = hiddenInput.name;
37011         
37012         if(cfg.cn.length > 0) {
37013             cfg.cn.push(hiddenInput);
37014         }
37015         
37016         return cfg;
37017     },
37018
37019     // private
37020     initEvents : function()
37021     {   
37022         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37023         
37024         var allowed = "0123456789";
37025         
37026         if(this.allowDecimals){
37027             allowed += this.decimalSeparator;
37028         }
37029         
37030         if(this.allowNegative){
37031             allowed += "-";
37032         }
37033         
37034         if(this.thousandsDelimiter) {
37035             allowed += ",";
37036         }
37037         
37038         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37039         
37040         var keyPress = function(e){
37041             
37042             var k = e.getKey();
37043             
37044             var c = e.getCharCode();
37045             
37046             if(
37047                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37048                     allowed.indexOf(String.fromCharCode(c)) === -1
37049             ){
37050                 e.stopEvent();
37051                 return;
37052             }
37053             
37054             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37055                 return;
37056             }
37057             
37058             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37059                 e.stopEvent();
37060             }
37061         };
37062         
37063         this.el.on("keypress", keyPress, this);
37064     },
37065     
37066     validateValue : function(value)
37067     {
37068         
37069         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37070             return false;
37071         }
37072         
37073         var num = this.parseValue(value);
37074         
37075         if(isNaN(num)){
37076             this.markInvalid(String.format(this.nanText, value));
37077             return false;
37078         }
37079         
37080         if(num < this.minValue){
37081             this.markInvalid(String.format(this.minText, this.minValue));
37082             return false;
37083         }
37084         
37085         if(num > this.maxValue){
37086             this.markInvalid(String.format(this.maxText, this.maxValue));
37087             return false;
37088         }
37089         
37090         return true;
37091     },
37092
37093     getValue : function()
37094     {
37095         var v = this.hiddenEl().getValue();
37096         
37097         return this.fixPrecision(this.parseValue(v));
37098     },
37099
37100     parseValue : function(value)
37101     {
37102         if(this.thousandsDelimiter) {
37103             value += "";
37104             r = new RegExp(",", "g");
37105             value = value.replace(r, "");
37106         }
37107         
37108         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37109         return isNaN(value) ? '' : value;
37110     },
37111
37112     fixPrecision : function(value)
37113     {
37114         if(this.thousandsDelimiter) {
37115             value += "";
37116             r = new RegExp(",", "g");
37117             value = value.replace(r, "");
37118         }
37119         
37120         var nan = isNaN(value);
37121         
37122         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37123             return nan ? '' : value;
37124         }
37125         return parseFloat(value).toFixed(this.decimalPrecision);
37126     },
37127
37128     setValue : function(v)
37129     {
37130         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37131         
37132         this.value = v;
37133         
37134         if(this.rendered){
37135             
37136             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37137             
37138             this.inputEl().dom.value = (v == '') ? '' :
37139                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37140             
37141             if(!this.allowZero && v === '0') {
37142                 this.hiddenEl().dom.value = '';
37143                 this.inputEl().dom.value = '';
37144             }
37145             
37146             this.validate();
37147         }
37148     },
37149
37150     decimalPrecisionFcn : function(v)
37151     {
37152         return Math.floor(v);
37153     },
37154
37155     beforeBlur : function()
37156     {
37157         var v = this.parseValue(this.getRawValue());
37158         
37159         if(v || v === 0 || v === ''){
37160             this.setValue(v);
37161         }
37162     },
37163     
37164     hiddenEl : function()
37165     {
37166         return this.el.select('input.hidden-number-input',true).first();
37167     }
37168     
37169 });
37170
37171  
37172
37173 /*
37174 * Licence: LGPL
37175 */
37176
37177 /**
37178  * @class Roo.bootstrap.DocumentSlider
37179  * @extends Roo.bootstrap.Component
37180  * Bootstrap DocumentSlider class
37181  * 
37182  * @constructor
37183  * Create a new DocumentViewer
37184  * @param {Object} config The config object
37185  */
37186
37187 Roo.bootstrap.DocumentSlider = function(config){
37188     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37189     
37190     this.files = [];
37191     
37192     this.addEvents({
37193         /**
37194          * @event initial
37195          * Fire after initEvent
37196          * @param {Roo.bootstrap.DocumentSlider} this
37197          */
37198         "initial" : true,
37199         /**
37200          * @event update
37201          * Fire after update
37202          * @param {Roo.bootstrap.DocumentSlider} this
37203          */
37204         "update" : true,
37205         /**
37206          * @event click
37207          * Fire after click
37208          * @param {Roo.bootstrap.DocumentSlider} this
37209          */
37210         "click" : true
37211     });
37212 };
37213
37214 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37215     
37216     files : false,
37217     
37218     indicator : 0,
37219     
37220     getAutoCreate : function()
37221     {
37222         var cfg = {
37223             tag : 'div',
37224             cls : 'roo-document-slider',
37225             cn : [
37226                 {
37227                     tag : 'div',
37228                     cls : 'roo-document-slider-header',
37229                     cn : [
37230                         {
37231                             tag : 'div',
37232                             cls : 'roo-document-slider-header-title'
37233                         }
37234                     ]
37235                 },
37236                 {
37237                     tag : 'div',
37238                     cls : 'roo-document-slider-body',
37239                     cn : [
37240                         {
37241                             tag : 'div',
37242                             cls : 'roo-document-slider-prev',
37243                             cn : [
37244                                 {
37245                                     tag : 'i',
37246                                     cls : 'fa fa-chevron-left'
37247                                 }
37248                             ]
37249                         },
37250                         {
37251                             tag : 'div',
37252                             cls : 'roo-document-slider-thumb',
37253                             cn : [
37254                                 {
37255                                     tag : 'img',
37256                                     cls : 'roo-document-slider-image'
37257                                 }
37258                             ]
37259                         },
37260                         {
37261                             tag : 'div',
37262                             cls : 'roo-document-slider-next',
37263                             cn : [
37264                                 {
37265                                     tag : 'i',
37266                                     cls : 'fa fa-chevron-right'
37267                                 }
37268                             ]
37269                         }
37270                     ]
37271                 }
37272             ]
37273         };
37274         
37275         return cfg;
37276     },
37277     
37278     initEvents : function()
37279     {
37280         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37281         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37282         
37283         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37284         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37285         
37286         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37287         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37288         
37289         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37290         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37291         
37292         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37293         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37294         
37295         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37296         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37297         
37298         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37299         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37300         
37301         this.thumbEl.on('click', this.onClick, this);
37302         
37303         this.prevIndicator.on('click', this.prev, this);
37304         
37305         this.nextIndicator.on('click', this.next, this);
37306         
37307     },
37308     
37309     initial : function()
37310     {
37311         if(this.files.length){
37312             this.indicator = 1;
37313             this.update()
37314         }
37315         
37316         this.fireEvent('initial', this);
37317     },
37318     
37319     update : function()
37320     {
37321         this.imageEl.attr('src', this.files[this.indicator - 1]);
37322         
37323         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37324         
37325         this.prevIndicator.show();
37326         
37327         if(this.indicator == 1){
37328             this.prevIndicator.hide();
37329         }
37330         
37331         this.nextIndicator.show();
37332         
37333         if(this.indicator == this.files.length){
37334             this.nextIndicator.hide();
37335         }
37336         
37337         this.thumbEl.scrollTo('top');
37338         
37339         this.fireEvent('update', this);
37340     },
37341     
37342     onClick : function(e)
37343     {
37344         e.preventDefault();
37345         
37346         this.fireEvent('click', this);
37347     },
37348     
37349     prev : function(e)
37350     {
37351         e.preventDefault();
37352         
37353         this.indicator = Math.max(1, this.indicator - 1);
37354         
37355         this.update();
37356     },
37357     
37358     next : function(e)
37359     {
37360         e.preventDefault();
37361         
37362         this.indicator = Math.min(this.files.length, this.indicator + 1);
37363         
37364         this.update();
37365     }
37366 });
37367 /*
37368  * - LGPL
37369  *
37370  * RadioSet
37371  *
37372  *
37373  */
37374
37375 /**
37376  * @class Roo.bootstrap.RadioSet
37377  * @extends Roo.bootstrap.Input
37378  * Bootstrap RadioSet class
37379  * @cfg {String} indicatorpos (left|right) default left
37380  * @cfg {Boolean} inline (true|false) inline the element (default true)
37381  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37382  * @constructor
37383  * Create a new RadioSet
37384  * @param {Object} config The config object
37385  */
37386
37387 Roo.bootstrap.RadioSet = function(config){
37388     
37389     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37390     
37391     this.radioes = [];
37392     
37393     Roo.bootstrap.RadioSet.register(this);
37394     
37395     this.addEvents({
37396         /**
37397         * @event check
37398         * Fires when the element is checked or unchecked.
37399         * @param {Roo.bootstrap.RadioSet} this This radio
37400         * @param {Roo.bootstrap.Radio} item The checked item
37401         */
37402        check : true,
37403        /**
37404         * @event click
37405         * Fires when the element is click.
37406         * @param {Roo.bootstrap.RadioSet} this This radio set
37407         * @param {Roo.bootstrap.Radio} item The checked item
37408         * @param {Roo.EventObject} e The event object
37409         */
37410        click : true
37411     });
37412     
37413 };
37414
37415 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37416
37417     radioes : false,
37418     
37419     inline : true,
37420     
37421     weight : '',
37422     
37423     indicatorpos : 'left',
37424     
37425     getAutoCreate : function()
37426     {
37427         var label = {
37428             tag : 'label',
37429             cls : 'roo-radio-set-label',
37430             cn : [
37431                 {
37432                     tag : 'span',
37433                     html : this.fieldLabel
37434                 }
37435             ]
37436         };
37437         if (Roo.bootstrap.version == 3) {
37438             
37439             
37440             if(this.indicatorpos == 'left'){
37441                 label.cn.unshift({
37442                     tag : 'i',
37443                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37444                     tooltip : 'This field is required'
37445                 });
37446             } else {
37447                 label.cn.push({
37448                     tag : 'i',
37449                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37450                     tooltip : 'This field is required'
37451                 });
37452             }
37453         }
37454         var items = {
37455             tag : 'div',
37456             cls : 'roo-radio-set-items'
37457         };
37458         
37459         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37460         
37461         if (align === 'left' && this.fieldLabel.length) {
37462             
37463             items = {
37464                 cls : "roo-radio-set-right", 
37465                 cn: [
37466                     items
37467                 ]
37468             };
37469             
37470             if(this.labelWidth > 12){
37471                 label.style = "width: " + this.labelWidth + 'px';
37472             }
37473             
37474             if(this.labelWidth < 13 && this.labelmd == 0){
37475                 this.labelmd = this.labelWidth;
37476             }
37477             
37478             if(this.labellg > 0){
37479                 label.cls += ' col-lg-' + this.labellg;
37480                 items.cls += ' col-lg-' + (12 - this.labellg);
37481             }
37482             
37483             if(this.labelmd > 0){
37484                 label.cls += ' col-md-' + this.labelmd;
37485                 items.cls += ' col-md-' + (12 - this.labelmd);
37486             }
37487             
37488             if(this.labelsm > 0){
37489                 label.cls += ' col-sm-' + this.labelsm;
37490                 items.cls += ' col-sm-' + (12 - this.labelsm);
37491             }
37492             
37493             if(this.labelxs > 0){
37494                 label.cls += ' col-xs-' + this.labelxs;
37495                 items.cls += ' col-xs-' + (12 - this.labelxs);
37496             }
37497         }
37498         
37499         var cfg = {
37500             tag : 'div',
37501             cls : 'roo-radio-set',
37502             cn : [
37503                 {
37504                     tag : 'input',
37505                     cls : 'roo-radio-set-input',
37506                     type : 'hidden',
37507                     name : this.name,
37508                     value : this.value ? this.value :  ''
37509                 },
37510                 label,
37511                 items
37512             ]
37513         };
37514         
37515         if(this.weight.length){
37516             cfg.cls += ' roo-radio-' + this.weight;
37517         }
37518         
37519         if(this.inline) {
37520             cfg.cls += ' roo-radio-set-inline';
37521         }
37522         
37523         var settings=this;
37524         ['xs','sm','md','lg'].map(function(size){
37525             if (settings[size]) {
37526                 cfg.cls += ' col-' + size + '-' + settings[size];
37527             }
37528         });
37529         
37530         return cfg;
37531         
37532     },
37533
37534     initEvents : function()
37535     {
37536         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37537         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37538         
37539         if(!this.fieldLabel.length){
37540             this.labelEl.hide();
37541         }
37542         
37543         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37544         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37545         
37546         this.indicator = this.indicatorEl();
37547         
37548         if(this.indicator){
37549             this.indicator.addClass('invisible');
37550         }
37551         
37552         this.originalValue = this.getValue();
37553         
37554     },
37555     
37556     inputEl: function ()
37557     {
37558         return this.el.select('.roo-radio-set-input', true).first();
37559     },
37560     
37561     getChildContainer : function()
37562     {
37563         return this.itemsEl;
37564     },
37565     
37566     register : function(item)
37567     {
37568         this.radioes.push(item);
37569         
37570     },
37571     
37572     validate : function()
37573     {   
37574         if(this.getVisibilityEl().hasClass('hidden')){
37575             return true;
37576         }
37577         
37578         var valid = false;
37579         
37580         Roo.each(this.radioes, function(i){
37581             if(!i.checked){
37582                 return;
37583             }
37584             
37585             valid = true;
37586             return false;
37587         });
37588         
37589         if(this.allowBlank) {
37590             return true;
37591         }
37592         
37593         if(this.disabled || valid){
37594             this.markValid();
37595             return true;
37596         }
37597         
37598         this.markInvalid();
37599         return false;
37600         
37601     },
37602     
37603     markValid : function()
37604     {
37605         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37606             this.indicatorEl().removeClass('visible');
37607             this.indicatorEl().addClass('invisible');
37608         }
37609         
37610         
37611         if (Roo.bootstrap.version == 3) {
37612             this.el.removeClass([this.invalidClass, this.validClass]);
37613             this.el.addClass(this.validClass);
37614         } else {
37615             this.el.removeClass(['is-invalid','is-valid']);
37616             this.el.addClass(['is-valid']);
37617         }
37618         this.fireEvent('valid', this);
37619     },
37620     
37621     markInvalid : function(msg)
37622     {
37623         if(this.allowBlank || this.disabled){
37624             return;
37625         }
37626         
37627         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37628             this.indicatorEl().removeClass('invisible');
37629             this.indicatorEl().addClass('visible');
37630         }
37631         if (Roo.bootstrap.version == 3) {
37632             this.el.removeClass([this.invalidClass, this.validClass]);
37633             this.el.addClass(this.invalidClass);
37634         } else {
37635             this.el.removeClass(['is-invalid','is-valid']);
37636             this.el.addClass(['is-invalid']);
37637         }
37638         
37639         this.fireEvent('invalid', this, msg);
37640         
37641     },
37642     
37643     setValue : function(v, suppressEvent)
37644     {   
37645         if(this.value === v){
37646             return;
37647         }
37648         
37649         this.value = v;
37650         
37651         if(this.rendered){
37652             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37653         }
37654         
37655         Roo.each(this.radioes, function(i){
37656             i.checked = false;
37657             i.el.removeClass('checked');
37658         });
37659         
37660         Roo.each(this.radioes, function(i){
37661             
37662             if(i.value === v || i.value.toString() === v.toString()){
37663                 i.checked = true;
37664                 i.el.addClass('checked');
37665                 
37666                 if(suppressEvent !== true){
37667                     this.fireEvent('check', this, i);
37668                 }
37669                 
37670                 return false;
37671             }
37672             
37673         }, this);
37674         
37675         this.validate();
37676     },
37677     
37678     clearInvalid : function(){
37679         
37680         if(!this.el || this.preventMark){
37681             return;
37682         }
37683         
37684         this.el.removeClass([this.invalidClass]);
37685         
37686         this.fireEvent('valid', this);
37687     }
37688     
37689 });
37690
37691 Roo.apply(Roo.bootstrap.RadioSet, {
37692     
37693     groups: {},
37694     
37695     register : function(set)
37696     {
37697         this.groups[set.name] = set;
37698     },
37699     
37700     get: function(name) 
37701     {
37702         if (typeof(this.groups[name]) == 'undefined') {
37703             return false;
37704         }
37705         
37706         return this.groups[name] ;
37707     }
37708     
37709 });
37710 /*
37711  * Based on:
37712  * Ext JS Library 1.1.1
37713  * Copyright(c) 2006-2007, Ext JS, LLC.
37714  *
37715  * Originally Released Under LGPL - original licence link has changed is not relivant.
37716  *
37717  * Fork - LGPL
37718  * <script type="text/javascript">
37719  */
37720
37721
37722 /**
37723  * @class Roo.bootstrap.SplitBar
37724  * @extends Roo.util.Observable
37725  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37726  * <br><br>
37727  * Usage:
37728  * <pre><code>
37729 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37730                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37731 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37732 split.minSize = 100;
37733 split.maxSize = 600;
37734 split.animate = true;
37735 split.on('moved', splitterMoved);
37736 </code></pre>
37737  * @constructor
37738  * Create a new SplitBar
37739  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37740  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37741  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37742  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37743                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37744                         position of the SplitBar).
37745  */
37746 Roo.bootstrap.SplitBar = function(cfg){
37747     
37748     /** @private */
37749     
37750     //{
37751     //  dragElement : elm
37752     //  resizingElement: el,
37753         // optional..
37754     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37755     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37756         // existingProxy ???
37757     //}
37758     
37759     this.el = Roo.get(cfg.dragElement, true);
37760     this.el.dom.unselectable = "on";
37761     /** @private */
37762     this.resizingEl = Roo.get(cfg.resizingElement, true);
37763
37764     /**
37765      * @private
37766      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37767      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37768      * @type Number
37769      */
37770     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37771     
37772     /**
37773      * The minimum size of the resizing element. (Defaults to 0)
37774      * @type Number
37775      */
37776     this.minSize = 0;
37777     
37778     /**
37779      * The maximum size of the resizing element. (Defaults to 2000)
37780      * @type Number
37781      */
37782     this.maxSize = 2000;
37783     
37784     /**
37785      * Whether to animate the transition to the new size
37786      * @type Boolean
37787      */
37788     this.animate = false;
37789     
37790     /**
37791      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37792      * @type Boolean
37793      */
37794     this.useShim = false;
37795     
37796     /** @private */
37797     this.shim = null;
37798     
37799     if(!cfg.existingProxy){
37800         /** @private */
37801         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37802     }else{
37803         this.proxy = Roo.get(cfg.existingProxy).dom;
37804     }
37805     /** @private */
37806     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37807     
37808     /** @private */
37809     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37810     
37811     /** @private */
37812     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37813     
37814     /** @private */
37815     this.dragSpecs = {};
37816     
37817     /**
37818      * @private The adapter to use to positon and resize elements
37819      */
37820     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37821     this.adapter.init(this);
37822     
37823     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37824         /** @private */
37825         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37826         this.el.addClass("roo-splitbar-h");
37827     }else{
37828         /** @private */
37829         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37830         this.el.addClass("roo-splitbar-v");
37831     }
37832     
37833     this.addEvents({
37834         /**
37835          * @event resize
37836          * Fires when the splitter is moved (alias for {@link #event-moved})
37837          * @param {Roo.bootstrap.SplitBar} this
37838          * @param {Number} newSize the new width or height
37839          */
37840         "resize" : true,
37841         /**
37842          * @event moved
37843          * Fires when the splitter is moved
37844          * @param {Roo.bootstrap.SplitBar} this
37845          * @param {Number} newSize the new width or height
37846          */
37847         "moved" : true,
37848         /**
37849          * @event beforeresize
37850          * Fires before the splitter is dragged
37851          * @param {Roo.bootstrap.SplitBar} this
37852          */
37853         "beforeresize" : true,
37854
37855         "beforeapply" : true
37856     });
37857
37858     Roo.util.Observable.call(this);
37859 };
37860
37861 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37862     onStartProxyDrag : function(x, y){
37863         this.fireEvent("beforeresize", this);
37864         if(!this.overlay){
37865             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37866             o.unselectable();
37867             o.enableDisplayMode("block");
37868             // all splitbars share the same overlay
37869             Roo.bootstrap.SplitBar.prototype.overlay = o;
37870         }
37871         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37872         this.overlay.show();
37873         Roo.get(this.proxy).setDisplayed("block");
37874         var size = this.adapter.getElementSize(this);
37875         this.activeMinSize = this.getMinimumSize();;
37876         this.activeMaxSize = this.getMaximumSize();;
37877         var c1 = size - this.activeMinSize;
37878         var c2 = Math.max(this.activeMaxSize - size, 0);
37879         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37880             this.dd.resetConstraints();
37881             this.dd.setXConstraint(
37882                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37883                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37884             );
37885             this.dd.setYConstraint(0, 0);
37886         }else{
37887             this.dd.resetConstraints();
37888             this.dd.setXConstraint(0, 0);
37889             this.dd.setYConstraint(
37890                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37891                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37892             );
37893          }
37894         this.dragSpecs.startSize = size;
37895         this.dragSpecs.startPoint = [x, y];
37896         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37897     },
37898     
37899     /** 
37900      * @private Called after the drag operation by the DDProxy
37901      */
37902     onEndProxyDrag : function(e){
37903         Roo.get(this.proxy).setDisplayed(false);
37904         var endPoint = Roo.lib.Event.getXY(e);
37905         if(this.overlay){
37906             this.overlay.hide();
37907         }
37908         var newSize;
37909         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37910             newSize = this.dragSpecs.startSize + 
37911                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37912                     endPoint[0] - this.dragSpecs.startPoint[0] :
37913                     this.dragSpecs.startPoint[0] - endPoint[0]
37914                 );
37915         }else{
37916             newSize = this.dragSpecs.startSize + 
37917                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37918                     endPoint[1] - this.dragSpecs.startPoint[1] :
37919                     this.dragSpecs.startPoint[1] - endPoint[1]
37920                 );
37921         }
37922         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37923         if(newSize != this.dragSpecs.startSize){
37924             if(this.fireEvent('beforeapply', this, newSize) !== false){
37925                 this.adapter.setElementSize(this, newSize);
37926                 this.fireEvent("moved", this, newSize);
37927                 this.fireEvent("resize", this, newSize);
37928             }
37929         }
37930     },
37931     
37932     /**
37933      * Get the adapter this SplitBar uses
37934      * @return The adapter object
37935      */
37936     getAdapter : function(){
37937         return this.adapter;
37938     },
37939     
37940     /**
37941      * Set the adapter this SplitBar uses
37942      * @param {Object} adapter A SplitBar adapter object
37943      */
37944     setAdapter : function(adapter){
37945         this.adapter = adapter;
37946         this.adapter.init(this);
37947     },
37948     
37949     /**
37950      * Gets the minimum size for the resizing element
37951      * @return {Number} The minimum size
37952      */
37953     getMinimumSize : function(){
37954         return this.minSize;
37955     },
37956     
37957     /**
37958      * Sets the minimum size for the resizing element
37959      * @param {Number} minSize The minimum size
37960      */
37961     setMinimumSize : function(minSize){
37962         this.minSize = minSize;
37963     },
37964     
37965     /**
37966      * Gets the maximum size for the resizing element
37967      * @return {Number} The maximum size
37968      */
37969     getMaximumSize : function(){
37970         return this.maxSize;
37971     },
37972     
37973     /**
37974      * Sets the maximum size for the resizing element
37975      * @param {Number} maxSize The maximum size
37976      */
37977     setMaximumSize : function(maxSize){
37978         this.maxSize = maxSize;
37979     },
37980     
37981     /**
37982      * Sets the initialize size for the resizing element
37983      * @param {Number} size The initial size
37984      */
37985     setCurrentSize : function(size){
37986         var oldAnimate = this.animate;
37987         this.animate = false;
37988         this.adapter.setElementSize(this, size);
37989         this.animate = oldAnimate;
37990     },
37991     
37992     /**
37993      * Destroy this splitbar. 
37994      * @param {Boolean} removeEl True to remove the element
37995      */
37996     destroy : function(removeEl){
37997         if(this.shim){
37998             this.shim.remove();
37999         }
38000         this.dd.unreg();
38001         this.proxy.parentNode.removeChild(this.proxy);
38002         if(removeEl){
38003             this.el.remove();
38004         }
38005     }
38006 });
38007
38008 /**
38009  * @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.
38010  */
38011 Roo.bootstrap.SplitBar.createProxy = function(dir){
38012     var proxy = new Roo.Element(document.createElement("div"));
38013     proxy.unselectable();
38014     var cls = 'roo-splitbar-proxy';
38015     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38016     document.body.appendChild(proxy.dom);
38017     return proxy.dom;
38018 };
38019
38020 /** 
38021  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38022  * Default Adapter. It assumes the splitter and resizing element are not positioned
38023  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38024  */
38025 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38026 };
38027
38028 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38029     // do nothing for now
38030     init : function(s){
38031     
38032     },
38033     /**
38034      * Called before drag operations to get the current size of the resizing element. 
38035      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38036      */
38037      getElementSize : function(s){
38038         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38039             return s.resizingEl.getWidth();
38040         }else{
38041             return s.resizingEl.getHeight();
38042         }
38043     },
38044     
38045     /**
38046      * Called after drag operations to set the size of the resizing element.
38047      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38048      * @param {Number} newSize The new size to set
38049      * @param {Function} onComplete A function to be invoked when resizing is complete
38050      */
38051     setElementSize : function(s, newSize, onComplete){
38052         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38053             if(!s.animate){
38054                 s.resizingEl.setWidth(newSize);
38055                 if(onComplete){
38056                     onComplete(s, newSize);
38057                 }
38058             }else{
38059                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38060             }
38061         }else{
38062             
38063             if(!s.animate){
38064                 s.resizingEl.setHeight(newSize);
38065                 if(onComplete){
38066                     onComplete(s, newSize);
38067                 }
38068             }else{
38069                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38070             }
38071         }
38072     }
38073 };
38074
38075 /** 
38076  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38077  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38078  * Adapter that  moves the splitter element to align with the resized sizing element. 
38079  * Used with an absolute positioned SplitBar.
38080  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38081  * document.body, make sure you assign an id to the body element.
38082  */
38083 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38084     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38085     this.container = Roo.get(container);
38086 };
38087
38088 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38089     init : function(s){
38090         this.basic.init(s);
38091     },
38092     
38093     getElementSize : function(s){
38094         return this.basic.getElementSize(s);
38095     },
38096     
38097     setElementSize : function(s, newSize, onComplete){
38098         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38099     },
38100     
38101     moveSplitter : function(s){
38102         var yes = Roo.bootstrap.SplitBar;
38103         switch(s.placement){
38104             case yes.LEFT:
38105                 s.el.setX(s.resizingEl.getRight());
38106                 break;
38107             case yes.RIGHT:
38108                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38109                 break;
38110             case yes.TOP:
38111                 s.el.setY(s.resizingEl.getBottom());
38112                 break;
38113             case yes.BOTTOM:
38114                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38115                 break;
38116         }
38117     }
38118 };
38119
38120 /**
38121  * Orientation constant - Create a vertical SplitBar
38122  * @static
38123  * @type Number
38124  */
38125 Roo.bootstrap.SplitBar.VERTICAL = 1;
38126
38127 /**
38128  * Orientation constant - Create a horizontal SplitBar
38129  * @static
38130  * @type Number
38131  */
38132 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38133
38134 /**
38135  * Placement constant - The resizing element is to the left of the splitter element
38136  * @static
38137  * @type Number
38138  */
38139 Roo.bootstrap.SplitBar.LEFT = 1;
38140
38141 /**
38142  * Placement constant - The resizing element is to the right of the splitter element
38143  * @static
38144  * @type Number
38145  */
38146 Roo.bootstrap.SplitBar.RIGHT = 2;
38147
38148 /**
38149  * Placement constant - The resizing element is positioned above the splitter element
38150  * @static
38151  * @type Number
38152  */
38153 Roo.bootstrap.SplitBar.TOP = 3;
38154
38155 /**
38156  * Placement constant - The resizing element is positioned under splitter element
38157  * @static
38158  * @type Number
38159  */
38160 Roo.bootstrap.SplitBar.BOTTOM = 4;
38161 Roo.namespace("Roo.bootstrap.layout");/*
38162  * Based on:
38163  * Ext JS Library 1.1.1
38164  * Copyright(c) 2006-2007, Ext JS, LLC.
38165  *
38166  * Originally Released Under LGPL - original licence link has changed is not relivant.
38167  *
38168  * Fork - LGPL
38169  * <script type="text/javascript">
38170  */
38171
38172 /**
38173  * @class Roo.bootstrap.layout.Manager
38174  * @extends Roo.bootstrap.Component
38175  * Base class for layout managers.
38176  */
38177 Roo.bootstrap.layout.Manager = function(config)
38178 {
38179     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38180
38181
38182
38183
38184
38185     /** false to disable window resize monitoring @type Boolean */
38186     this.monitorWindowResize = true;
38187     this.regions = {};
38188     this.addEvents({
38189         /**
38190          * @event layout
38191          * Fires when a layout is performed.
38192          * @param {Roo.LayoutManager} this
38193          */
38194         "layout" : true,
38195         /**
38196          * @event regionresized
38197          * Fires when the user resizes a region.
38198          * @param {Roo.LayoutRegion} region The resized region
38199          * @param {Number} newSize The new size (width for east/west, height for north/south)
38200          */
38201         "regionresized" : true,
38202         /**
38203          * @event regioncollapsed
38204          * Fires when a region is collapsed.
38205          * @param {Roo.LayoutRegion} region The collapsed region
38206          */
38207         "regioncollapsed" : true,
38208         /**
38209          * @event regionexpanded
38210          * Fires when a region is expanded.
38211          * @param {Roo.LayoutRegion} region The expanded region
38212          */
38213         "regionexpanded" : true
38214     });
38215     this.updating = false;
38216
38217     if (config.el) {
38218         this.el = Roo.get(config.el);
38219         this.initEvents();
38220     }
38221
38222 };
38223
38224 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38225
38226
38227     regions : null,
38228
38229     monitorWindowResize : true,
38230
38231
38232     updating : false,
38233
38234
38235     onRender : function(ct, position)
38236     {
38237         if(!this.el){
38238             this.el = Roo.get(ct);
38239             this.initEvents();
38240         }
38241         //this.fireEvent('render',this);
38242     },
38243
38244
38245     initEvents: function()
38246     {
38247
38248
38249         // ie scrollbar fix
38250         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38251             document.body.scroll = "no";
38252         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38253             this.el.position('relative');
38254         }
38255         this.id = this.el.id;
38256         this.el.addClass("roo-layout-container");
38257         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38258         if(this.el.dom != document.body ) {
38259             this.el.on('resize', this.layout,this);
38260             this.el.on('show', this.layout,this);
38261         }
38262
38263     },
38264
38265     /**
38266      * Returns true if this layout is currently being updated
38267      * @return {Boolean}
38268      */
38269     isUpdating : function(){
38270         return this.updating;
38271     },
38272
38273     /**
38274      * Suspend the LayoutManager from doing auto-layouts while
38275      * making multiple add or remove calls
38276      */
38277     beginUpdate : function(){
38278         this.updating = true;
38279     },
38280
38281     /**
38282      * Restore auto-layouts and optionally disable the manager from performing a layout
38283      * @param {Boolean} noLayout true to disable a layout update
38284      */
38285     endUpdate : function(noLayout){
38286         this.updating = false;
38287         if(!noLayout){
38288             this.layout();
38289         }
38290     },
38291
38292     layout: function(){
38293         // abstract...
38294     },
38295
38296     onRegionResized : function(region, newSize){
38297         this.fireEvent("regionresized", region, newSize);
38298         this.layout();
38299     },
38300
38301     onRegionCollapsed : function(region){
38302         this.fireEvent("regioncollapsed", region);
38303     },
38304
38305     onRegionExpanded : function(region){
38306         this.fireEvent("regionexpanded", region);
38307     },
38308
38309     /**
38310      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38311      * performs box-model adjustments.
38312      * @return {Object} The size as an object {width: (the width), height: (the height)}
38313      */
38314     getViewSize : function()
38315     {
38316         var size;
38317         if(this.el.dom != document.body){
38318             size = this.el.getSize();
38319         }else{
38320             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38321         }
38322         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38323         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38324         return size;
38325     },
38326
38327     /**
38328      * Returns the Element this layout is bound to.
38329      * @return {Roo.Element}
38330      */
38331     getEl : function(){
38332         return this.el;
38333     },
38334
38335     /**
38336      * Returns the specified region.
38337      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38338      * @return {Roo.LayoutRegion}
38339      */
38340     getRegion : function(target){
38341         return this.regions[target.toLowerCase()];
38342     },
38343
38344     onWindowResize : function(){
38345         if(this.monitorWindowResize){
38346             this.layout();
38347         }
38348     }
38349 });
38350 /*
38351  * Based on:
38352  * Ext JS Library 1.1.1
38353  * Copyright(c) 2006-2007, Ext JS, LLC.
38354  *
38355  * Originally Released Under LGPL - original licence link has changed is not relivant.
38356  *
38357  * Fork - LGPL
38358  * <script type="text/javascript">
38359  */
38360 /**
38361  * @class Roo.bootstrap.layout.Border
38362  * @extends Roo.bootstrap.layout.Manager
38363  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38364  * please see: examples/bootstrap/nested.html<br><br>
38365  
38366 <b>The container the layout is rendered into can be either the body element or any other element.
38367 If it is not the body element, the container needs to either be an absolute positioned element,
38368 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38369 the container size if it is not the body element.</b>
38370
38371 * @constructor
38372 * Create a new Border
38373 * @param {Object} config Configuration options
38374  */
38375 Roo.bootstrap.layout.Border = function(config){
38376     config = config || {};
38377     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38378     
38379     
38380     
38381     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38382         if(config[region]){
38383             config[region].region = region;
38384             this.addRegion(config[region]);
38385         }
38386     },this);
38387     
38388 };
38389
38390 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38391
38392 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38393     
38394     parent : false, // this might point to a 'nest' or a ???
38395     
38396     /**
38397      * Creates and adds a new region if it doesn't already exist.
38398      * @param {String} target The target region key (north, south, east, west or center).
38399      * @param {Object} config The regions config object
38400      * @return {BorderLayoutRegion} The new region
38401      */
38402     addRegion : function(config)
38403     {
38404         if(!this.regions[config.region]){
38405             var r = this.factory(config);
38406             this.bindRegion(r);
38407         }
38408         return this.regions[config.region];
38409     },
38410
38411     // private (kinda)
38412     bindRegion : function(r){
38413         this.regions[r.config.region] = r;
38414         
38415         r.on("visibilitychange",    this.layout, this);
38416         r.on("paneladded",          this.layout, this);
38417         r.on("panelremoved",        this.layout, this);
38418         r.on("invalidated",         this.layout, this);
38419         r.on("resized",             this.onRegionResized, this);
38420         r.on("collapsed",           this.onRegionCollapsed, this);
38421         r.on("expanded",            this.onRegionExpanded, this);
38422     },
38423
38424     /**
38425      * Performs a layout update.
38426      */
38427     layout : function()
38428     {
38429         if(this.updating) {
38430             return;
38431         }
38432         
38433         // render all the rebions if they have not been done alreayd?
38434         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38435             if(this.regions[region] && !this.regions[region].bodyEl){
38436                 this.regions[region].onRender(this.el)
38437             }
38438         },this);
38439         
38440         var size = this.getViewSize();
38441         var w = size.width;
38442         var h = size.height;
38443         var centerW = w;
38444         var centerH = h;
38445         var centerY = 0;
38446         var centerX = 0;
38447         //var x = 0, y = 0;
38448
38449         var rs = this.regions;
38450         var north = rs["north"];
38451         var south = rs["south"]; 
38452         var west = rs["west"];
38453         var east = rs["east"];
38454         var center = rs["center"];
38455         //if(this.hideOnLayout){ // not supported anymore
38456             //c.el.setStyle("display", "none");
38457         //}
38458         if(north && north.isVisible()){
38459             var b = north.getBox();
38460             var m = north.getMargins();
38461             b.width = w - (m.left+m.right);
38462             b.x = m.left;
38463             b.y = m.top;
38464             centerY = b.height + b.y + m.bottom;
38465             centerH -= centerY;
38466             north.updateBox(this.safeBox(b));
38467         }
38468         if(south && south.isVisible()){
38469             var b = south.getBox();
38470             var m = south.getMargins();
38471             b.width = w - (m.left+m.right);
38472             b.x = m.left;
38473             var totalHeight = (b.height + m.top + m.bottom);
38474             b.y = h - totalHeight + m.top;
38475             centerH -= totalHeight;
38476             south.updateBox(this.safeBox(b));
38477         }
38478         if(west && west.isVisible()){
38479             var b = west.getBox();
38480             var m = west.getMargins();
38481             b.height = centerH - (m.top+m.bottom);
38482             b.x = m.left;
38483             b.y = centerY + m.top;
38484             var totalWidth = (b.width + m.left + m.right);
38485             centerX += totalWidth;
38486             centerW -= totalWidth;
38487             west.updateBox(this.safeBox(b));
38488         }
38489         if(east && east.isVisible()){
38490             var b = east.getBox();
38491             var m = east.getMargins();
38492             b.height = centerH - (m.top+m.bottom);
38493             var totalWidth = (b.width + m.left + m.right);
38494             b.x = w - totalWidth + m.left;
38495             b.y = centerY + m.top;
38496             centerW -= totalWidth;
38497             east.updateBox(this.safeBox(b));
38498         }
38499         if(center){
38500             var m = center.getMargins();
38501             var centerBox = {
38502                 x: centerX + m.left,
38503                 y: centerY + m.top,
38504                 width: centerW - (m.left+m.right),
38505                 height: centerH - (m.top+m.bottom)
38506             };
38507             //if(this.hideOnLayout){
38508                 //center.el.setStyle("display", "block");
38509             //}
38510             center.updateBox(this.safeBox(centerBox));
38511         }
38512         this.el.repaint();
38513         this.fireEvent("layout", this);
38514     },
38515
38516     // private
38517     safeBox : function(box){
38518         box.width = Math.max(0, box.width);
38519         box.height = Math.max(0, box.height);
38520         return box;
38521     },
38522
38523     /**
38524      * Adds a ContentPanel (or subclass) to this layout.
38525      * @param {String} target The target region key (north, south, east, west or center).
38526      * @param {Roo.ContentPanel} panel The panel to add
38527      * @return {Roo.ContentPanel} The added panel
38528      */
38529     add : function(target, panel){
38530          
38531         target = target.toLowerCase();
38532         return this.regions[target].add(panel);
38533     },
38534
38535     /**
38536      * Remove a ContentPanel (or subclass) to this layout.
38537      * @param {String} target The target region key (north, south, east, west or center).
38538      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38539      * @return {Roo.ContentPanel} The removed panel
38540      */
38541     remove : function(target, panel){
38542         target = target.toLowerCase();
38543         return this.regions[target].remove(panel);
38544     },
38545
38546     /**
38547      * Searches all regions for a panel with the specified id
38548      * @param {String} panelId
38549      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38550      */
38551     findPanel : function(panelId){
38552         var rs = this.regions;
38553         for(var target in rs){
38554             if(typeof rs[target] != "function"){
38555                 var p = rs[target].getPanel(panelId);
38556                 if(p){
38557                     return p;
38558                 }
38559             }
38560         }
38561         return null;
38562     },
38563
38564     /**
38565      * Searches all regions for a panel with the specified id and activates (shows) it.
38566      * @param {String/ContentPanel} panelId The panels id or the panel itself
38567      * @return {Roo.ContentPanel} The shown panel or null
38568      */
38569     showPanel : function(panelId) {
38570       var rs = this.regions;
38571       for(var target in rs){
38572          var r = rs[target];
38573          if(typeof r != "function"){
38574             if(r.hasPanel(panelId)){
38575                return r.showPanel(panelId);
38576             }
38577          }
38578       }
38579       return null;
38580    },
38581
38582    /**
38583      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38584      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38585      */
38586    /*
38587     restoreState : function(provider){
38588         if(!provider){
38589             provider = Roo.state.Manager;
38590         }
38591         var sm = new Roo.LayoutStateManager();
38592         sm.init(this, provider);
38593     },
38594 */
38595  
38596  
38597     /**
38598      * Adds a xtype elements to the layout.
38599      * <pre><code>
38600
38601 layout.addxtype({
38602        xtype : 'ContentPanel',
38603        region: 'west',
38604        items: [ .... ]
38605    }
38606 );
38607
38608 layout.addxtype({
38609         xtype : 'NestedLayoutPanel',
38610         region: 'west',
38611         layout: {
38612            center: { },
38613            west: { }   
38614         },
38615         items : [ ... list of content panels or nested layout panels.. ]
38616    }
38617 );
38618 </code></pre>
38619      * @param {Object} cfg Xtype definition of item to add.
38620      */
38621     addxtype : function(cfg)
38622     {
38623         // basically accepts a pannel...
38624         // can accept a layout region..!?!?
38625         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38626         
38627         
38628         // theory?  children can only be panels??
38629         
38630         //if (!cfg.xtype.match(/Panel$/)) {
38631         //    return false;
38632         //}
38633         var ret = false;
38634         
38635         if (typeof(cfg.region) == 'undefined') {
38636             Roo.log("Failed to add Panel, region was not set");
38637             Roo.log(cfg);
38638             return false;
38639         }
38640         var region = cfg.region;
38641         delete cfg.region;
38642         
38643           
38644         var xitems = [];
38645         if (cfg.items) {
38646             xitems = cfg.items;
38647             delete cfg.items;
38648         }
38649         var nb = false;
38650         
38651         if ( region == 'center') {
38652             Roo.log("Center: " + cfg.title);
38653         }
38654         
38655         
38656         switch(cfg.xtype) 
38657         {
38658             case 'Content':  // ContentPanel (el, cfg)
38659             case 'Scroll':  // ContentPanel (el, cfg)
38660             case 'View': 
38661                 cfg.autoCreate = cfg.autoCreate || true;
38662                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38663                 //} else {
38664                 //    var el = this.el.createChild();
38665                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38666                 //}
38667                 
38668                 this.add(region, ret);
38669                 break;
38670             
38671             /*
38672             case 'TreePanel': // our new panel!
38673                 cfg.el = this.el.createChild();
38674                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38675                 this.add(region, ret);
38676                 break;
38677             */
38678             
38679             case 'Nest': 
38680                 // create a new Layout (which is  a Border Layout...
38681                 
38682                 var clayout = cfg.layout;
38683                 clayout.el  = this.el.createChild();
38684                 clayout.items   = clayout.items  || [];
38685                 
38686                 delete cfg.layout;
38687                 
38688                 // replace this exitems with the clayout ones..
38689                 xitems = clayout.items;
38690                  
38691                 // force background off if it's in center...
38692                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38693                     cfg.background = false;
38694                 }
38695                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38696                 
38697                 
38698                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38699                 //console.log('adding nested layout panel '  + cfg.toSource());
38700                 this.add(region, ret);
38701                 nb = {}; /// find first...
38702                 break;
38703             
38704             case 'Grid':
38705                 
38706                 // needs grid and region
38707                 
38708                 //var el = this.getRegion(region).el.createChild();
38709                 /*
38710                  *var el = this.el.createChild();
38711                 // create the grid first...
38712                 cfg.grid.container = el;
38713                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38714                 */
38715                 
38716                 if (region == 'center' && this.active ) {
38717                     cfg.background = false;
38718                 }
38719                 
38720                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38721                 
38722                 this.add(region, ret);
38723                 /*
38724                 if (cfg.background) {
38725                     // render grid on panel activation (if panel background)
38726                     ret.on('activate', function(gp) {
38727                         if (!gp.grid.rendered) {
38728                     //        gp.grid.render(el);
38729                         }
38730                     });
38731                 } else {
38732                   //  cfg.grid.render(el);
38733                 }
38734                 */
38735                 break;
38736            
38737            
38738             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38739                 // it was the old xcomponent building that caused this before.
38740                 // espeically if border is the top element in the tree.
38741                 ret = this;
38742                 break; 
38743                 
38744                     
38745                 
38746                 
38747                 
38748             default:
38749                 /*
38750                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38751                     
38752                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38753                     this.add(region, ret);
38754                 } else {
38755                 */
38756                     Roo.log(cfg);
38757                     throw "Can not add '" + cfg.xtype + "' to Border";
38758                     return null;
38759              
38760                                 
38761              
38762         }
38763         this.beginUpdate();
38764         // add children..
38765         var region = '';
38766         var abn = {};
38767         Roo.each(xitems, function(i)  {
38768             region = nb && i.region ? i.region : false;
38769             
38770             var add = ret.addxtype(i);
38771            
38772             if (region) {
38773                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38774                 if (!i.background) {
38775                     abn[region] = nb[region] ;
38776                 }
38777             }
38778             
38779         });
38780         this.endUpdate();
38781
38782         // make the last non-background panel active..
38783         //if (nb) { Roo.log(abn); }
38784         if (nb) {
38785             
38786             for(var r in abn) {
38787                 region = this.getRegion(r);
38788                 if (region) {
38789                     // tried using nb[r], but it does not work..
38790                      
38791                     region.showPanel(abn[r]);
38792                    
38793                 }
38794             }
38795         }
38796         return ret;
38797         
38798     },
38799     
38800     
38801 // private
38802     factory : function(cfg)
38803     {
38804         
38805         var validRegions = Roo.bootstrap.layout.Border.regions;
38806
38807         var target = cfg.region;
38808         cfg.mgr = this;
38809         
38810         var r = Roo.bootstrap.layout;
38811         Roo.log(target);
38812         switch(target){
38813             case "north":
38814                 return new r.North(cfg);
38815             case "south":
38816                 return new r.South(cfg);
38817             case "east":
38818                 return new r.East(cfg);
38819             case "west":
38820                 return new r.West(cfg);
38821             case "center":
38822                 return new r.Center(cfg);
38823         }
38824         throw 'Layout region "'+target+'" not supported.';
38825     }
38826     
38827     
38828 });
38829  /*
38830  * Based on:
38831  * Ext JS Library 1.1.1
38832  * Copyright(c) 2006-2007, Ext JS, LLC.
38833  *
38834  * Originally Released Under LGPL - original licence link has changed is not relivant.
38835  *
38836  * Fork - LGPL
38837  * <script type="text/javascript">
38838  */
38839  
38840 /**
38841  * @class Roo.bootstrap.layout.Basic
38842  * @extends Roo.util.Observable
38843  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38844  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38845  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38846  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38847  * @cfg {string}   region  the region that it inhabits..
38848  * @cfg {bool}   skipConfig skip config?
38849  * 
38850
38851  */
38852 Roo.bootstrap.layout.Basic = function(config){
38853     
38854     this.mgr = config.mgr;
38855     
38856     this.position = config.region;
38857     
38858     var skipConfig = config.skipConfig;
38859     
38860     this.events = {
38861         /**
38862          * @scope Roo.BasicLayoutRegion
38863          */
38864         
38865         /**
38866          * @event beforeremove
38867          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38868          * @param {Roo.LayoutRegion} this
38869          * @param {Roo.ContentPanel} panel The panel
38870          * @param {Object} e The cancel event object
38871          */
38872         "beforeremove" : true,
38873         /**
38874          * @event invalidated
38875          * Fires when the layout for this region is changed.
38876          * @param {Roo.LayoutRegion} this
38877          */
38878         "invalidated" : true,
38879         /**
38880          * @event visibilitychange
38881          * Fires when this region is shown or hidden 
38882          * @param {Roo.LayoutRegion} this
38883          * @param {Boolean} visibility true or false
38884          */
38885         "visibilitychange" : true,
38886         /**
38887          * @event paneladded
38888          * Fires when a panel is added. 
38889          * @param {Roo.LayoutRegion} this
38890          * @param {Roo.ContentPanel} panel The panel
38891          */
38892         "paneladded" : true,
38893         /**
38894          * @event panelremoved
38895          * Fires when a panel is removed. 
38896          * @param {Roo.LayoutRegion} this
38897          * @param {Roo.ContentPanel} panel The panel
38898          */
38899         "panelremoved" : true,
38900         /**
38901          * @event beforecollapse
38902          * Fires when this region before collapse.
38903          * @param {Roo.LayoutRegion} this
38904          */
38905         "beforecollapse" : true,
38906         /**
38907          * @event collapsed
38908          * Fires when this region is collapsed.
38909          * @param {Roo.LayoutRegion} this
38910          */
38911         "collapsed" : true,
38912         /**
38913          * @event expanded
38914          * Fires when this region is expanded.
38915          * @param {Roo.LayoutRegion} this
38916          */
38917         "expanded" : true,
38918         /**
38919          * @event slideshow
38920          * Fires when this region is slid into view.
38921          * @param {Roo.LayoutRegion} this
38922          */
38923         "slideshow" : true,
38924         /**
38925          * @event slidehide
38926          * Fires when this region slides out of view. 
38927          * @param {Roo.LayoutRegion} this
38928          */
38929         "slidehide" : true,
38930         /**
38931          * @event panelactivated
38932          * Fires when a panel is activated. 
38933          * @param {Roo.LayoutRegion} this
38934          * @param {Roo.ContentPanel} panel The activated panel
38935          */
38936         "panelactivated" : true,
38937         /**
38938          * @event resized
38939          * Fires when the user resizes this region. 
38940          * @param {Roo.LayoutRegion} this
38941          * @param {Number} newSize The new size (width for east/west, height for north/south)
38942          */
38943         "resized" : true
38944     };
38945     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38946     this.panels = new Roo.util.MixedCollection();
38947     this.panels.getKey = this.getPanelId.createDelegate(this);
38948     this.box = null;
38949     this.activePanel = null;
38950     // ensure listeners are added...
38951     
38952     if (config.listeners || config.events) {
38953         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38954             listeners : config.listeners || {},
38955             events : config.events || {}
38956         });
38957     }
38958     
38959     if(skipConfig !== true){
38960         this.applyConfig(config);
38961     }
38962 };
38963
38964 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38965 {
38966     getPanelId : function(p){
38967         return p.getId();
38968     },
38969     
38970     applyConfig : function(config){
38971         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38972         this.config = config;
38973         
38974     },
38975     
38976     /**
38977      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38978      * the width, for horizontal (north, south) the height.
38979      * @param {Number} newSize The new width or height
38980      */
38981     resizeTo : function(newSize){
38982         var el = this.el ? this.el :
38983                  (this.activePanel ? this.activePanel.getEl() : null);
38984         if(el){
38985             switch(this.position){
38986                 case "east":
38987                 case "west":
38988                     el.setWidth(newSize);
38989                     this.fireEvent("resized", this, newSize);
38990                 break;
38991                 case "north":
38992                 case "south":
38993                     el.setHeight(newSize);
38994                     this.fireEvent("resized", this, newSize);
38995                 break;                
38996             }
38997         }
38998     },
38999     
39000     getBox : function(){
39001         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39002     },
39003     
39004     getMargins : function(){
39005         return this.margins;
39006     },
39007     
39008     updateBox : function(box){
39009         this.box = box;
39010         var el = this.activePanel.getEl();
39011         el.dom.style.left = box.x + "px";
39012         el.dom.style.top = box.y + "px";
39013         this.activePanel.setSize(box.width, box.height);
39014     },
39015     
39016     /**
39017      * Returns the container element for this region.
39018      * @return {Roo.Element}
39019      */
39020     getEl : function(){
39021         return this.activePanel;
39022     },
39023     
39024     /**
39025      * Returns true if this region is currently visible.
39026      * @return {Boolean}
39027      */
39028     isVisible : function(){
39029         return this.activePanel ? true : false;
39030     },
39031     
39032     setActivePanel : function(panel){
39033         panel = this.getPanel(panel);
39034         if(this.activePanel && this.activePanel != panel){
39035             this.activePanel.setActiveState(false);
39036             this.activePanel.getEl().setLeftTop(-10000,-10000);
39037         }
39038         this.activePanel = panel;
39039         panel.setActiveState(true);
39040         if(this.box){
39041             panel.setSize(this.box.width, this.box.height);
39042         }
39043         this.fireEvent("panelactivated", this, panel);
39044         this.fireEvent("invalidated");
39045     },
39046     
39047     /**
39048      * Show the specified panel.
39049      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39050      * @return {Roo.ContentPanel} The shown panel or null
39051      */
39052     showPanel : function(panel){
39053         panel = this.getPanel(panel);
39054         if(panel){
39055             this.setActivePanel(panel);
39056         }
39057         return panel;
39058     },
39059     
39060     /**
39061      * Get the active panel for this region.
39062      * @return {Roo.ContentPanel} The active panel or null
39063      */
39064     getActivePanel : function(){
39065         return this.activePanel;
39066     },
39067     
39068     /**
39069      * Add the passed ContentPanel(s)
39070      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39071      * @return {Roo.ContentPanel} The panel added (if only one was added)
39072      */
39073     add : function(panel){
39074         if(arguments.length > 1){
39075             for(var i = 0, len = arguments.length; i < len; i++) {
39076                 this.add(arguments[i]);
39077             }
39078             return null;
39079         }
39080         if(this.hasPanel(panel)){
39081             this.showPanel(panel);
39082             return panel;
39083         }
39084         var el = panel.getEl();
39085         if(el.dom.parentNode != this.mgr.el.dom){
39086             this.mgr.el.dom.appendChild(el.dom);
39087         }
39088         if(panel.setRegion){
39089             panel.setRegion(this);
39090         }
39091         this.panels.add(panel);
39092         el.setStyle("position", "absolute");
39093         if(!panel.background){
39094             this.setActivePanel(panel);
39095             if(this.config.initialSize && this.panels.getCount()==1){
39096                 this.resizeTo(this.config.initialSize);
39097             }
39098         }
39099         this.fireEvent("paneladded", this, panel);
39100         return panel;
39101     },
39102     
39103     /**
39104      * Returns true if the panel is in this region.
39105      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39106      * @return {Boolean}
39107      */
39108     hasPanel : function(panel){
39109         if(typeof panel == "object"){ // must be panel obj
39110             panel = panel.getId();
39111         }
39112         return this.getPanel(panel) ? true : false;
39113     },
39114     
39115     /**
39116      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39117      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39118      * @param {Boolean} preservePanel Overrides the config preservePanel option
39119      * @return {Roo.ContentPanel} The panel that was removed
39120      */
39121     remove : function(panel, preservePanel){
39122         panel = this.getPanel(panel);
39123         if(!panel){
39124             return null;
39125         }
39126         var e = {};
39127         this.fireEvent("beforeremove", this, panel, e);
39128         if(e.cancel === true){
39129             return null;
39130         }
39131         var panelId = panel.getId();
39132         this.panels.removeKey(panelId);
39133         return panel;
39134     },
39135     
39136     /**
39137      * Returns the panel specified or null if it's not in this region.
39138      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39139      * @return {Roo.ContentPanel}
39140      */
39141     getPanel : function(id){
39142         if(typeof id == "object"){ // must be panel obj
39143             return id;
39144         }
39145         return this.panels.get(id);
39146     },
39147     
39148     /**
39149      * Returns this regions position (north/south/east/west/center).
39150      * @return {String} 
39151      */
39152     getPosition: function(){
39153         return this.position;    
39154     }
39155 });/*
39156  * Based on:
39157  * Ext JS Library 1.1.1
39158  * Copyright(c) 2006-2007, Ext JS, LLC.
39159  *
39160  * Originally Released Under LGPL - original licence link has changed is not relivant.
39161  *
39162  * Fork - LGPL
39163  * <script type="text/javascript">
39164  */
39165  
39166 /**
39167  * @class Roo.bootstrap.layout.Region
39168  * @extends Roo.bootstrap.layout.Basic
39169  * This class represents a region in a layout manager.
39170  
39171  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39172  * @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})
39173  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39174  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39175  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39176  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39177  * @cfg {String}    title           The title for the region (overrides panel titles)
39178  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39179  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39180  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39181  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39182  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39183  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39184  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39185  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39186  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39187  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39188
39189  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39190  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39191  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39192  * @cfg {Number}    width           For East/West panels
39193  * @cfg {Number}    height          For North/South panels
39194  * @cfg {Boolean}   split           To show the splitter
39195  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39196  * 
39197  * @cfg {string}   cls             Extra CSS classes to add to region
39198  * 
39199  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39200  * @cfg {string}   region  the region that it inhabits..
39201  *
39202
39203  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39204  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39205
39206  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39207  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39208  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39209  */
39210 Roo.bootstrap.layout.Region = function(config)
39211 {
39212     this.applyConfig(config);
39213
39214     var mgr = config.mgr;
39215     var pos = config.region;
39216     config.skipConfig = true;
39217     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39218     
39219     if (mgr.el) {
39220         this.onRender(mgr.el);   
39221     }
39222      
39223     this.visible = true;
39224     this.collapsed = false;
39225     this.unrendered_panels = [];
39226 };
39227
39228 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39229
39230     position: '', // set by wrapper (eg. north/south etc..)
39231     unrendered_panels : null,  // unrendered panels.
39232     
39233     tabPosition : false,
39234     
39235     mgr: false, // points to 'Border'
39236     
39237     
39238     createBody : function(){
39239         /** This region's body element 
39240         * @type Roo.Element */
39241         this.bodyEl = this.el.createChild({
39242                 tag: "div",
39243                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39244         });
39245     },
39246
39247     onRender: function(ctr, pos)
39248     {
39249         var dh = Roo.DomHelper;
39250         /** This region's container element 
39251         * @type Roo.Element */
39252         this.el = dh.append(ctr.dom, {
39253                 tag: "div",
39254                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39255             }, true);
39256         /** This region's title element 
39257         * @type Roo.Element */
39258     
39259         this.titleEl = dh.append(this.el.dom,  {
39260                 tag: "div",
39261                 unselectable: "on",
39262                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39263                 children:[
39264                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39265                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39266                 ]
39267             }, true);
39268         
39269         this.titleEl.enableDisplayMode();
39270         /** This region's title text element 
39271         * @type HTMLElement */
39272         this.titleTextEl = this.titleEl.dom.firstChild;
39273         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39274         /*
39275         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39276         this.closeBtn.enableDisplayMode();
39277         this.closeBtn.on("click", this.closeClicked, this);
39278         this.closeBtn.hide();
39279     */
39280         this.createBody(this.config);
39281         if(this.config.hideWhenEmpty){
39282             this.hide();
39283             this.on("paneladded", this.validateVisibility, this);
39284             this.on("panelremoved", this.validateVisibility, this);
39285         }
39286         if(this.autoScroll){
39287             this.bodyEl.setStyle("overflow", "auto");
39288         }else{
39289             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39290         }
39291         //if(c.titlebar !== false){
39292             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39293                 this.titleEl.hide();
39294             }else{
39295                 this.titleEl.show();
39296                 if(this.config.title){
39297                     this.titleTextEl.innerHTML = this.config.title;
39298                 }
39299             }
39300         //}
39301         if(this.config.collapsed){
39302             this.collapse(true);
39303         }
39304         if(this.config.hidden){
39305             this.hide();
39306         }
39307         
39308         if (this.unrendered_panels && this.unrendered_panels.length) {
39309             for (var i =0;i< this.unrendered_panels.length; i++) {
39310                 this.add(this.unrendered_panels[i]);
39311             }
39312             this.unrendered_panels = null;
39313             
39314         }
39315         
39316     },
39317     
39318     applyConfig : function(c)
39319     {
39320         /*
39321          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39322             var dh = Roo.DomHelper;
39323             if(c.titlebar !== false){
39324                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39325                 this.collapseBtn.on("click", this.collapse, this);
39326                 this.collapseBtn.enableDisplayMode();
39327                 /*
39328                 if(c.showPin === true || this.showPin){
39329                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39330                     this.stickBtn.enableDisplayMode();
39331                     this.stickBtn.on("click", this.expand, this);
39332                     this.stickBtn.hide();
39333                 }
39334                 
39335             }
39336             */
39337             /** This region's collapsed element
39338             * @type Roo.Element */
39339             /*
39340              *
39341             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39342                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39343             ]}, true);
39344             
39345             if(c.floatable !== false){
39346                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39347                this.collapsedEl.on("click", this.collapseClick, this);
39348             }
39349
39350             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39351                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39352                    id: "message", unselectable: "on", style:{"float":"left"}});
39353                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39354              }
39355             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39356             this.expandBtn.on("click", this.expand, this);
39357             
39358         }
39359         
39360         if(this.collapseBtn){
39361             this.collapseBtn.setVisible(c.collapsible == true);
39362         }
39363         
39364         this.cmargins = c.cmargins || this.cmargins ||
39365                          (this.position == "west" || this.position == "east" ?
39366                              {top: 0, left: 2, right:2, bottom: 0} :
39367                              {top: 2, left: 0, right:0, bottom: 2});
39368         */
39369         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39370         
39371         
39372         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39373         
39374         this.autoScroll = c.autoScroll || false;
39375         
39376         
39377        
39378         
39379         this.duration = c.duration || .30;
39380         this.slideDuration = c.slideDuration || .45;
39381         this.config = c;
39382        
39383     },
39384     /**
39385      * Returns true if this region is currently visible.
39386      * @return {Boolean}
39387      */
39388     isVisible : function(){
39389         return this.visible;
39390     },
39391
39392     /**
39393      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39394      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39395      */
39396     //setCollapsedTitle : function(title){
39397     //    title = title || "&#160;";
39398      //   if(this.collapsedTitleTextEl){
39399       //      this.collapsedTitleTextEl.innerHTML = title;
39400        // }
39401     //},
39402
39403     getBox : function(){
39404         var b;
39405       //  if(!this.collapsed){
39406             b = this.el.getBox(false, true);
39407        // }else{
39408           //  b = this.collapsedEl.getBox(false, true);
39409         //}
39410         return b;
39411     },
39412
39413     getMargins : function(){
39414         return this.margins;
39415         //return this.collapsed ? this.cmargins : this.margins;
39416     },
39417 /*
39418     highlight : function(){
39419         this.el.addClass("x-layout-panel-dragover");
39420     },
39421
39422     unhighlight : function(){
39423         this.el.removeClass("x-layout-panel-dragover");
39424     },
39425 */
39426     updateBox : function(box)
39427     {
39428         if (!this.bodyEl) {
39429             return; // not rendered yet..
39430         }
39431         
39432         this.box = box;
39433         if(!this.collapsed){
39434             this.el.dom.style.left = box.x + "px";
39435             this.el.dom.style.top = box.y + "px";
39436             this.updateBody(box.width, box.height);
39437         }else{
39438             this.collapsedEl.dom.style.left = box.x + "px";
39439             this.collapsedEl.dom.style.top = box.y + "px";
39440             this.collapsedEl.setSize(box.width, box.height);
39441         }
39442         if(this.tabs){
39443             this.tabs.autoSizeTabs();
39444         }
39445     },
39446
39447     updateBody : function(w, h)
39448     {
39449         if(w !== null){
39450             this.el.setWidth(w);
39451             w -= this.el.getBorderWidth("rl");
39452             if(this.config.adjustments){
39453                 w += this.config.adjustments[0];
39454             }
39455         }
39456         if(h !== null && h > 0){
39457             this.el.setHeight(h);
39458             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39459             h -= this.el.getBorderWidth("tb");
39460             if(this.config.adjustments){
39461                 h += this.config.adjustments[1];
39462             }
39463             this.bodyEl.setHeight(h);
39464             if(this.tabs){
39465                 h = this.tabs.syncHeight(h);
39466             }
39467         }
39468         if(this.panelSize){
39469             w = w !== null ? w : this.panelSize.width;
39470             h = h !== null ? h : this.panelSize.height;
39471         }
39472         if(this.activePanel){
39473             var el = this.activePanel.getEl();
39474             w = w !== null ? w : el.getWidth();
39475             h = h !== null ? h : el.getHeight();
39476             this.panelSize = {width: w, height: h};
39477             this.activePanel.setSize(w, h);
39478         }
39479         if(Roo.isIE && this.tabs){
39480             this.tabs.el.repaint();
39481         }
39482     },
39483
39484     /**
39485      * Returns the container element for this region.
39486      * @return {Roo.Element}
39487      */
39488     getEl : function(){
39489         return this.el;
39490     },
39491
39492     /**
39493      * Hides this region.
39494      */
39495     hide : function(){
39496         //if(!this.collapsed){
39497             this.el.dom.style.left = "-2000px";
39498             this.el.hide();
39499         //}else{
39500          //   this.collapsedEl.dom.style.left = "-2000px";
39501          //   this.collapsedEl.hide();
39502        // }
39503         this.visible = false;
39504         this.fireEvent("visibilitychange", this, false);
39505     },
39506
39507     /**
39508      * Shows this region if it was previously hidden.
39509      */
39510     show : function(){
39511         //if(!this.collapsed){
39512             this.el.show();
39513         //}else{
39514         //    this.collapsedEl.show();
39515        // }
39516         this.visible = true;
39517         this.fireEvent("visibilitychange", this, true);
39518     },
39519 /*
39520     closeClicked : function(){
39521         if(this.activePanel){
39522             this.remove(this.activePanel);
39523         }
39524     },
39525
39526     collapseClick : function(e){
39527         if(this.isSlid){
39528            e.stopPropagation();
39529            this.slideIn();
39530         }else{
39531            e.stopPropagation();
39532            this.slideOut();
39533         }
39534     },
39535 */
39536     /**
39537      * Collapses this region.
39538      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39539      */
39540     /*
39541     collapse : function(skipAnim, skipCheck = false){
39542         if(this.collapsed) {
39543             return;
39544         }
39545         
39546         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39547             
39548             this.collapsed = true;
39549             if(this.split){
39550                 this.split.el.hide();
39551             }
39552             if(this.config.animate && skipAnim !== true){
39553                 this.fireEvent("invalidated", this);
39554                 this.animateCollapse();
39555             }else{
39556                 this.el.setLocation(-20000,-20000);
39557                 this.el.hide();
39558                 this.collapsedEl.show();
39559                 this.fireEvent("collapsed", this);
39560                 this.fireEvent("invalidated", this);
39561             }
39562         }
39563         
39564     },
39565 */
39566     animateCollapse : function(){
39567         // overridden
39568     },
39569
39570     /**
39571      * Expands this region if it was previously collapsed.
39572      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39573      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39574      */
39575     /*
39576     expand : function(e, skipAnim){
39577         if(e) {
39578             e.stopPropagation();
39579         }
39580         if(!this.collapsed || this.el.hasActiveFx()) {
39581             return;
39582         }
39583         if(this.isSlid){
39584             this.afterSlideIn();
39585             skipAnim = true;
39586         }
39587         this.collapsed = false;
39588         if(this.config.animate && skipAnim !== true){
39589             this.animateExpand();
39590         }else{
39591             this.el.show();
39592             if(this.split){
39593                 this.split.el.show();
39594             }
39595             this.collapsedEl.setLocation(-2000,-2000);
39596             this.collapsedEl.hide();
39597             this.fireEvent("invalidated", this);
39598             this.fireEvent("expanded", this);
39599         }
39600     },
39601 */
39602     animateExpand : function(){
39603         // overridden
39604     },
39605
39606     initTabs : function()
39607     {
39608         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39609         
39610         var ts = new Roo.bootstrap.panel.Tabs({
39611             el: this.bodyEl.dom,
39612             region : this,
39613             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39614             disableTooltips: this.config.disableTabTips,
39615             toolbar : this.config.toolbar
39616         });
39617         
39618         if(this.config.hideTabs){
39619             ts.stripWrap.setDisplayed(false);
39620         }
39621         this.tabs = ts;
39622         ts.resizeTabs = this.config.resizeTabs === true;
39623         ts.minTabWidth = this.config.minTabWidth || 40;
39624         ts.maxTabWidth = this.config.maxTabWidth || 250;
39625         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39626         ts.monitorResize = false;
39627         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39628         ts.bodyEl.addClass('roo-layout-tabs-body');
39629         this.panels.each(this.initPanelAsTab, this);
39630     },
39631
39632     initPanelAsTab : function(panel){
39633         var ti = this.tabs.addTab(
39634             panel.getEl().id,
39635             panel.getTitle(),
39636             null,
39637             this.config.closeOnTab && panel.isClosable(),
39638             panel.tpl
39639         );
39640         if(panel.tabTip !== undefined){
39641             ti.setTooltip(panel.tabTip);
39642         }
39643         ti.on("activate", function(){
39644               this.setActivePanel(panel);
39645         }, this);
39646         
39647         if(this.config.closeOnTab){
39648             ti.on("beforeclose", function(t, e){
39649                 e.cancel = true;
39650                 this.remove(panel);
39651             }, this);
39652         }
39653         
39654         panel.tabItem = ti;
39655         
39656         return ti;
39657     },
39658
39659     updatePanelTitle : function(panel, title)
39660     {
39661         if(this.activePanel == panel){
39662             this.updateTitle(title);
39663         }
39664         if(this.tabs){
39665             var ti = this.tabs.getTab(panel.getEl().id);
39666             ti.setText(title);
39667             if(panel.tabTip !== undefined){
39668                 ti.setTooltip(panel.tabTip);
39669             }
39670         }
39671     },
39672
39673     updateTitle : function(title){
39674         if(this.titleTextEl && !this.config.title){
39675             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39676         }
39677     },
39678
39679     setActivePanel : function(panel)
39680     {
39681         panel = this.getPanel(panel);
39682         if(this.activePanel && this.activePanel != panel){
39683             if(this.activePanel.setActiveState(false) === false){
39684                 return;
39685             }
39686         }
39687         this.activePanel = panel;
39688         panel.setActiveState(true);
39689         if(this.panelSize){
39690             panel.setSize(this.panelSize.width, this.panelSize.height);
39691         }
39692         if(this.closeBtn){
39693             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39694         }
39695         this.updateTitle(panel.getTitle());
39696         if(this.tabs){
39697             this.fireEvent("invalidated", this);
39698         }
39699         this.fireEvent("panelactivated", this, panel);
39700     },
39701
39702     /**
39703      * Shows the specified panel.
39704      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39705      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39706      */
39707     showPanel : function(panel)
39708     {
39709         panel = this.getPanel(panel);
39710         if(panel){
39711             if(this.tabs){
39712                 var tab = this.tabs.getTab(panel.getEl().id);
39713                 if(tab.isHidden()){
39714                     this.tabs.unhideTab(tab.id);
39715                 }
39716                 tab.activate();
39717             }else{
39718                 this.setActivePanel(panel);
39719             }
39720         }
39721         return panel;
39722     },
39723
39724     /**
39725      * Get the active panel for this region.
39726      * @return {Roo.ContentPanel} The active panel or null
39727      */
39728     getActivePanel : function(){
39729         return this.activePanel;
39730     },
39731
39732     validateVisibility : function(){
39733         if(this.panels.getCount() < 1){
39734             this.updateTitle("&#160;");
39735             this.closeBtn.hide();
39736             this.hide();
39737         }else{
39738             if(!this.isVisible()){
39739                 this.show();
39740             }
39741         }
39742     },
39743
39744     /**
39745      * Adds the passed ContentPanel(s) to this region.
39746      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39747      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39748      */
39749     add : function(panel)
39750     {
39751         if(arguments.length > 1){
39752             for(var i = 0, len = arguments.length; i < len; i++) {
39753                 this.add(arguments[i]);
39754             }
39755             return null;
39756         }
39757         
39758         // if we have not been rendered yet, then we can not really do much of this..
39759         if (!this.bodyEl) {
39760             this.unrendered_panels.push(panel);
39761             return panel;
39762         }
39763         
39764         
39765         
39766         
39767         if(this.hasPanel(panel)){
39768             this.showPanel(panel);
39769             return panel;
39770         }
39771         panel.setRegion(this);
39772         this.panels.add(panel);
39773        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39774             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39775             // and hide them... ???
39776             this.bodyEl.dom.appendChild(panel.getEl().dom);
39777             if(panel.background !== true){
39778                 this.setActivePanel(panel);
39779             }
39780             this.fireEvent("paneladded", this, panel);
39781             return panel;
39782         }
39783         */
39784         if(!this.tabs){
39785             this.initTabs();
39786         }else{
39787             this.initPanelAsTab(panel);
39788         }
39789         
39790         
39791         if(panel.background !== true){
39792             this.tabs.activate(panel.getEl().id);
39793         }
39794         this.fireEvent("paneladded", this, panel);
39795         return panel;
39796     },
39797
39798     /**
39799      * Hides the tab for the specified panel.
39800      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39801      */
39802     hidePanel : function(panel){
39803         if(this.tabs && (panel = this.getPanel(panel))){
39804             this.tabs.hideTab(panel.getEl().id);
39805         }
39806     },
39807
39808     /**
39809      * Unhides the tab for a previously hidden panel.
39810      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39811      */
39812     unhidePanel : function(panel){
39813         if(this.tabs && (panel = this.getPanel(panel))){
39814             this.tabs.unhideTab(panel.getEl().id);
39815         }
39816     },
39817
39818     clearPanels : function(){
39819         while(this.panels.getCount() > 0){
39820              this.remove(this.panels.first());
39821         }
39822     },
39823
39824     /**
39825      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39826      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39827      * @param {Boolean} preservePanel Overrides the config preservePanel option
39828      * @return {Roo.ContentPanel} The panel that was removed
39829      */
39830     remove : function(panel, preservePanel)
39831     {
39832         panel = this.getPanel(panel);
39833         if(!panel){
39834             return null;
39835         }
39836         var e = {};
39837         this.fireEvent("beforeremove", this, panel, e);
39838         if(e.cancel === true){
39839             return null;
39840         }
39841         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39842         var panelId = panel.getId();
39843         this.panels.removeKey(panelId);
39844         if(preservePanel){
39845             document.body.appendChild(panel.getEl().dom);
39846         }
39847         if(this.tabs){
39848             this.tabs.removeTab(panel.getEl().id);
39849         }else if (!preservePanel){
39850             this.bodyEl.dom.removeChild(panel.getEl().dom);
39851         }
39852         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39853             var p = this.panels.first();
39854             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39855             tempEl.appendChild(p.getEl().dom);
39856             this.bodyEl.update("");
39857             this.bodyEl.dom.appendChild(p.getEl().dom);
39858             tempEl = null;
39859             this.updateTitle(p.getTitle());
39860             this.tabs = null;
39861             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39862             this.setActivePanel(p);
39863         }
39864         panel.setRegion(null);
39865         if(this.activePanel == panel){
39866             this.activePanel = null;
39867         }
39868         if(this.config.autoDestroy !== false && preservePanel !== true){
39869             try{panel.destroy();}catch(e){}
39870         }
39871         this.fireEvent("panelremoved", this, panel);
39872         return panel;
39873     },
39874
39875     /**
39876      * Returns the TabPanel component used by this region
39877      * @return {Roo.TabPanel}
39878      */
39879     getTabs : function(){
39880         return this.tabs;
39881     },
39882
39883     createTool : function(parentEl, className){
39884         var btn = Roo.DomHelper.append(parentEl, {
39885             tag: "div",
39886             cls: "x-layout-tools-button",
39887             children: [ {
39888                 tag: "div",
39889                 cls: "roo-layout-tools-button-inner " + className,
39890                 html: "&#160;"
39891             }]
39892         }, true);
39893         btn.addClassOnOver("roo-layout-tools-button-over");
39894         return btn;
39895     }
39896 });/*
39897  * Based on:
39898  * Ext JS Library 1.1.1
39899  * Copyright(c) 2006-2007, Ext JS, LLC.
39900  *
39901  * Originally Released Under LGPL - original licence link has changed is not relivant.
39902  *
39903  * Fork - LGPL
39904  * <script type="text/javascript">
39905  */
39906  
39907
39908
39909 /**
39910  * @class Roo.SplitLayoutRegion
39911  * @extends Roo.LayoutRegion
39912  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39913  */
39914 Roo.bootstrap.layout.Split = function(config){
39915     this.cursor = config.cursor;
39916     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39917 };
39918
39919 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39920 {
39921     splitTip : "Drag to resize.",
39922     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39923     useSplitTips : false,
39924
39925     applyConfig : function(config){
39926         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39927     },
39928     
39929     onRender : function(ctr,pos) {
39930         
39931         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39932         if(!this.config.split){
39933             return;
39934         }
39935         if(!this.split){
39936             
39937             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39938                             tag: "div",
39939                             id: this.el.id + "-split",
39940                             cls: "roo-layout-split roo-layout-split-"+this.position,
39941                             html: "&#160;"
39942             });
39943             /** The SplitBar for this region 
39944             * @type Roo.SplitBar */
39945             // does not exist yet...
39946             Roo.log([this.position, this.orientation]);
39947             
39948             this.split = new Roo.bootstrap.SplitBar({
39949                 dragElement : splitEl,
39950                 resizingElement: this.el,
39951                 orientation : this.orientation
39952             });
39953             
39954             this.split.on("moved", this.onSplitMove, this);
39955             this.split.useShim = this.config.useShim === true;
39956             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39957             if(this.useSplitTips){
39958                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39959             }
39960             //if(config.collapsible){
39961             //    this.split.el.on("dblclick", this.collapse,  this);
39962             //}
39963         }
39964         if(typeof this.config.minSize != "undefined"){
39965             this.split.minSize = this.config.minSize;
39966         }
39967         if(typeof this.config.maxSize != "undefined"){
39968             this.split.maxSize = this.config.maxSize;
39969         }
39970         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39971             this.hideSplitter();
39972         }
39973         
39974     },
39975
39976     getHMaxSize : function(){
39977          var cmax = this.config.maxSize || 10000;
39978          var center = this.mgr.getRegion("center");
39979          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39980     },
39981
39982     getVMaxSize : function(){
39983          var cmax = this.config.maxSize || 10000;
39984          var center = this.mgr.getRegion("center");
39985          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39986     },
39987
39988     onSplitMove : function(split, newSize){
39989         this.fireEvent("resized", this, newSize);
39990     },
39991     
39992     /** 
39993      * Returns the {@link Roo.SplitBar} for this region.
39994      * @return {Roo.SplitBar}
39995      */
39996     getSplitBar : function(){
39997         return this.split;
39998     },
39999     
40000     hide : function(){
40001         this.hideSplitter();
40002         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40003     },
40004
40005     hideSplitter : function(){
40006         if(this.split){
40007             this.split.el.setLocation(-2000,-2000);
40008             this.split.el.hide();
40009         }
40010     },
40011
40012     show : function(){
40013         if(this.split){
40014             this.split.el.show();
40015         }
40016         Roo.bootstrap.layout.Split.superclass.show.call(this);
40017     },
40018     
40019     beforeSlide: function(){
40020         if(Roo.isGecko){// firefox overflow auto bug workaround
40021             this.bodyEl.clip();
40022             if(this.tabs) {
40023                 this.tabs.bodyEl.clip();
40024             }
40025             if(this.activePanel){
40026                 this.activePanel.getEl().clip();
40027                 
40028                 if(this.activePanel.beforeSlide){
40029                     this.activePanel.beforeSlide();
40030                 }
40031             }
40032         }
40033     },
40034     
40035     afterSlide : function(){
40036         if(Roo.isGecko){// firefox overflow auto bug workaround
40037             this.bodyEl.unclip();
40038             if(this.tabs) {
40039                 this.tabs.bodyEl.unclip();
40040             }
40041             if(this.activePanel){
40042                 this.activePanel.getEl().unclip();
40043                 if(this.activePanel.afterSlide){
40044                     this.activePanel.afterSlide();
40045                 }
40046             }
40047         }
40048     },
40049
40050     initAutoHide : function(){
40051         if(this.autoHide !== false){
40052             if(!this.autoHideHd){
40053                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40054                 this.autoHideHd = {
40055                     "mouseout": function(e){
40056                         if(!e.within(this.el, true)){
40057                             st.delay(500);
40058                         }
40059                     },
40060                     "mouseover" : function(e){
40061                         st.cancel();
40062                     },
40063                     scope : this
40064                 };
40065             }
40066             this.el.on(this.autoHideHd);
40067         }
40068     },
40069
40070     clearAutoHide : function(){
40071         if(this.autoHide !== false){
40072             this.el.un("mouseout", this.autoHideHd.mouseout);
40073             this.el.un("mouseover", this.autoHideHd.mouseover);
40074         }
40075     },
40076
40077     clearMonitor : function(){
40078         Roo.get(document).un("click", this.slideInIf, this);
40079     },
40080
40081     // these names are backwards but not changed for compat
40082     slideOut : function(){
40083         if(this.isSlid || this.el.hasActiveFx()){
40084             return;
40085         }
40086         this.isSlid = true;
40087         if(this.collapseBtn){
40088             this.collapseBtn.hide();
40089         }
40090         this.closeBtnState = this.closeBtn.getStyle('display');
40091         this.closeBtn.hide();
40092         if(this.stickBtn){
40093             this.stickBtn.show();
40094         }
40095         this.el.show();
40096         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40097         this.beforeSlide();
40098         this.el.setStyle("z-index", 10001);
40099         this.el.slideIn(this.getSlideAnchor(), {
40100             callback: function(){
40101                 this.afterSlide();
40102                 this.initAutoHide();
40103                 Roo.get(document).on("click", this.slideInIf, this);
40104                 this.fireEvent("slideshow", this);
40105             },
40106             scope: this,
40107             block: true
40108         });
40109     },
40110
40111     afterSlideIn : function(){
40112         this.clearAutoHide();
40113         this.isSlid = false;
40114         this.clearMonitor();
40115         this.el.setStyle("z-index", "");
40116         if(this.collapseBtn){
40117             this.collapseBtn.show();
40118         }
40119         this.closeBtn.setStyle('display', this.closeBtnState);
40120         if(this.stickBtn){
40121             this.stickBtn.hide();
40122         }
40123         this.fireEvent("slidehide", this);
40124     },
40125
40126     slideIn : function(cb){
40127         if(!this.isSlid || this.el.hasActiveFx()){
40128             Roo.callback(cb);
40129             return;
40130         }
40131         this.isSlid = false;
40132         this.beforeSlide();
40133         this.el.slideOut(this.getSlideAnchor(), {
40134             callback: function(){
40135                 this.el.setLeftTop(-10000, -10000);
40136                 this.afterSlide();
40137                 this.afterSlideIn();
40138                 Roo.callback(cb);
40139             },
40140             scope: this,
40141             block: true
40142         });
40143     },
40144     
40145     slideInIf : function(e){
40146         if(!e.within(this.el)){
40147             this.slideIn();
40148         }
40149     },
40150
40151     animateCollapse : function(){
40152         this.beforeSlide();
40153         this.el.setStyle("z-index", 20000);
40154         var anchor = this.getSlideAnchor();
40155         this.el.slideOut(anchor, {
40156             callback : function(){
40157                 this.el.setStyle("z-index", "");
40158                 this.collapsedEl.slideIn(anchor, {duration:.3});
40159                 this.afterSlide();
40160                 this.el.setLocation(-10000,-10000);
40161                 this.el.hide();
40162                 this.fireEvent("collapsed", this);
40163             },
40164             scope: this,
40165             block: true
40166         });
40167     },
40168
40169     animateExpand : function(){
40170         this.beforeSlide();
40171         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40172         this.el.setStyle("z-index", 20000);
40173         this.collapsedEl.hide({
40174             duration:.1
40175         });
40176         this.el.slideIn(this.getSlideAnchor(), {
40177             callback : function(){
40178                 this.el.setStyle("z-index", "");
40179                 this.afterSlide();
40180                 if(this.split){
40181                     this.split.el.show();
40182                 }
40183                 this.fireEvent("invalidated", this);
40184                 this.fireEvent("expanded", this);
40185             },
40186             scope: this,
40187             block: true
40188         });
40189     },
40190
40191     anchors : {
40192         "west" : "left",
40193         "east" : "right",
40194         "north" : "top",
40195         "south" : "bottom"
40196     },
40197
40198     sanchors : {
40199         "west" : "l",
40200         "east" : "r",
40201         "north" : "t",
40202         "south" : "b"
40203     },
40204
40205     canchors : {
40206         "west" : "tl-tr",
40207         "east" : "tr-tl",
40208         "north" : "tl-bl",
40209         "south" : "bl-tl"
40210     },
40211
40212     getAnchor : function(){
40213         return this.anchors[this.position];
40214     },
40215
40216     getCollapseAnchor : function(){
40217         return this.canchors[this.position];
40218     },
40219
40220     getSlideAnchor : function(){
40221         return this.sanchors[this.position];
40222     },
40223
40224     getAlignAdj : function(){
40225         var cm = this.cmargins;
40226         switch(this.position){
40227             case "west":
40228                 return [0, 0];
40229             break;
40230             case "east":
40231                 return [0, 0];
40232             break;
40233             case "north":
40234                 return [0, 0];
40235             break;
40236             case "south":
40237                 return [0, 0];
40238             break;
40239         }
40240     },
40241
40242     getExpandAdj : function(){
40243         var c = this.collapsedEl, cm = this.cmargins;
40244         switch(this.position){
40245             case "west":
40246                 return [-(cm.right+c.getWidth()+cm.left), 0];
40247             break;
40248             case "east":
40249                 return [cm.right+c.getWidth()+cm.left, 0];
40250             break;
40251             case "north":
40252                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40253             break;
40254             case "south":
40255                 return [0, cm.top+cm.bottom+c.getHeight()];
40256             break;
40257         }
40258     }
40259 });/*
40260  * Based on:
40261  * Ext JS Library 1.1.1
40262  * Copyright(c) 2006-2007, Ext JS, LLC.
40263  *
40264  * Originally Released Under LGPL - original licence link has changed is not relivant.
40265  *
40266  * Fork - LGPL
40267  * <script type="text/javascript">
40268  */
40269 /*
40270  * These classes are private internal classes
40271  */
40272 Roo.bootstrap.layout.Center = function(config){
40273     config.region = "center";
40274     Roo.bootstrap.layout.Region.call(this, config);
40275     this.visible = true;
40276     this.minWidth = config.minWidth || 20;
40277     this.minHeight = config.minHeight || 20;
40278 };
40279
40280 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40281     hide : function(){
40282         // center panel can't be hidden
40283     },
40284     
40285     show : function(){
40286         // center panel can't be hidden
40287     },
40288     
40289     getMinWidth: function(){
40290         return this.minWidth;
40291     },
40292     
40293     getMinHeight: function(){
40294         return this.minHeight;
40295     }
40296 });
40297
40298
40299
40300
40301  
40302
40303
40304
40305
40306
40307
40308 Roo.bootstrap.layout.North = function(config)
40309 {
40310     config.region = 'north';
40311     config.cursor = 'n-resize';
40312     
40313     Roo.bootstrap.layout.Split.call(this, config);
40314     
40315     
40316     if(this.split){
40317         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40318         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40319         this.split.el.addClass("roo-layout-split-v");
40320     }
40321     //var size = config.initialSize || config.height;
40322     //if(this.el && typeof size != "undefined"){
40323     //    this.el.setHeight(size);
40324     //}
40325 };
40326 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40327 {
40328     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40329      
40330      
40331     onRender : function(ctr, pos)
40332     {
40333         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40334         var size = this.config.initialSize || this.config.height;
40335         if(this.el && typeof size != "undefined"){
40336             this.el.setHeight(size);
40337         }
40338     
40339     },
40340     
40341     getBox : function(){
40342         if(this.collapsed){
40343             return this.collapsedEl.getBox();
40344         }
40345         var box = this.el.getBox();
40346         if(this.split){
40347             box.height += this.split.el.getHeight();
40348         }
40349         return box;
40350     },
40351     
40352     updateBox : function(box){
40353         if(this.split && !this.collapsed){
40354             box.height -= this.split.el.getHeight();
40355             this.split.el.setLeft(box.x);
40356             this.split.el.setTop(box.y+box.height);
40357             this.split.el.setWidth(box.width);
40358         }
40359         if(this.collapsed){
40360             this.updateBody(box.width, null);
40361         }
40362         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40363     }
40364 });
40365
40366
40367
40368
40369
40370 Roo.bootstrap.layout.South = function(config){
40371     config.region = 'south';
40372     config.cursor = 's-resize';
40373     Roo.bootstrap.layout.Split.call(this, config);
40374     if(this.split){
40375         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40376         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40377         this.split.el.addClass("roo-layout-split-v");
40378     }
40379     
40380 };
40381
40382 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40383     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40384     
40385     onRender : function(ctr, pos)
40386     {
40387         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40388         var size = this.config.initialSize || this.config.height;
40389         if(this.el && typeof size != "undefined"){
40390             this.el.setHeight(size);
40391         }
40392     
40393     },
40394     
40395     getBox : function(){
40396         if(this.collapsed){
40397             return this.collapsedEl.getBox();
40398         }
40399         var box = this.el.getBox();
40400         if(this.split){
40401             var sh = this.split.el.getHeight();
40402             box.height += sh;
40403             box.y -= sh;
40404         }
40405         return box;
40406     },
40407     
40408     updateBox : function(box){
40409         if(this.split && !this.collapsed){
40410             var sh = this.split.el.getHeight();
40411             box.height -= sh;
40412             box.y += sh;
40413             this.split.el.setLeft(box.x);
40414             this.split.el.setTop(box.y-sh);
40415             this.split.el.setWidth(box.width);
40416         }
40417         if(this.collapsed){
40418             this.updateBody(box.width, null);
40419         }
40420         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40421     }
40422 });
40423
40424 Roo.bootstrap.layout.East = function(config){
40425     config.region = "east";
40426     config.cursor = "e-resize";
40427     Roo.bootstrap.layout.Split.call(this, config);
40428     if(this.split){
40429         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40430         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40431         this.split.el.addClass("roo-layout-split-h");
40432     }
40433     
40434 };
40435 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40436     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40437     
40438     onRender : function(ctr, pos)
40439     {
40440         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40441         var size = this.config.initialSize || this.config.width;
40442         if(this.el && typeof size != "undefined"){
40443             this.el.setWidth(size);
40444         }
40445     
40446     },
40447     
40448     getBox : function(){
40449         if(this.collapsed){
40450             return this.collapsedEl.getBox();
40451         }
40452         var box = this.el.getBox();
40453         if(this.split){
40454             var sw = this.split.el.getWidth();
40455             box.width += sw;
40456             box.x -= sw;
40457         }
40458         return box;
40459     },
40460
40461     updateBox : function(box){
40462         if(this.split && !this.collapsed){
40463             var sw = this.split.el.getWidth();
40464             box.width -= sw;
40465             this.split.el.setLeft(box.x);
40466             this.split.el.setTop(box.y);
40467             this.split.el.setHeight(box.height);
40468             box.x += sw;
40469         }
40470         if(this.collapsed){
40471             this.updateBody(null, box.height);
40472         }
40473         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40474     }
40475 });
40476
40477 Roo.bootstrap.layout.West = function(config){
40478     config.region = "west";
40479     config.cursor = "w-resize";
40480     
40481     Roo.bootstrap.layout.Split.call(this, config);
40482     if(this.split){
40483         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40484         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40485         this.split.el.addClass("roo-layout-split-h");
40486     }
40487     
40488 };
40489 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40490     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40491     
40492     onRender: function(ctr, pos)
40493     {
40494         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40495         var size = this.config.initialSize || this.config.width;
40496         if(typeof size != "undefined"){
40497             this.el.setWidth(size);
40498         }
40499     },
40500     
40501     getBox : function(){
40502         if(this.collapsed){
40503             return this.collapsedEl.getBox();
40504         }
40505         var box = this.el.getBox();
40506         if (box.width == 0) {
40507             box.width = this.config.width; // kludge?
40508         }
40509         if(this.split){
40510             box.width += this.split.el.getWidth();
40511         }
40512         return box;
40513     },
40514     
40515     updateBox : function(box){
40516         if(this.split && !this.collapsed){
40517             var sw = this.split.el.getWidth();
40518             box.width -= sw;
40519             this.split.el.setLeft(box.x+box.width);
40520             this.split.el.setTop(box.y);
40521             this.split.el.setHeight(box.height);
40522         }
40523         if(this.collapsed){
40524             this.updateBody(null, box.height);
40525         }
40526         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40527     }
40528 });Roo.namespace("Roo.bootstrap.panel");/*
40529  * Based on:
40530  * Ext JS Library 1.1.1
40531  * Copyright(c) 2006-2007, Ext JS, LLC.
40532  *
40533  * Originally Released Under LGPL - original licence link has changed is not relivant.
40534  *
40535  * Fork - LGPL
40536  * <script type="text/javascript">
40537  */
40538 /**
40539  * @class Roo.ContentPanel
40540  * @extends Roo.util.Observable
40541  * A basic ContentPanel element.
40542  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40543  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40544  * @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
40545  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40546  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40547  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40548  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40549  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40550  * @cfg {String} title          The title for this panel
40551  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40552  * @cfg {String} url            Calls {@link #setUrl} with this value
40553  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40554  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40555  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40556  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40557  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40558  * @cfg {Boolean} badges render the badges
40559  * @cfg {String} cls  extra classes to use  
40560  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40561
40562  * @constructor
40563  * Create a new ContentPanel.
40564  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40565  * @param {String/Object} config A string to set only the title or a config object
40566  * @param {String} content (optional) Set the HTML content for this panel
40567  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40568  */
40569 Roo.bootstrap.panel.Content = function( config){
40570     
40571     this.tpl = config.tpl || false;
40572     
40573     var el = config.el;
40574     var content = config.content;
40575
40576     if(config.autoCreate){ // xtype is available if this is called from factory
40577         el = Roo.id();
40578     }
40579     this.el = Roo.get(el);
40580     if(!this.el && config && config.autoCreate){
40581         if(typeof config.autoCreate == "object"){
40582             if(!config.autoCreate.id){
40583                 config.autoCreate.id = config.id||el;
40584             }
40585             this.el = Roo.DomHelper.append(document.body,
40586                         config.autoCreate, true);
40587         }else{
40588             var elcfg =  {
40589                 tag: "div",
40590                 cls: (config.cls || '') +
40591                     (config.background ? ' bg-' + config.background : '') +
40592                     " roo-layout-inactive-content",
40593                 id: config.id||el
40594             };
40595             if (config.iframe) {
40596                 elcfg.cn = [
40597                     {
40598                         tag : 'iframe',
40599                         style : 'border: 0px',
40600                         src : 'about:blank'
40601                     }
40602                 ];
40603             }
40604               
40605             if (config.html) {
40606                 elcfg.html = config.html;
40607                 
40608             }
40609                         
40610             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40611             if (config.iframe) {
40612                 this.iframeEl = this.el.select('iframe',true).first();
40613             }
40614             
40615         }
40616     } 
40617     this.closable = false;
40618     this.loaded = false;
40619     this.active = false;
40620    
40621       
40622     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40623         
40624         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40625         
40626         this.wrapEl = this.el; //this.el.wrap();
40627         var ti = [];
40628         if (config.toolbar.items) {
40629             ti = config.toolbar.items ;
40630             delete config.toolbar.items ;
40631         }
40632         
40633         var nitems = [];
40634         this.toolbar.render(this.wrapEl, 'before');
40635         for(var i =0;i < ti.length;i++) {
40636           //  Roo.log(['add child', items[i]]);
40637             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40638         }
40639         this.toolbar.items = nitems;
40640         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40641         delete config.toolbar;
40642         
40643     }
40644     /*
40645     // xtype created footer. - not sure if will work as we normally have to render first..
40646     if (this.footer && !this.footer.el && this.footer.xtype) {
40647         if (!this.wrapEl) {
40648             this.wrapEl = this.el.wrap();
40649         }
40650     
40651         this.footer.container = this.wrapEl.createChild();
40652          
40653         this.footer = Roo.factory(this.footer, Roo);
40654         
40655     }
40656     */
40657     
40658      if(typeof config == "string"){
40659         this.title = config;
40660     }else{
40661         Roo.apply(this, config);
40662     }
40663     
40664     if(this.resizeEl){
40665         this.resizeEl = Roo.get(this.resizeEl, true);
40666     }else{
40667         this.resizeEl = this.el;
40668     }
40669     // handle view.xtype
40670     
40671  
40672     
40673     
40674     this.addEvents({
40675         /**
40676          * @event activate
40677          * Fires when this panel is activated. 
40678          * @param {Roo.ContentPanel} this
40679          */
40680         "activate" : true,
40681         /**
40682          * @event deactivate
40683          * Fires when this panel is activated. 
40684          * @param {Roo.ContentPanel} this
40685          */
40686         "deactivate" : true,
40687
40688         /**
40689          * @event resize
40690          * Fires when this panel is resized if fitToFrame is true.
40691          * @param {Roo.ContentPanel} this
40692          * @param {Number} width The width after any component adjustments
40693          * @param {Number} height The height after any component adjustments
40694          */
40695         "resize" : true,
40696         
40697          /**
40698          * @event render
40699          * Fires when this tab is created
40700          * @param {Roo.ContentPanel} this
40701          */
40702         "render" : true,
40703         
40704           /**
40705          * @event scroll
40706          * Fires when this content is scrolled
40707          * @param {Roo.ContentPanel} this
40708          * @param {Event} scrollEvent
40709          */
40710         "scroll" : true
40711         
40712         
40713         
40714     });
40715     
40716
40717     
40718     
40719     if(this.autoScroll && !this.iframe){
40720         this.resizeEl.setStyle("overflow", "auto");
40721         this.resizeEl.on('scroll', this.onScroll, this);
40722     } else {
40723         // fix randome scrolling
40724         //this.el.on('scroll', function() {
40725         //    Roo.log('fix random scolling');
40726         //    this.scrollTo('top',0); 
40727         //});
40728     }
40729     content = content || this.content;
40730     if(content){
40731         this.setContent(content);
40732     }
40733     if(config && config.url){
40734         this.setUrl(this.url, this.params, this.loadOnce);
40735     }
40736     
40737     
40738     
40739     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40740     
40741     if (this.view && typeof(this.view.xtype) != 'undefined') {
40742         this.view.el = this.el.appendChild(document.createElement("div"));
40743         this.view = Roo.factory(this.view); 
40744         this.view.render  &&  this.view.render(false, '');  
40745     }
40746     
40747     
40748     this.fireEvent('render', this);
40749 };
40750
40751 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40752     
40753     cls : '',
40754     background : '',
40755     
40756     tabTip : '',
40757     
40758     iframe : false,
40759     iframeEl : false,
40760     
40761     /* Resize Element - use this to work out scroll etc. */
40762     resizeEl : false,
40763     
40764     setRegion : function(region){
40765         this.region = region;
40766         this.setActiveClass(region && !this.background);
40767     },
40768     
40769     
40770     setActiveClass: function(state)
40771     {
40772         if(state){
40773            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40774            this.el.setStyle('position','relative');
40775         }else{
40776            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40777            this.el.setStyle('position', 'absolute');
40778         } 
40779     },
40780     
40781     /**
40782      * Returns the toolbar for this Panel if one was configured. 
40783      * @return {Roo.Toolbar} 
40784      */
40785     getToolbar : function(){
40786         return this.toolbar;
40787     },
40788     
40789     setActiveState : function(active)
40790     {
40791         this.active = active;
40792         this.setActiveClass(active);
40793         if(!active){
40794             if(this.fireEvent("deactivate", this) === false){
40795                 return false;
40796             }
40797             return true;
40798         }
40799         this.fireEvent("activate", this);
40800         return true;
40801     },
40802     /**
40803      * Updates this panel's element (not for iframe)
40804      * @param {String} content The new content
40805      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40806     */
40807     setContent : function(content, loadScripts){
40808         if (this.iframe) {
40809             return;
40810         }
40811         
40812         this.el.update(content, loadScripts);
40813     },
40814
40815     ignoreResize : function(w, h){
40816         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40817             return true;
40818         }else{
40819             this.lastSize = {width: w, height: h};
40820             return false;
40821         }
40822     },
40823     /**
40824      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40825      * @return {Roo.UpdateManager} The UpdateManager
40826      */
40827     getUpdateManager : function(){
40828         if (this.iframe) {
40829             return false;
40830         }
40831         return this.el.getUpdateManager();
40832     },
40833      /**
40834      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40835      * Does not work with IFRAME contents
40836      * @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:
40837 <pre><code>
40838 panel.load({
40839     url: "your-url.php",
40840     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40841     callback: yourFunction,
40842     scope: yourObject, //(optional scope)
40843     discardUrl: false,
40844     nocache: false,
40845     text: "Loading...",
40846     timeout: 30,
40847     scripts: false
40848 });
40849 </code></pre>
40850      
40851      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40852      * 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.
40853      * @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}
40854      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40855      * @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.
40856      * @return {Roo.ContentPanel} this
40857      */
40858     load : function(){
40859         
40860         if (this.iframe) {
40861             return this;
40862         }
40863         
40864         var um = this.el.getUpdateManager();
40865         um.update.apply(um, arguments);
40866         return this;
40867     },
40868
40869
40870     /**
40871      * 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.
40872      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40873      * @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)
40874      * @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)
40875      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40876      */
40877     setUrl : function(url, params, loadOnce){
40878         if (this.iframe) {
40879             this.iframeEl.dom.src = url;
40880             return false;
40881         }
40882         
40883         if(this.refreshDelegate){
40884             this.removeListener("activate", this.refreshDelegate);
40885         }
40886         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40887         this.on("activate", this.refreshDelegate);
40888         return this.el.getUpdateManager();
40889     },
40890     
40891     _handleRefresh : function(url, params, loadOnce){
40892         if(!loadOnce || !this.loaded){
40893             var updater = this.el.getUpdateManager();
40894             updater.update(url, params, this._setLoaded.createDelegate(this));
40895         }
40896     },
40897     
40898     _setLoaded : function(){
40899         this.loaded = true;
40900     }, 
40901     
40902     /**
40903      * Returns this panel's id
40904      * @return {String} 
40905      */
40906     getId : function(){
40907         return this.el.id;
40908     },
40909     
40910     /** 
40911      * Returns this panel's element - used by regiosn to add.
40912      * @return {Roo.Element} 
40913      */
40914     getEl : function(){
40915         return this.wrapEl || this.el;
40916     },
40917     
40918    
40919     
40920     adjustForComponents : function(width, height)
40921     {
40922         //Roo.log('adjustForComponents ');
40923         if(this.resizeEl != this.el){
40924             width -= this.el.getFrameWidth('lr');
40925             height -= this.el.getFrameWidth('tb');
40926         }
40927         if(this.toolbar){
40928             var te = this.toolbar.getEl();
40929             te.setWidth(width);
40930             height -= te.getHeight();
40931         }
40932         if(this.footer){
40933             var te = this.footer.getEl();
40934             te.setWidth(width);
40935             height -= te.getHeight();
40936         }
40937         
40938         
40939         if(this.adjustments){
40940             width += this.adjustments[0];
40941             height += this.adjustments[1];
40942         }
40943         return {"width": width, "height": height};
40944     },
40945     
40946     setSize : function(width, height){
40947         if(this.fitToFrame && !this.ignoreResize(width, height)){
40948             if(this.fitContainer && this.resizeEl != this.el){
40949                 this.el.setSize(width, height);
40950             }
40951             var size = this.adjustForComponents(width, height);
40952             if (this.iframe) {
40953                 this.iframeEl.setSize(width,height);
40954             }
40955             
40956             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40957             this.fireEvent('resize', this, size.width, size.height);
40958             
40959             
40960         }
40961     },
40962     
40963     /**
40964      * Returns this panel's title
40965      * @return {String} 
40966      */
40967     getTitle : function(){
40968         
40969         if (typeof(this.title) != 'object') {
40970             return this.title;
40971         }
40972         
40973         var t = '';
40974         for (var k in this.title) {
40975             if (!this.title.hasOwnProperty(k)) {
40976                 continue;
40977             }
40978             
40979             if (k.indexOf('-') >= 0) {
40980                 var s = k.split('-');
40981                 for (var i = 0; i<s.length; i++) {
40982                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40983                 }
40984             } else {
40985                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40986             }
40987         }
40988         return t;
40989     },
40990     
40991     /**
40992      * Set this panel's title
40993      * @param {String} title
40994      */
40995     setTitle : function(title){
40996         this.title = title;
40997         if(this.region){
40998             this.region.updatePanelTitle(this, title);
40999         }
41000     },
41001     
41002     /**
41003      * Returns true is this panel was configured to be closable
41004      * @return {Boolean} 
41005      */
41006     isClosable : function(){
41007         return this.closable;
41008     },
41009     
41010     beforeSlide : function(){
41011         this.el.clip();
41012         this.resizeEl.clip();
41013     },
41014     
41015     afterSlide : function(){
41016         this.el.unclip();
41017         this.resizeEl.unclip();
41018     },
41019     
41020     /**
41021      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41022      *   Will fail silently if the {@link #setUrl} method has not been called.
41023      *   This does not activate the panel, just updates its content.
41024      */
41025     refresh : function(){
41026         if(this.refreshDelegate){
41027            this.loaded = false;
41028            this.refreshDelegate();
41029         }
41030     },
41031     
41032     /**
41033      * Destroys this panel
41034      */
41035     destroy : function(){
41036         this.el.removeAllListeners();
41037         var tempEl = document.createElement("span");
41038         tempEl.appendChild(this.el.dom);
41039         tempEl.innerHTML = "";
41040         this.el.remove();
41041         this.el = null;
41042     },
41043     
41044     /**
41045      * form - if the content panel contains a form - this is a reference to it.
41046      * @type {Roo.form.Form}
41047      */
41048     form : false,
41049     /**
41050      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41051      *    This contains a reference to it.
41052      * @type {Roo.View}
41053      */
41054     view : false,
41055     
41056       /**
41057      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41058      * <pre><code>
41059
41060 layout.addxtype({
41061        xtype : 'Form',
41062        items: [ .... ]
41063    }
41064 );
41065
41066 </code></pre>
41067      * @param {Object} cfg Xtype definition of item to add.
41068      */
41069     
41070     
41071     getChildContainer: function () {
41072         return this.getEl();
41073     },
41074     
41075     
41076     onScroll : function(e)
41077     {
41078         this.fireEvent('scroll', this, e);
41079     }
41080     
41081     
41082     /*
41083         var  ret = new Roo.factory(cfg);
41084         return ret;
41085         
41086         
41087         // add form..
41088         if (cfg.xtype.match(/^Form$/)) {
41089             
41090             var el;
41091             //if (this.footer) {
41092             //    el = this.footer.container.insertSibling(false, 'before');
41093             //} else {
41094                 el = this.el.createChild();
41095             //}
41096
41097             this.form = new  Roo.form.Form(cfg);
41098             
41099             
41100             if ( this.form.allItems.length) {
41101                 this.form.render(el.dom);
41102             }
41103             return this.form;
41104         }
41105         // should only have one of theses..
41106         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41107             // views.. should not be just added - used named prop 'view''
41108             
41109             cfg.el = this.el.appendChild(document.createElement("div"));
41110             // factory?
41111             
41112             var ret = new Roo.factory(cfg);
41113              
41114              ret.render && ret.render(false, ''); // render blank..
41115             this.view = ret;
41116             return ret;
41117         }
41118         return false;
41119     }
41120     \*/
41121 });
41122  
41123 /**
41124  * @class Roo.bootstrap.panel.Grid
41125  * @extends Roo.bootstrap.panel.Content
41126  * @constructor
41127  * Create a new GridPanel.
41128  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41129  * @param {Object} config A the config object
41130   
41131  */
41132
41133
41134
41135 Roo.bootstrap.panel.Grid = function(config)
41136 {
41137     
41138       
41139     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41140         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41141
41142     config.el = this.wrapper;
41143     //this.el = this.wrapper;
41144     
41145       if (config.container) {
41146         // ctor'ed from a Border/panel.grid
41147         
41148         
41149         this.wrapper.setStyle("overflow", "hidden");
41150         this.wrapper.addClass('roo-grid-container');
41151
41152     }
41153     
41154     
41155     if(config.toolbar){
41156         var tool_el = this.wrapper.createChild();    
41157         this.toolbar = Roo.factory(config.toolbar);
41158         var ti = [];
41159         if (config.toolbar.items) {
41160             ti = config.toolbar.items ;
41161             delete config.toolbar.items ;
41162         }
41163         
41164         var nitems = [];
41165         this.toolbar.render(tool_el);
41166         for(var i =0;i < ti.length;i++) {
41167           //  Roo.log(['add child', items[i]]);
41168             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41169         }
41170         this.toolbar.items = nitems;
41171         
41172         delete config.toolbar;
41173     }
41174     
41175     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41176     config.grid.scrollBody = true;;
41177     config.grid.monitorWindowResize = false; // turn off autosizing
41178     config.grid.autoHeight = false;
41179     config.grid.autoWidth = false;
41180     
41181     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41182     
41183     if (config.background) {
41184         // render grid on panel activation (if panel background)
41185         this.on('activate', function(gp) {
41186             if (!gp.grid.rendered) {
41187                 gp.grid.render(this.wrapper);
41188                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41189             }
41190         });
41191             
41192     } else {
41193         this.grid.render(this.wrapper);
41194         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41195
41196     }
41197     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41198     // ??? needed ??? config.el = this.wrapper;
41199     
41200     
41201     
41202   
41203     // xtype created footer. - not sure if will work as we normally have to render first..
41204     if (this.footer && !this.footer.el && this.footer.xtype) {
41205         
41206         var ctr = this.grid.getView().getFooterPanel(true);
41207         this.footer.dataSource = this.grid.dataSource;
41208         this.footer = Roo.factory(this.footer, Roo);
41209         this.footer.render(ctr);
41210         
41211     }
41212     
41213     
41214     
41215     
41216      
41217 };
41218
41219 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41220     getId : function(){
41221         return this.grid.id;
41222     },
41223     
41224     /**
41225      * Returns the grid for this panel
41226      * @return {Roo.bootstrap.Table} 
41227      */
41228     getGrid : function(){
41229         return this.grid;    
41230     },
41231     
41232     setSize : function(width, height){
41233         if(!this.ignoreResize(width, height)){
41234             var grid = this.grid;
41235             var size = this.adjustForComponents(width, height);
41236             // tfoot is not a footer?
41237           
41238             
41239             var gridel = grid.getGridEl();
41240             gridel.setSize(size.width, size.height);
41241             
41242             var tbd = grid.getGridEl().select('tbody', true).first();
41243             var thd = grid.getGridEl().select('thead',true).first();
41244             var tbf= grid.getGridEl().select('tfoot', true).first();
41245
41246             if (tbf) {
41247                 size.height -= tbf.getHeight();
41248             }
41249             if (thd) {
41250                 size.height -= thd.getHeight();
41251             }
41252             
41253             tbd.setSize(size.width, size.height );
41254             // this is for the account management tab -seems to work there.
41255             var thd = grid.getGridEl().select('thead',true).first();
41256             //if (tbd) {
41257             //    tbd.setSize(size.width, size.height - thd.getHeight());
41258             //}
41259              
41260             grid.autoSize();
41261         }
41262     },
41263      
41264     
41265     
41266     beforeSlide : function(){
41267         this.grid.getView().scroller.clip();
41268     },
41269     
41270     afterSlide : function(){
41271         this.grid.getView().scroller.unclip();
41272     },
41273     
41274     destroy : function(){
41275         this.grid.destroy();
41276         delete this.grid;
41277         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41278     }
41279 });
41280
41281 /**
41282  * @class Roo.bootstrap.panel.Nest
41283  * @extends Roo.bootstrap.panel.Content
41284  * @constructor
41285  * Create a new Panel, that can contain a layout.Border.
41286  * 
41287  * 
41288  * @param {Roo.BorderLayout} layout The layout for this panel
41289  * @param {String/Object} config A string to set only the title or a config object
41290  */
41291 Roo.bootstrap.panel.Nest = function(config)
41292 {
41293     // construct with only one argument..
41294     /* FIXME - implement nicer consturctors
41295     if (layout.layout) {
41296         config = layout;
41297         layout = config.layout;
41298         delete config.layout;
41299     }
41300     if (layout.xtype && !layout.getEl) {
41301         // then layout needs constructing..
41302         layout = Roo.factory(layout, Roo);
41303     }
41304     */
41305     
41306     config.el =  config.layout.getEl();
41307     
41308     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41309     
41310     config.layout.monitorWindowResize = false; // turn off autosizing
41311     this.layout = config.layout;
41312     this.layout.getEl().addClass("roo-layout-nested-layout");
41313     this.layout.parent = this;
41314     
41315     
41316     
41317     
41318 };
41319
41320 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41321
41322     setSize : function(width, height){
41323         if(!this.ignoreResize(width, height)){
41324             var size = this.adjustForComponents(width, height);
41325             var el = this.layout.getEl();
41326             if (size.height < 1) {
41327                 el.setWidth(size.width);   
41328             } else {
41329                 el.setSize(size.width, size.height);
41330             }
41331             var touch = el.dom.offsetWidth;
41332             this.layout.layout();
41333             // ie requires a double layout on the first pass
41334             if(Roo.isIE && !this.initialized){
41335                 this.initialized = true;
41336                 this.layout.layout();
41337             }
41338         }
41339     },
41340     
41341     // activate all subpanels if not currently active..
41342     
41343     setActiveState : function(active){
41344         this.active = active;
41345         this.setActiveClass(active);
41346         
41347         if(!active){
41348             this.fireEvent("deactivate", this);
41349             return;
41350         }
41351         
41352         this.fireEvent("activate", this);
41353         // not sure if this should happen before or after..
41354         if (!this.layout) {
41355             return; // should not happen..
41356         }
41357         var reg = false;
41358         for (var r in this.layout.regions) {
41359             reg = this.layout.getRegion(r);
41360             if (reg.getActivePanel()) {
41361                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41362                 reg.setActivePanel(reg.getActivePanel());
41363                 continue;
41364             }
41365             if (!reg.panels.length) {
41366                 continue;
41367             }
41368             reg.showPanel(reg.getPanel(0));
41369         }
41370         
41371         
41372         
41373         
41374     },
41375     
41376     /**
41377      * Returns the nested BorderLayout for this panel
41378      * @return {Roo.BorderLayout} 
41379      */
41380     getLayout : function(){
41381         return this.layout;
41382     },
41383     
41384      /**
41385      * Adds a xtype elements to the layout of the nested panel
41386      * <pre><code>
41387
41388 panel.addxtype({
41389        xtype : 'ContentPanel',
41390        region: 'west',
41391        items: [ .... ]
41392    }
41393 );
41394
41395 panel.addxtype({
41396         xtype : 'NestedLayoutPanel',
41397         region: 'west',
41398         layout: {
41399            center: { },
41400            west: { }   
41401         },
41402         items : [ ... list of content panels or nested layout panels.. ]
41403    }
41404 );
41405 </code></pre>
41406      * @param {Object} cfg Xtype definition of item to add.
41407      */
41408     addxtype : function(cfg) {
41409         return this.layout.addxtype(cfg);
41410     
41411     }
41412 });/*
41413  * Based on:
41414  * Ext JS Library 1.1.1
41415  * Copyright(c) 2006-2007, Ext JS, LLC.
41416  *
41417  * Originally Released Under LGPL - original licence link has changed is not relivant.
41418  *
41419  * Fork - LGPL
41420  * <script type="text/javascript">
41421  */
41422 /**
41423  * @class Roo.TabPanel
41424  * @extends Roo.util.Observable
41425  * A lightweight tab container.
41426  * <br><br>
41427  * Usage:
41428  * <pre><code>
41429 // basic tabs 1, built from existing content
41430 var tabs = new Roo.TabPanel("tabs1");
41431 tabs.addTab("script", "View Script");
41432 tabs.addTab("markup", "View Markup");
41433 tabs.activate("script");
41434
41435 // more advanced tabs, built from javascript
41436 var jtabs = new Roo.TabPanel("jtabs");
41437 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41438
41439 // set up the UpdateManager
41440 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41441 var updater = tab2.getUpdateManager();
41442 updater.setDefaultUrl("ajax1.htm");
41443 tab2.on('activate', updater.refresh, updater, true);
41444
41445 // Use setUrl for Ajax loading
41446 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41447 tab3.setUrl("ajax2.htm", null, true);
41448
41449 // Disabled tab
41450 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41451 tab4.disable();
41452
41453 jtabs.activate("jtabs-1");
41454  * </code></pre>
41455  * @constructor
41456  * Create a new TabPanel.
41457  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41458  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41459  */
41460 Roo.bootstrap.panel.Tabs = function(config){
41461     /**
41462     * The container element for this TabPanel.
41463     * @type Roo.Element
41464     */
41465     this.el = Roo.get(config.el);
41466     delete config.el;
41467     if(config){
41468         if(typeof config == "boolean"){
41469             this.tabPosition = config ? "bottom" : "top";
41470         }else{
41471             Roo.apply(this, config);
41472         }
41473     }
41474     
41475     if(this.tabPosition == "bottom"){
41476         // if tabs are at the bottom = create the body first.
41477         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41478         this.el.addClass("roo-tabs-bottom");
41479     }
41480     // next create the tabs holders
41481     
41482     if (this.tabPosition == "west"){
41483         
41484         var reg = this.region; // fake it..
41485         while (reg) {
41486             if (!reg.mgr.parent) {
41487                 break;
41488             }
41489             reg = reg.mgr.parent.region;
41490         }
41491         Roo.log("got nest?");
41492         Roo.log(reg);
41493         if (reg.mgr.getRegion('west')) {
41494             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41495             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41496             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41497             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41498             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41499         
41500             
41501         }
41502         
41503         
41504     } else {
41505      
41506         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41507         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41508         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41509         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41510     }
41511     
41512     
41513     if(Roo.isIE){
41514         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41515     }
41516     
41517     // finally - if tabs are at the top, then create the body last..
41518     if(this.tabPosition != "bottom"){
41519         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41520          * @type Roo.Element
41521          */
41522         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41523         this.el.addClass("roo-tabs-top");
41524     }
41525     this.items = [];
41526
41527     this.bodyEl.setStyle("position", "relative");
41528
41529     this.active = null;
41530     this.activateDelegate = this.activate.createDelegate(this);
41531
41532     this.addEvents({
41533         /**
41534          * @event tabchange
41535          * Fires when the active tab changes
41536          * @param {Roo.TabPanel} this
41537          * @param {Roo.TabPanelItem} activePanel The new active tab
41538          */
41539         "tabchange": true,
41540         /**
41541          * @event beforetabchange
41542          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41543          * @param {Roo.TabPanel} this
41544          * @param {Object} e Set cancel to true on this object to cancel the tab change
41545          * @param {Roo.TabPanelItem} tab The tab being changed to
41546          */
41547         "beforetabchange" : true
41548     });
41549
41550     Roo.EventManager.onWindowResize(this.onResize, this);
41551     this.cpad = this.el.getPadding("lr");
41552     this.hiddenCount = 0;
41553
41554
41555     // toolbar on the tabbar support...
41556     if (this.toolbar) {
41557         alert("no toolbar support yet");
41558         this.toolbar  = false;
41559         /*
41560         var tcfg = this.toolbar;
41561         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41562         this.toolbar = new Roo.Toolbar(tcfg);
41563         if (Roo.isSafari) {
41564             var tbl = tcfg.container.child('table', true);
41565             tbl.setAttribute('width', '100%');
41566         }
41567         */
41568         
41569     }
41570    
41571
41572
41573     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41574 };
41575
41576 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41577     /*
41578      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41579      */
41580     tabPosition : "top",
41581     /*
41582      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41583      */
41584     currentTabWidth : 0,
41585     /*
41586      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41587      */
41588     minTabWidth : 40,
41589     /*
41590      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41591      */
41592     maxTabWidth : 250,
41593     /*
41594      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41595      */
41596     preferredTabWidth : 175,
41597     /*
41598      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41599      */
41600     resizeTabs : false,
41601     /*
41602      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41603      */
41604     monitorResize : true,
41605     /*
41606      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41607      */
41608     toolbar : false,  // set by caller..
41609     
41610     region : false, /// set by caller
41611     
41612     disableTooltips : true, // not used yet...
41613
41614     /**
41615      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41616      * @param {String} id The id of the div to use <b>or create</b>
41617      * @param {String} text The text for the tab
41618      * @param {String} content (optional) Content to put in the TabPanelItem body
41619      * @param {Boolean} closable (optional) True to create a close icon on the tab
41620      * @return {Roo.TabPanelItem} The created TabPanelItem
41621      */
41622     addTab : function(id, text, content, closable, tpl)
41623     {
41624         var item = new Roo.bootstrap.panel.TabItem({
41625             panel: this,
41626             id : id,
41627             text : text,
41628             closable : closable,
41629             tpl : tpl
41630         });
41631         this.addTabItem(item);
41632         if(content){
41633             item.setContent(content);
41634         }
41635         return item;
41636     },
41637
41638     /**
41639      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41640      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41641      * @return {Roo.TabPanelItem}
41642      */
41643     getTab : function(id){
41644         return this.items[id];
41645     },
41646
41647     /**
41648      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41649      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41650      */
41651     hideTab : function(id){
41652         var t = this.items[id];
41653         if(!t.isHidden()){
41654            t.setHidden(true);
41655            this.hiddenCount++;
41656            this.autoSizeTabs();
41657         }
41658     },
41659
41660     /**
41661      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41662      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41663      */
41664     unhideTab : function(id){
41665         var t = this.items[id];
41666         if(t.isHidden()){
41667            t.setHidden(false);
41668            this.hiddenCount--;
41669            this.autoSizeTabs();
41670         }
41671     },
41672
41673     /**
41674      * Adds an existing {@link Roo.TabPanelItem}.
41675      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41676      */
41677     addTabItem : function(item)
41678     {
41679         this.items[item.id] = item;
41680         this.items.push(item);
41681         this.autoSizeTabs();
41682       //  if(this.resizeTabs){
41683     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41684   //         this.autoSizeTabs();
41685 //        }else{
41686 //            item.autoSize();
41687        // }
41688     },
41689
41690     /**
41691      * Removes a {@link Roo.TabPanelItem}.
41692      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41693      */
41694     removeTab : function(id){
41695         var items = this.items;
41696         var tab = items[id];
41697         if(!tab) { return; }
41698         var index = items.indexOf(tab);
41699         if(this.active == tab && items.length > 1){
41700             var newTab = this.getNextAvailable(index);
41701             if(newTab) {
41702                 newTab.activate();
41703             }
41704         }
41705         this.stripEl.dom.removeChild(tab.pnode.dom);
41706         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41707             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41708         }
41709         items.splice(index, 1);
41710         delete this.items[tab.id];
41711         tab.fireEvent("close", tab);
41712         tab.purgeListeners();
41713         this.autoSizeTabs();
41714     },
41715
41716     getNextAvailable : function(start){
41717         var items = this.items;
41718         var index = start;
41719         // look for a next tab that will slide over to
41720         // replace the one being removed
41721         while(index < items.length){
41722             var item = items[++index];
41723             if(item && !item.isHidden()){
41724                 return item;
41725             }
41726         }
41727         // if one isn't found select the previous tab (on the left)
41728         index = start;
41729         while(index >= 0){
41730             var item = items[--index];
41731             if(item && !item.isHidden()){
41732                 return item;
41733             }
41734         }
41735         return null;
41736     },
41737
41738     /**
41739      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41740      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41741      */
41742     disableTab : function(id){
41743         var tab = this.items[id];
41744         if(tab && this.active != tab){
41745             tab.disable();
41746         }
41747     },
41748
41749     /**
41750      * Enables a {@link Roo.TabPanelItem} that is disabled.
41751      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41752      */
41753     enableTab : function(id){
41754         var tab = this.items[id];
41755         tab.enable();
41756     },
41757
41758     /**
41759      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41760      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41761      * @return {Roo.TabPanelItem} The TabPanelItem.
41762      */
41763     activate : function(id)
41764     {
41765         //Roo.log('activite:'  + id);
41766         
41767         var tab = this.items[id];
41768         if(!tab){
41769             return null;
41770         }
41771         if(tab == this.active || tab.disabled){
41772             return tab;
41773         }
41774         var e = {};
41775         this.fireEvent("beforetabchange", this, e, tab);
41776         if(e.cancel !== true && !tab.disabled){
41777             if(this.active){
41778                 this.active.hide();
41779             }
41780             this.active = this.items[id];
41781             this.active.show();
41782             this.fireEvent("tabchange", this, this.active);
41783         }
41784         return tab;
41785     },
41786
41787     /**
41788      * Gets the active {@link Roo.TabPanelItem}.
41789      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41790      */
41791     getActiveTab : function(){
41792         return this.active;
41793     },
41794
41795     /**
41796      * Updates the tab body element to fit the height of the container element
41797      * for overflow scrolling
41798      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41799      */
41800     syncHeight : function(targetHeight){
41801         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41802         var bm = this.bodyEl.getMargins();
41803         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41804         this.bodyEl.setHeight(newHeight);
41805         return newHeight;
41806     },
41807
41808     onResize : function(){
41809         if(this.monitorResize){
41810             this.autoSizeTabs();
41811         }
41812     },
41813
41814     /**
41815      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41816      */
41817     beginUpdate : function(){
41818         this.updating = true;
41819     },
41820
41821     /**
41822      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41823      */
41824     endUpdate : function(){
41825         this.updating = false;
41826         this.autoSizeTabs();
41827     },
41828
41829     /**
41830      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41831      */
41832     autoSizeTabs : function()
41833     {
41834         var count = this.items.length;
41835         var vcount = count - this.hiddenCount;
41836         
41837         if (vcount < 2) {
41838             this.stripEl.hide();
41839         } else {
41840             this.stripEl.show();
41841         }
41842         
41843         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41844             return;
41845         }
41846         
41847         
41848         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41849         var availWidth = Math.floor(w / vcount);
41850         var b = this.stripBody;
41851         if(b.getWidth() > w){
41852             var tabs = this.items;
41853             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41854             if(availWidth < this.minTabWidth){
41855                 /*if(!this.sleft){    // incomplete scrolling code
41856                     this.createScrollButtons();
41857                 }
41858                 this.showScroll();
41859                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41860             }
41861         }else{
41862             if(this.currentTabWidth < this.preferredTabWidth){
41863                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41864             }
41865         }
41866     },
41867
41868     /**
41869      * Returns the number of tabs in this TabPanel.
41870      * @return {Number}
41871      */
41872      getCount : function(){
41873          return this.items.length;
41874      },
41875
41876     /**
41877      * Resizes all the tabs to the passed width
41878      * @param {Number} The new width
41879      */
41880     setTabWidth : function(width){
41881         this.currentTabWidth = width;
41882         for(var i = 0, len = this.items.length; i < len; i++) {
41883                 if(!this.items[i].isHidden()) {
41884                 this.items[i].setWidth(width);
41885             }
41886         }
41887     },
41888
41889     /**
41890      * Destroys this TabPanel
41891      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41892      */
41893     destroy : function(removeEl){
41894         Roo.EventManager.removeResizeListener(this.onResize, this);
41895         for(var i = 0, len = this.items.length; i < len; i++){
41896             this.items[i].purgeListeners();
41897         }
41898         if(removeEl === true){
41899             this.el.update("");
41900             this.el.remove();
41901         }
41902     },
41903     
41904     createStrip : function(container)
41905     {
41906         var strip = document.createElement("nav");
41907         strip.className = Roo.bootstrap.version == 4 ?
41908             "navbar-light bg-light" : 
41909             "navbar navbar-default"; //"x-tabs-wrap";
41910         container.appendChild(strip);
41911         return strip;
41912     },
41913     
41914     createStripList : function(strip)
41915     {
41916         // div wrapper for retard IE
41917         // returns the "tr" element.
41918         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41919         //'<div class="x-tabs-strip-wrap">'+
41920           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41921           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41922         return strip.firstChild; //.firstChild.firstChild.firstChild;
41923     },
41924     createBody : function(container)
41925     {
41926         var body = document.createElement("div");
41927         Roo.id(body, "tab-body");
41928         //Roo.fly(body).addClass("x-tabs-body");
41929         Roo.fly(body).addClass("tab-content");
41930         container.appendChild(body);
41931         return body;
41932     },
41933     createItemBody :function(bodyEl, id){
41934         var body = Roo.getDom(id);
41935         if(!body){
41936             body = document.createElement("div");
41937             body.id = id;
41938         }
41939         //Roo.fly(body).addClass("x-tabs-item-body");
41940         Roo.fly(body).addClass("tab-pane");
41941          bodyEl.insertBefore(body, bodyEl.firstChild);
41942         return body;
41943     },
41944     /** @private */
41945     createStripElements :  function(stripEl, text, closable, tpl)
41946     {
41947         var td = document.createElement("li"); // was td..
41948         td.className = 'nav-item';
41949         
41950         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41951         
41952         
41953         stripEl.appendChild(td);
41954         /*if(closable){
41955             td.className = "x-tabs-closable";
41956             if(!this.closeTpl){
41957                 this.closeTpl = new Roo.Template(
41958                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41959                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41960                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41961                 );
41962             }
41963             var el = this.closeTpl.overwrite(td, {"text": text});
41964             var close = el.getElementsByTagName("div")[0];
41965             var inner = el.getElementsByTagName("em")[0];
41966             return {"el": el, "close": close, "inner": inner};
41967         } else {
41968         */
41969         // not sure what this is..
41970 //            if(!this.tabTpl){
41971                 //this.tabTpl = new Roo.Template(
41972                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41973                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41974                 //);
41975 //                this.tabTpl = new Roo.Template(
41976 //                   '<a href="#">' +
41977 //                   '<span unselectable="on"' +
41978 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41979 //                            ' >{text}</span></a>'
41980 //                );
41981 //                
41982 //            }
41983
41984
41985             var template = tpl || this.tabTpl || false;
41986             
41987             if(!template){
41988                 template =  new Roo.Template(
41989                         Roo.bootstrap.version == 4 ? 
41990                             (
41991                                 '<a class="nav-link" href="#" unselectable="on"' +
41992                                      (this.disableTooltips ? '' : ' title="{text}"') +
41993                                      ' >{text}</a>'
41994                             ) : (
41995                                 '<a class="nav-link" href="#">' +
41996                                 '<span unselectable="on"' +
41997                                          (this.disableTooltips ? '' : ' title="{text}"') +
41998                                     ' >{text}</span></a>'
41999                             )
42000                 );
42001             }
42002             
42003             switch (typeof(template)) {
42004                 case 'object' :
42005                     break;
42006                 case 'string' :
42007                     template = new Roo.Template(template);
42008                     break;
42009                 default :
42010                     break;
42011             }
42012             
42013             var el = template.overwrite(td, {"text": text});
42014             
42015             var inner = el.getElementsByTagName("span")[0];
42016             
42017             return {"el": el, "inner": inner};
42018             
42019     }
42020         
42021     
42022 });
42023
42024 /**
42025  * @class Roo.TabPanelItem
42026  * @extends Roo.util.Observable
42027  * Represents an individual item (tab plus body) in a TabPanel.
42028  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42029  * @param {String} id The id of this TabPanelItem
42030  * @param {String} text The text for the tab of this TabPanelItem
42031  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42032  */
42033 Roo.bootstrap.panel.TabItem = function(config){
42034     /**
42035      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42036      * @type Roo.TabPanel
42037      */
42038     this.tabPanel = config.panel;
42039     /**
42040      * The id for this TabPanelItem
42041      * @type String
42042      */
42043     this.id = config.id;
42044     /** @private */
42045     this.disabled = false;
42046     /** @private */
42047     this.text = config.text;
42048     /** @private */
42049     this.loaded = false;
42050     this.closable = config.closable;
42051
42052     /**
42053      * The body element for this TabPanelItem.
42054      * @type Roo.Element
42055      */
42056     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42057     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42058     this.bodyEl.setStyle("display", "block");
42059     this.bodyEl.setStyle("zoom", "1");
42060     //this.hideAction();
42061
42062     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42063     /** @private */
42064     this.el = Roo.get(els.el);
42065     this.inner = Roo.get(els.inner, true);
42066      this.textEl = Roo.bootstrap.version == 4 ?
42067         this.el : Roo.get(this.el.dom.firstChild, true);
42068
42069     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42070     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42071
42072     
42073 //    this.el.on("mousedown", this.onTabMouseDown, this);
42074     this.el.on("click", this.onTabClick, this);
42075     /** @private */
42076     if(config.closable){
42077         var c = Roo.get(els.close, true);
42078         c.dom.title = this.closeText;
42079         c.addClassOnOver("close-over");
42080         c.on("click", this.closeClick, this);
42081      }
42082
42083     this.addEvents({
42084          /**
42085          * @event activate
42086          * Fires when this tab becomes the active tab.
42087          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42088          * @param {Roo.TabPanelItem} this
42089          */
42090         "activate": true,
42091         /**
42092          * @event beforeclose
42093          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42094          * @param {Roo.TabPanelItem} this
42095          * @param {Object} e Set cancel to true on this object to cancel the close.
42096          */
42097         "beforeclose": true,
42098         /**
42099          * @event close
42100          * Fires when this tab is closed.
42101          * @param {Roo.TabPanelItem} this
42102          */
42103          "close": true,
42104         /**
42105          * @event deactivate
42106          * Fires when this tab is no longer the active tab.
42107          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42108          * @param {Roo.TabPanelItem} this
42109          */
42110          "deactivate" : true
42111     });
42112     this.hidden = false;
42113
42114     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42115 };
42116
42117 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42118            {
42119     purgeListeners : function(){
42120        Roo.util.Observable.prototype.purgeListeners.call(this);
42121        this.el.removeAllListeners();
42122     },
42123     /**
42124      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42125      */
42126     show : function(){
42127         this.status_node.addClass("active");
42128         this.showAction();
42129         if(Roo.isOpera){
42130             this.tabPanel.stripWrap.repaint();
42131         }
42132         this.fireEvent("activate", this.tabPanel, this);
42133     },
42134
42135     /**
42136      * Returns true if this tab is the active tab.
42137      * @return {Boolean}
42138      */
42139     isActive : function(){
42140         return this.tabPanel.getActiveTab() == this;
42141     },
42142
42143     /**
42144      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42145      */
42146     hide : function(){
42147         this.status_node.removeClass("active");
42148         this.hideAction();
42149         this.fireEvent("deactivate", this.tabPanel, this);
42150     },
42151
42152     hideAction : function(){
42153         this.bodyEl.hide();
42154         this.bodyEl.setStyle("position", "absolute");
42155         this.bodyEl.setLeft("-20000px");
42156         this.bodyEl.setTop("-20000px");
42157     },
42158
42159     showAction : function(){
42160         this.bodyEl.setStyle("position", "relative");
42161         this.bodyEl.setTop("");
42162         this.bodyEl.setLeft("");
42163         this.bodyEl.show();
42164     },
42165
42166     /**
42167      * Set the tooltip for the tab.
42168      * @param {String} tooltip The tab's tooltip
42169      */
42170     setTooltip : function(text){
42171         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42172             this.textEl.dom.qtip = text;
42173             this.textEl.dom.removeAttribute('title');
42174         }else{
42175             this.textEl.dom.title = text;
42176         }
42177     },
42178
42179     onTabClick : function(e){
42180         e.preventDefault();
42181         this.tabPanel.activate(this.id);
42182     },
42183
42184     onTabMouseDown : function(e){
42185         e.preventDefault();
42186         this.tabPanel.activate(this.id);
42187     },
42188 /*
42189     getWidth : function(){
42190         return this.inner.getWidth();
42191     },
42192
42193     setWidth : function(width){
42194         var iwidth = width - this.linode.getPadding("lr");
42195         this.inner.setWidth(iwidth);
42196         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42197         this.linode.setWidth(width);
42198     },
42199 */
42200     /**
42201      * Show or hide the tab
42202      * @param {Boolean} hidden True to hide or false to show.
42203      */
42204     setHidden : function(hidden){
42205         this.hidden = hidden;
42206         this.linode.setStyle("display", hidden ? "none" : "");
42207     },
42208
42209     /**
42210      * Returns true if this tab is "hidden"
42211      * @return {Boolean}
42212      */
42213     isHidden : function(){
42214         return this.hidden;
42215     },
42216
42217     /**
42218      * Returns the text for this tab
42219      * @return {String}
42220      */
42221     getText : function(){
42222         return this.text;
42223     },
42224     /*
42225     autoSize : function(){
42226         //this.el.beginMeasure();
42227         this.textEl.setWidth(1);
42228         /*
42229          *  #2804 [new] Tabs in Roojs
42230          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42231          */
42232         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42233         //this.el.endMeasure();
42234     //},
42235
42236     /**
42237      * Sets the text for the tab (Note: this also sets the tooltip text)
42238      * @param {String} text The tab's text and tooltip
42239      */
42240     setText : function(text){
42241         this.text = text;
42242         this.textEl.update(text);
42243         this.setTooltip(text);
42244         //if(!this.tabPanel.resizeTabs){
42245         //    this.autoSize();
42246         //}
42247     },
42248     /**
42249      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42250      */
42251     activate : function(){
42252         this.tabPanel.activate(this.id);
42253     },
42254
42255     /**
42256      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42257      */
42258     disable : function(){
42259         if(this.tabPanel.active != this){
42260             this.disabled = true;
42261             this.status_node.addClass("disabled");
42262         }
42263     },
42264
42265     /**
42266      * Enables this TabPanelItem if it was previously disabled.
42267      */
42268     enable : function(){
42269         this.disabled = false;
42270         this.status_node.removeClass("disabled");
42271     },
42272
42273     /**
42274      * Sets the content for this TabPanelItem.
42275      * @param {String} content The content
42276      * @param {Boolean} loadScripts true to look for and load scripts
42277      */
42278     setContent : function(content, loadScripts){
42279         this.bodyEl.update(content, loadScripts);
42280     },
42281
42282     /**
42283      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42284      * @return {Roo.UpdateManager} The UpdateManager
42285      */
42286     getUpdateManager : function(){
42287         return this.bodyEl.getUpdateManager();
42288     },
42289
42290     /**
42291      * Set a URL to be used to load the content for this TabPanelItem.
42292      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42293      * @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)
42294      * @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)
42295      * @return {Roo.UpdateManager} The UpdateManager
42296      */
42297     setUrl : function(url, params, loadOnce){
42298         if(this.refreshDelegate){
42299             this.un('activate', this.refreshDelegate);
42300         }
42301         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42302         this.on("activate", this.refreshDelegate);
42303         return this.bodyEl.getUpdateManager();
42304     },
42305
42306     /** @private */
42307     _handleRefresh : function(url, params, loadOnce){
42308         if(!loadOnce || !this.loaded){
42309             var updater = this.bodyEl.getUpdateManager();
42310             updater.update(url, params, this._setLoaded.createDelegate(this));
42311         }
42312     },
42313
42314     /**
42315      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42316      *   Will fail silently if the setUrl method has not been called.
42317      *   This does not activate the panel, just updates its content.
42318      */
42319     refresh : function(){
42320         if(this.refreshDelegate){
42321            this.loaded = false;
42322            this.refreshDelegate();
42323         }
42324     },
42325
42326     /** @private */
42327     _setLoaded : function(){
42328         this.loaded = true;
42329     },
42330
42331     /** @private */
42332     closeClick : function(e){
42333         var o = {};
42334         e.stopEvent();
42335         this.fireEvent("beforeclose", this, o);
42336         if(o.cancel !== true){
42337             this.tabPanel.removeTab(this.id);
42338         }
42339     },
42340     /**
42341      * The text displayed in the tooltip for the close icon.
42342      * @type String
42343      */
42344     closeText : "Close this tab"
42345 });
42346 /**
42347 *    This script refer to:
42348 *    Title: International Telephone Input
42349 *    Author: Jack O'Connor
42350 *    Code version:  v12.1.12
42351 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42352 **/
42353
42354 Roo.bootstrap.PhoneInputData = function() {
42355     var d = [
42356       [
42357         "Afghanistan (‫افغانستان‬‎)",
42358         "af",
42359         "93"
42360       ],
42361       [
42362         "Albania (Shqipëri)",
42363         "al",
42364         "355"
42365       ],
42366       [
42367         "Algeria (‫الجزائر‬‎)",
42368         "dz",
42369         "213"
42370       ],
42371       [
42372         "American Samoa",
42373         "as",
42374         "1684"
42375       ],
42376       [
42377         "Andorra",
42378         "ad",
42379         "376"
42380       ],
42381       [
42382         "Angola",
42383         "ao",
42384         "244"
42385       ],
42386       [
42387         "Anguilla",
42388         "ai",
42389         "1264"
42390       ],
42391       [
42392         "Antigua and Barbuda",
42393         "ag",
42394         "1268"
42395       ],
42396       [
42397         "Argentina",
42398         "ar",
42399         "54"
42400       ],
42401       [
42402         "Armenia (Հայաստան)",
42403         "am",
42404         "374"
42405       ],
42406       [
42407         "Aruba",
42408         "aw",
42409         "297"
42410       ],
42411       [
42412         "Australia",
42413         "au",
42414         "61",
42415         0
42416       ],
42417       [
42418         "Austria (Österreich)",
42419         "at",
42420         "43"
42421       ],
42422       [
42423         "Azerbaijan (Azərbaycan)",
42424         "az",
42425         "994"
42426       ],
42427       [
42428         "Bahamas",
42429         "bs",
42430         "1242"
42431       ],
42432       [
42433         "Bahrain (‫البحرين‬‎)",
42434         "bh",
42435         "973"
42436       ],
42437       [
42438         "Bangladesh (বাংলাদেশ)",
42439         "bd",
42440         "880"
42441       ],
42442       [
42443         "Barbados",
42444         "bb",
42445         "1246"
42446       ],
42447       [
42448         "Belarus (Беларусь)",
42449         "by",
42450         "375"
42451       ],
42452       [
42453         "Belgium (België)",
42454         "be",
42455         "32"
42456       ],
42457       [
42458         "Belize",
42459         "bz",
42460         "501"
42461       ],
42462       [
42463         "Benin (Bénin)",
42464         "bj",
42465         "229"
42466       ],
42467       [
42468         "Bermuda",
42469         "bm",
42470         "1441"
42471       ],
42472       [
42473         "Bhutan (འབྲུག)",
42474         "bt",
42475         "975"
42476       ],
42477       [
42478         "Bolivia",
42479         "bo",
42480         "591"
42481       ],
42482       [
42483         "Bosnia and Herzegovina (Босна и Херцеговина)",
42484         "ba",
42485         "387"
42486       ],
42487       [
42488         "Botswana",
42489         "bw",
42490         "267"
42491       ],
42492       [
42493         "Brazil (Brasil)",
42494         "br",
42495         "55"
42496       ],
42497       [
42498         "British Indian Ocean Territory",
42499         "io",
42500         "246"
42501       ],
42502       [
42503         "British Virgin Islands",
42504         "vg",
42505         "1284"
42506       ],
42507       [
42508         "Brunei",
42509         "bn",
42510         "673"
42511       ],
42512       [
42513         "Bulgaria (България)",
42514         "bg",
42515         "359"
42516       ],
42517       [
42518         "Burkina Faso",
42519         "bf",
42520         "226"
42521       ],
42522       [
42523         "Burundi (Uburundi)",
42524         "bi",
42525         "257"
42526       ],
42527       [
42528         "Cambodia (កម្ពុជា)",
42529         "kh",
42530         "855"
42531       ],
42532       [
42533         "Cameroon (Cameroun)",
42534         "cm",
42535         "237"
42536       ],
42537       [
42538         "Canada",
42539         "ca",
42540         "1",
42541         1,
42542         ["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"]
42543       ],
42544       [
42545         "Cape Verde (Kabu Verdi)",
42546         "cv",
42547         "238"
42548       ],
42549       [
42550         "Caribbean Netherlands",
42551         "bq",
42552         "599",
42553         1
42554       ],
42555       [
42556         "Cayman Islands",
42557         "ky",
42558         "1345"
42559       ],
42560       [
42561         "Central African Republic (République centrafricaine)",
42562         "cf",
42563         "236"
42564       ],
42565       [
42566         "Chad (Tchad)",
42567         "td",
42568         "235"
42569       ],
42570       [
42571         "Chile",
42572         "cl",
42573         "56"
42574       ],
42575       [
42576         "China (中国)",
42577         "cn",
42578         "86"
42579       ],
42580       [
42581         "Christmas Island",
42582         "cx",
42583         "61",
42584         2
42585       ],
42586       [
42587         "Cocos (Keeling) Islands",
42588         "cc",
42589         "61",
42590         1
42591       ],
42592       [
42593         "Colombia",
42594         "co",
42595         "57"
42596       ],
42597       [
42598         "Comoros (‫جزر القمر‬‎)",
42599         "km",
42600         "269"
42601       ],
42602       [
42603         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42604         "cd",
42605         "243"
42606       ],
42607       [
42608         "Congo (Republic) (Congo-Brazzaville)",
42609         "cg",
42610         "242"
42611       ],
42612       [
42613         "Cook Islands",
42614         "ck",
42615         "682"
42616       ],
42617       [
42618         "Costa Rica",
42619         "cr",
42620         "506"
42621       ],
42622       [
42623         "Côte d’Ivoire",
42624         "ci",
42625         "225"
42626       ],
42627       [
42628         "Croatia (Hrvatska)",
42629         "hr",
42630         "385"
42631       ],
42632       [
42633         "Cuba",
42634         "cu",
42635         "53"
42636       ],
42637       [
42638         "Curaçao",
42639         "cw",
42640         "599",
42641         0
42642       ],
42643       [
42644         "Cyprus (Κύπρος)",
42645         "cy",
42646         "357"
42647       ],
42648       [
42649         "Czech Republic (Česká republika)",
42650         "cz",
42651         "420"
42652       ],
42653       [
42654         "Denmark (Danmark)",
42655         "dk",
42656         "45"
42657       ],
42658       [
42659         "Djibouti",
42660         "dj",
42661         "253"
42662       ],
42663       [
42664         "Dominica",
42665         "dm",
42666         "1767"
42667       ],
42668       [
42669         "Dominican Republic (República Dominicana)",
42670         "do",
42671         "1",
42672         2,
42673         ["809", "829", "849"]
42674       ],
42675       [
42676         "Ecuador",
42677         "ec",
42678         "593"
42679       ],
42680       [
42681         "Egypt (‫مصر‬‎)",
42682         "eg",
42683         "20"
42684       ],
42685       [
42686         "El Salvador",
42687         "sv",
42688         "503"
42689       ],
42690       [
42691         "Equatorial Guinea (Guinea Ecuatorial)",
42692         "gq",
42693         "240"
42694       ],
42695       [
42696         "Eritrea",
42697         "er",
42698         "291"
42699       ],
42700       [
42701         "Estonia (Eesti)",
42702         "ee",
42703         "372"
42704       ],
42705       [
42706         "Ethiopia",
42707         "et",
42708         "251"
42709       ],
42710       [
42711         "Falkland Islands (Islas Malvinas)",
42712         "fk",
42713         "500"
42714       ],
42715       [
42716         "Faroe Islands (Føroyar)",
42717         "fo",
42718         "298"
42719       ],
42720       [
42721         "Fiji",
42722         "fj",
42723         "679"
42724       ],
42725       [
42726         "Finland (Suomi)",
42727         "fi",
42728         "358",
42729         0
42730       ],
42731       [
42732         "France",
42733         "fr",
42734         "33"
42735       ],
42736       [
42737         "French Guiana (Guyane française)",
42738         "gf",
42739         "594"
42740       ],
42741       [
42742         "French Polynesia (Polynésie française)",
42743         "pf",
42744         "689"
42745       ],
42746       [
42747         "Gabon",
42748         "ga",
42749         "241"
42750       ],
42751       [
42752         "Gambia",
42753         "gm",
42754         "220"
42755       ],
42756       [
42757         "Georgia (საქართველო)",
42758         "ge",
42759         "995"
42760       ],
42761       [
42762         "Germany (Deutschland)",
42763         "de",
42764         "49"
42765       ],
42766       [
42767         "Ghana (Gaana)",
42768         "gh",
42769         "233"
42770       ],
42771       [
42772         "Gibraltar",
42773         "gi",
42774         "350"
42775       ],
42776       [
42777         "Greece (Ελλάδα)",
42778         "gr",
42779         "30"
42780       ],
42781       [
42782         "Greenland (Kalaallit Nunaat)",
42783         "gl",
42784         "299"
42785       ],
42786       [
42787         "Grenada",
42788         "gd",
42789         "1473"
42790       ],
42791       [
42792         "Guadeloupe",
42793         "gp",
42794         "590",
42795         0
42796       ],
42797       [
42798         "Guam",
42799         "gu",
42800         "1671"
42801       ],
42802       [
42803         "Guatemala",
42804         "gt",
42805         "502"
42806       ],
42807       [
42808         "Guernsey",
42809         "gg",
42810         "44",
42811         1
42812       ],
42813       [
42814         "Guinea (Guinée)",
42815         "gn",
42816         "224"
42817       ],
42818       [
42819         "Guinea-Bissau (Guiné Bissau)",
42820         "gw",
42821         "245"
42822       ],
42823       [
42824         "Guyana",
42825         "gy",
42826         "592"
42827       ],
42828       [
42829         "Haiti",
42830         "ht",
42831         "509"
42832       ],
42833       [
42834         "Honduras",
42835         "hn",
42836         "504"
42837       ],
42838       [
42839         "Hong Kong (香港)",
42840         "hk",
42841         "852"
42842       ],
42843       [
42844         "Hungary (Magyarország)",
42845         "hu",
42846         "36"
42847       ],
42848       [
42849         "Iceland (Ísland)",
42850         "is",
42851         "354"
42852       ],
42853       [
42854         "India (भारत)",
42855         "in",
42856         "91"
42857       ],
42858       [
42859         "Indonesia",
42860         "id",
42861         "62"
42862       ],
42863       [
42864         "Iran (‫ایران‬‎)",
42865         "ir",
42866         "98"
42867       ],
42868       [
42869         "Iraq (‫العراق‬‎)",
42870         "iq",
42871         "964"
42872       ],
42873       [
42874         "Ireland",
42875         "ie",
42876         "353"
42877       ],
42878       [
42879         "Isle of Man",
42880         "im",
42881         "44",
42882         2
42883       ],
42884       [
42885         "Israel (‫ישראל‬‎)",
42886         "il",
42887         "972"
42888       ],
42889       [
42890         "Italy (Italia)",
42891         "it",
42892         "39",
42893         0
42894       ],
42895       [
42896         "Jamaica",
42897         "jm",
42898         "1876"
42899       ],
42900       [
42901         "Japan (日本)",
42902         "jp",
42903         "81"
42904       ],
42905       [
42906         "Jersey",
42907         "je",
42908         "44",
42909         3
42910       ],
42911       [
42912         "Jordan (‫الأردن‬‎)",
42913         "jo",
42914         "962"
42915       ],
42916       [
42917         "Kazakhstan (Казахстан)",
42918         "kz",
42919         "7",
42920         1
42921       ],
42922       [
42923         "Kenya",
42924         "ke",
42925         "254"
42926       ],
42927       [
42928         "Kiribati",
42929         "ki",
42930         "686"
42931       ],
42932       [
42933         "Kosovo",
42934         "xk",
42935         "383"
42936       ],
42937       [
42938         "Kuwait (‫الكويت‬‎)",
42939         "kw",
42940         "965"
42941       ],
42942       [
42943         "Kyrgyzstan (Кыргызстан)",
42944         "kg",
42945         "996"
42946       ],
42947       [
42948         "Laos (ລາວ)",
42949         "la",
42950         "856"
42951       ],
42952       [
42953         "Latvia (Latvija)",
42954         "lv",
42955         "371"
42956       ],
42957       [
42958         "Lebanon (‫لبنان‬‎)",
42959         "lb",
42960         "961"
42961       ],
42962       [
42963         "Lesotho",
42964         "ls",
42965         "266"
42966       ],
42967       [
42968         "Liberia",
42969         "lr",
42970         "231"
42971       ],
42972       [
42973         "Libya (‫ليبيا‬‎)",
42974         "ly",
42975         "218"
42976       ],
42977       [
42978         "Liechtenstein",
42979         "li",
42980         "423"
42981       ],
42982       [
42983         "Lithuania (Lietuva)",
42984         "lt",
42985         "370"
42986       ],
42987       [
42988         "Luxembourg",
42989         "lu",
42990         "352"
42991       ],
42992       [
42993         "Macau (澳門)",
42994         "mo",
42995         "853"
42996       ],
42997       [
42998         "Macedonia (FYROM) (Македонија)",
42999         "mk",
43000         "389"
43001       ],
43002       [
43003         "Madagascar (Madagasikara)",
43004         "mg",
43005         "261"
43006       ],
43007       [
43008         "Malawi",
43009         "mw",
43010         "265"
43011       ],
43012       [
43013         "Malaysia",
43014         "my",
43015         "60"
43016       ],
43017       [
43018         "Maldives",
43019         "mv",
43020         "960"
43021       ],
43022       [
43023         "Mali",
43024         "ml",
43025         "223"
43026       ],
43027       [
43028         "Malta",
43029         "mt",
43030         "356"
43031       ],
43032       [
43033         "Marshall Islands",
43034         "mh",
43035         "692"
43036       ],
43037       [
43038         "Martinique",
43039         "mq",
43040         "596"
43041       ],
43042       [
43043         "Mauritania (‫موريتانيا‬‎)",
43044         "mr",
43045         "222"
43046       ],
43047       [
43048         "Mauritius (Moris)",
43049         "mu",
43050         "230"
43051       ],
43052       [
43053         "Mayotte",
43054         "yt",
43055         "262",
43056         1
43057       ],
43058       [
43059         "Mexico (México)",
43060         "mx",
43061         "52"
43062       ],
43063       [
43064         "Micronesia",
43065         "fm",
43066         "691"
43067       ],
43068       [
43069         "Moldova (Republica Moldova)",
43070         "md",
43071         "373"
43072       ],
43073       [
43074         "Monaco",
43075         "mc",
43076         "377"
43077       ],
43078       [
43079         "Mongolia (Монгол)",
43080         "mn",
43081         "976"
43082       ],
43083       [
43084         "Montenegro (Crna Gora)",
43085         "me",
43086         "382"
43087       ],
43088       [
43089         "Montserrat",
43090         "ms",
43091         "1664"
43092       ],
43093       [
43094         "Morocco (‫المغرب‬‎)",
43095         "ma",
43096         "212",
43097         0
43098       ],
43099       [
43100         "Mozambique (Moçambique)",
43101         "mz",
43102         "258"
43103       ],
43104       [
43105         "Myanmar (Burma) (မြန်မာ)",
43106         "mm",
43107         "95"
43108       ],
43109       [
43110         "Namibia (Namibië)",
43111         "na",
43112         "264"
43113       ],
43114       [
43115         "Nauru",
43116         "nr",
43117         "674"
43118       ],
43119       [
43120         "Nepal (नेपाल)",
43121         "np",
43122         "977"
43123       ],
43124       [
43125         "Netherlands (Nederland)",
43126         "nl",
43127         "31"
43128       ],
43129       [
43130         "New Caledonia (Nouvelle-Calédonie)",
43131         "nc",
43132         "687"
43133       ],
43134       [
43135         "New Zealand",
43136         "nz",
43137         "64"
43138       ],
43139       [
43140         "Nicaragua",
43141         "ni",
43142         "505"
43143       ],
43144       [
43145         "Niger (Nijar)",
43146         "ne",
43147         "227"
43148       ],
43149       [
43150         "Nigeria",
43151         "ng",
43152         "234"
43153       ],
43154       [
43155         "Niue",
43156         "nu",
43157         "683"
43158       ],
43159       [
43160         "Norfolk Island",
43161         "nf",
43162         "672"
43163       ],
43164       [
43165         "North Korea (조선 민주주의 인민 공화국)",
43166         "kp",
43167         "850"
43168       ],
43169       [
43170         "Northern Mariana Islands",
43171         "mp",
43172         "1670"
43173       ],
43174       [
43175         "Norway (Norge)",
43176         "no",
43177         "47",
43178         0
43179       ],
43180       [
43181         "Oman (‫عُمان‬‎)",
43182         "om",
43183         "968"
43184       ],
43185       [
43186         "Pakistan (‫پاکستان‬‎)",
43187         "pk",
43188         "92"
43189       ],
43190       [
43191         "Palau",
43192         "pw",
43193         "680"
43194       ],
43195       [
43196         "Palestine (‫فلسطين‬‎)",
43197         "ps",
43198         "970"
43199       ],
43200       [
43201         "Panama (Panamá)",
43202         "pa",
43203         "507"
43204       ],
43205       [
43206         "Papua New Guinea",
43207         "pg",
43208         "675"
43209       ],
43210       [
43211         "Paraguay",
43212         "py",
43213         "595"
43214       ],
43215       [
43216         "Peru (Perú)",
43217         "pe",
43218         "51"
43219       ],
43220       [
43221         "Philippines",
43222         "ph",
43223         "63"
43224       ],
43225       [
43226         "Poland (Polska)",
43227         "pl",
43228         "48"
43229       ],
43230       [
43231         "Portugal",
43232         "pt",
43233         "351"
43234       ],
43235       [
43236         "Puerto Rico",
43237         "pr",
43238         "1",
43239         3,
43240         ["787", "939"]
43241       ],
43242       [
43243         "Qatar (‫قطر‬‎)",
43244         "qa",
43245         "974"
43246       ],
43247       [
43248         "Réunion (La Réunion)",
43249         "re",
43250         "262",
43251         0
43252       ],
43253       [
43254         "Romania (România)",
43255         "ro",
43256         "40"
43257       ],
43258       [
43259         "Russia (Россия)",
43260         "ru",
43261         "7",
43262         0
43263       ],
43264       [
43265         "Rwanda",
43266         "rw",
43267         "250"
43268       ],
43269       [
43270         "Saint Barthélemy",
43271         "bl",
43272         "590",
43273         1
43274       ],
43275       [
43276         "Saint Helena",
43277         "sh",
43278         "290"
43279       ],
43280       [
43281         "Saint Kitts and Nevis",
43282         "kn",
43283         "1869"
43284       ],
43285       [
43286         "Saint Lucia",
43287         "lc",
43288         "1758"
43289       ],
43290       [
43291         "Saint Martin (Saint-Martin (partie française))",
43292         "mf",
43293         "590",
43294         2
43295       ],
43296       [
43297         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43298         "pm",
43299         "508"
43300       ],
43301       [
43302         "Saint Vincent and the Grenadines",
43303         "vc",
43304         "1784"
43305       ],
43306       [
43307         "Samoa",
43308         "ws",
43309         "685"
43310       ],
43311       [
43312         "San Marino",
43313         "sm",
43314         "378"
43315       ],
43316       [
43317         "São Tomé and Príncipe (São Tomé e Príncipe)",
43318         "st",
43319         "239"
43320       ],
43321       [
43322         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43323         "sa",
43324         "966"
43325       ],
43326       [
43327         "Senegal (Sénégal)",
43328         "sn",
43329         "221"
43330       ],
43331       [
43332         "Serbia (Србија)",
43333         "rs",
43334         "381"
43335       ],
43336       [
43337         "Seychelles",
43338         "sc",
43339         "248"
43340       ],
43341       [
43342         "Sierra Leone",
43343         "sl",
43344         "232"
43345       ],
43346       [
43347         "Singapore",
43348         "sg",
43349         "65"
43350       ],
43351       [
43352         "Sint Maarten",
43353         "sx",
43354         "1721"
43355       ],
43356       [
43357         "Slovakia (Slovensko)",
43358         "sk",
43359         "421"
43360       ],
43361       [
43362         "Slovenia (Slovenija)",
43363         "si",
43364         "386"
43365       ],
43366       [
43367         "Solomon Islands",
43368         "sb",
43369         "677"
43370       ],
43371       [
43372         "Somalia (Soomaaliya)",
43373         "so",
43374         "252"
43375       ],
43376       [
43377         "South Africa",
43378         "za",
43379         "27"
43380       ],
43381       [
43382         "South Korea (대한민국)",
43383         "kr",
43384         "82"
43385       ],
43386       [
43387         "South Sudan (‫جنوب السودان‬‎)",
43388         "ss",
43389         "211"
43390       ],
43391       [
43392         "Spain (España)",
43393         "es",
43394         "34"
43395       ],
43396       [
43397         "Sri Lanka (ශ්‍රී ලංකාව)",
43398         "lk",
43399         "94"
43400       ],
43401       [
43402         "Sudan (‫السودان‬‎)",
43403         "sd",
43404         "249"
43405       ],
43406       [
43407         "Suriname",
43408         "sr",
43409         "597"
43410       ],
43411       [
43412         "Svalbard and Jan Mayen",
43413         "sj",
43414         "47",
43415         1
43416       ],
43417       [
43418         "Swaziland",
43419         "sz",
43420         "268"
43421       ],
43422       [
43423         "Sweden (Sverige)",
43424         "se",
43425         "46"
43426       ],
43427       [
43428         "Switzerland (Schweiz)",
43429         "ch",
43430         "41"
43431       ],
43432       [
43433         "Syria (‫سوريا‬‎)",
43434         "sy",
43435         "963"
43436       ],
43437       [
43438         "Taiwan (台灣)",
43439         "tw",
43440         "886"
43441       ],
43442       [
43443         "Tajikistan",
43444         "tj",
43445         "992"
43446       ],
43447       [
43448         "Tanzania",
43449         "tz",
43450         "255"
43451       ],
43452       [
43453         "Thailand (ไทย)",
43454         "th",
43455         "66"
43456       ],
43457       [
43458         "Timor-Leste",
43459         "tl",
43460         "670"
43461       ],
43462       [
43463         "Togo",
43464         "tg",
43465         "228"
43466       ],
43467       [
43468         "Tokelau",
43469         "tk",
43470         "690"
43471       ],
43472       [
43473         "Tonga",
43474         "to",
43475         "676"
43476       ],
43477       [
43478         "Trinidad and Tobago",
43479         "tt",
43480         "1868"
43481       ],
43482       [
43483         "Tunisia (‫تونس‬‎)",
43484         "tn",
43485         "216"
43486       ],
43487       [
43488         "Turkey (Türkiye)",
43489         "tr",
43490         "90"
43491       ],
43492       [
43493         "Turkmenistan",
43494         "tm",
43495         "993"
43496       ],
43497       [
43498         "Turks and Caicos Islands",
43499         "tc",
43500         "1649"
43501       ],
43502       [
43503         "Tuvalu",
43504         "tv",
43505         "688"
43506       ],
43507       [
43508         "U.S. Virgin Islands",
43509         "vi",
43510         "1340"
43511       ],
43512       [
43513         "Uganda",
43514         "ug",
43515         "256"
43516       ],
43517       [
43518         "Ukraine (Україна)",
43519         "ua",
43520         "380"
43521       ],
43522       [
43523         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43524         "ae",
43525         "971"
43526       ],
43527       [
43528         "United Kingdom",
43529         "gb",
43530         "44",
43531         0
43532       ],
43533       [
43534         "United States",
43535         "us",
43536         "1",
43537         0
43538       ],
43539       [
43540         "Uruguay",
43541         "uy",
43542         "598"
43543       ],
43544       [
43545         "Uzbekistan (Oʻzbekiston)",
43546         "uz",
43547         "998"
43548       ],
43549       [
43550         "Vanuatu",
43551         "vu",
43552         "678"
43553       ],
43554       [
43555         "Vatican City (Città del Vaticano)",
43556         "va",
43557         "39",
43558         1
43559       ],
43560       [
43561         "Venezuela",
43562         "ve",
43563         "58"
43564       ],
43565       [
43566         "Vietnam (Việt Nam)",
43567         "vn",
43568         "84"
43569       ],
43570       [
43571         "Wallis and Futuna (Wallis-et-Futuna)",
43572         "wf",
43573         "681"
43574       ],
43575       [
43576         "Western Sahara (‫الصحراء الغربية‬‎)",
43577         "eh",
43578         "212",
43579         1
43580       ],
43581       [
43582         "Yemen (‫اليمن‬‎)",
43583         "ye",
43584         "967"
43585       ],
43586       [
43587         "Zambia",
43588         "zm",
43589         "260"
43590       ],
43591       [
43592         "Zimbabwe",
43593         "zw",
43594         "263"
43595       ],
43596       [
43597         "Åland Islands",
43598         "ax",
43599         "358",
43600         1
43601       ]
43602   ];
43603   
43604   return d;
43605 }/**
43606 *    This script refer to:
43607 *    Title: International Telephone Input
43608 *    Author: Jack O'Connor
43609 *    Code version:  v12.1.12
43610 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43611 **/
43612
43613 /**
43614  * @class Roo.bootstrap.PhoneInput
43615  * @extends Roo.bootstrap.TriggerField
43616  * An input with International dial-code selection
43617  
43618  * @cfg {String} defaultDialCode default '+852'
43619  * @cfg {Array} preferedCountries default []
43620   
43621  * @constructor
43622  * Create a new PhoneInput.
43623  * @param {Object} config Configuration options
43624  */
43625
43626 Roo.bootstrap.PhoneInput = function(config) {
43627     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43628 };
43629
43630 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43631         
43632         listWidth: undefined,
43633         
43634         selectedClass: 'active',
43635         
43636         invalidClass : "has-warning",
43637         
43638         validClass: 'has-success',
43639         
43640         allowed: '0123456789',
43641         
43642         max_length: 15,
43643         
43644         /**
43645          * @cfg {String} defaultDialCode The default dial code when initializing the input
43646          */
43647         defaultDialCode: '+852',
43648         
43649         /**
43650          * @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
43651          */
43652         preferedCountries: false,
43653         
43654         getAutoCreate : function()
43655         {
43656             var data = Roo.bootstrap.PhoneInputData();
43657             var align = this.labelAlign || this.parentLabelAlign();
43658             var id = Roo.id();
43659             
43660             this.allCountries = [];
43661             this.dialCodeMapping = [];
43662             
43663             for (var i = 0; i < data.length; i++) {
43664               var c = data[i];
43665               this.allCountries[i] = {
43666                 name: c[0],
43667                 iso2: c[1],
43668                 dialCode: c[2],
43669                 priority: c[3] || 0,
43670                 areaCodes: c[4] || null
43671               };
43672               this.dialCodeMapping[c[2]] = {
43673                   name: c[0],
43674                   iso2: c[1],
43675                   priority: c[3] || 0,
43676                   areaCodes: c[4] || null
43677               };
43678             }
43679             
43680             var cfg = {
43681                 cls: 'form-group',
43682                 cn: []
43683             };
43684             
43685             var input =  {
43686                 tag: 'input',
43687                 id : id,
43688                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43689                 maxlength: this.max_length,
43690                 cls : 'form-control tel-input',
43691                 autocomplete: 'new-password'
43692             };
43693             
43694             var hiddenInput = {
43695                 tag: 'input',
43696                 type: 'hidden',
43697                 cls: 'hidden-tel-input'
43698             };
43699             
43700             if (this.name) {
43701                 hiddenInput.name = this.name;
43702             }
43703             
43704             if (this.disabled) {
43705                 input.disabled = true;
43706             }
43707             
43708             var flag_container = {
43709                 tag: 'div',
43710                 cls: 'flag-box',
43711                 cn: [
43712                     {
43713                         tag: 'div',
43714                         cls: 'flag'
43715                     },
43716                     {
43717                         tag: 'div',
43718                         cls: 'caret'
43719                     }
43720                 ]
43721             };
43722             
43723             var box = {
43724                 tag: 'div',
43725                 cls: this.hasFeedback ? 'has-feedback' : '',
43726                 cn: [
43727                     hiddenInput,
43728                     input,
43729                     {
43730                         tag: 'input',
43731                         cls: 'dial-code-holder',
43732                         disabled: true
43733                     }
43734                 ]
43735             };
43736             
43737             var container = {
43738                 cls: 'roo-select2-container input-group',
43739                 cn: [
43740                     flag_container,
43741                     box
43742                 ]
43743             };
43744             
43745             if (this.fieldLabel.length) {
43746                 var indicator = {
43747                     tag: 'i',
43748                     tooltip: 'This field is required'
43749                 };
43750                 
43751                 var label = {
43752                     tag: 'label',
43753                     'for':  id,
43754                     cls: 'control-label',
43755                     cn: []
43756                 };
43757                 
43758                 var label_text = {
43759                     tag: 'span',
43760                     html: this.fieldLabel
43761                 };
43762                 
43763                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43764                 label.cn = [
43765                     indicator,
43766                     label_text
43767                 ];
43768                 
43769                 if(this.indicatorpos == 'right') {
43770                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43771                     label.cn = [
43772                         label_text,
43773                         indicator
43774                     ];
43775                 }
43776                 
43777                 if(align == 'left') {
43778                     container = {
43779                         tag: 'div',
43780                         cn: [
43781                             container
43782                         ]
43783                     };
43784                     
43785                     if(this.labelWidth > 12){
43786                         label.style = "width: " + this.labelWidth + 'px';
43787                     }
43788                     if(this.labelWidth < 13 && this.labelmd == 0){
43789                         this.labelmd = this.labelWidth;
43790                     }
43791                     if(this.labellg > 0){
43792                         label.cls += ' col-lg-' + this.labellg;
43793                         input.cls += ' col-lg-' + (12 - this.labellg);
43794                     }
43795                     if(this.labelmd > 0){
43796                         label.cls += ' col-md-' + this.labelmd;
43797                         container.cls += ' col-md-' + (12 - this.labelmd);
43798                     }
43799                     if(this.labelsm > 0){
43800                         label.cls += ' col-sm-' + this.labelsm;
43801                         container.cls += ' col-sm-' + (12 - this.labelsm);
43802                     }
43803                     if(this.labelxs > 0){
43804                         label.cls += ' col-xs-' + this.labelxs;
43805                         container.cls += ' col-xs-' + (12 - this.labelxs);
43806                     }
43807                 }
43808             }
43809             
43810             cfg.cn = [
43811                 label,
43812                 container
43813             ];
43814             
43815             var settings = this;
43816             
43817             ['xs','sm','md','lg'].map(function(size){
43818                 if (settings[size]) {
43819                     cfg.cls += ' col-' + size + '-' + settings[size];
43820                 }
43821             });
43822             
43823             this.store = new Roo.data.Store({
43824                 proxy : new Roo.data.MemoryProxy({}),
43825                 reader : new Roo.data.JsonReader({
43826                     fields : [
43827                         {
43828                             'name' : 'name',
43829                             'type' : 'string'
43830                         },
43831                         {
43832                             'name' : 'iso2',
43833                             'type' : 'string'
43834                         },
43835                         {
43836                             'name' : 'dialCode',
43837                             'type' : 'string'
43838                         },
43839                         {
43840                             'name' : 'priority',
43841                             'type' : 'string'
43842                         },
43843                         {
43844                             'name' : 'areaCodes',
43845                             'type' : 'string'
43846                         }
43847                     ]
43848                 })
43849             });
43850             
43851             if(!this.preferedCountries) {
43852                 this.preferedCountries = [
43853                     'hk',
43854                     'gb',
43855                     'us'
43856                 ];
43857             }
43858             
43859             var p = this.preferedCountries.reverse();
43860             
43861             if(p) {
43862                 for (var i = 0; i < p.length; i++) {
43863                     for (var j = 0; j < this.allCountries.length; j++) {
43864                         if(this.allCountries[j].iso2 == p[i]) {
43865                             var t = this.allCountries[j];
43866                             this.allCountries.splice(j,1);
43867                             this.allCountries.unshift(t);
43868                         }
43869                     } 
43870                 }
43871             }
43872             
43873             this.store.proxy.data = {
43874                 success: true,
43875                 data: this.allCountries
43876             };
43877             
43878             return cfg;
43879         },
43880         
43881         initEvents : function()
43882         {
43883             this.createList();
43884             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43885             
43886             this.indicator = this.indicatorEl();
43887             this.flag = this.flagEl();
43888             this.dialCodeHolder = this.dialCodeHolderEl();
43889             
43890             this.trigger = this.el.select('div.flag-box',true).first();
43891             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43892             
43893             var _this = this;
43894             
43895             (function(){
43896                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43897                 _this.list.setWidth(lw);
43898             }).defer(100);
43899             
43900             this.list.on('mouseover', this.onViewOver, this);
43901             this.list.on('mousemove', this.onViewMove, this);
43902             this.inputEl().on("keyup", this.onKeyUp, this);
43903             this.inputEl().on("keypress", this.onKeyPress, this);
43904             
43905             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43906
43907             this.view = new Roo.View(this.list, this.tpl, {
43908                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43909             });
43910             
43911             this.view.on('click', this.onViewClick, this);
43912             this.setValue(this.defaultDialCode);
43913         },
43914         
43915         onTriggerClick : function(e)
43916         {
43917             Roo.log('trigger click');
43918             if(this.disabled){
43919                 return;
43920             }
43921             
43922             if(this.isExpanded()){
43923                 this.collapse();
43924                 this.hasFocus = false;
43925             }else {
43926                 this.store.load({});
43927                 this.hasFocus = true;
43928                 this.expand();
43929             }
43930         },
43931         
43932         isExpanded : function()
43933         {
43934             return this.list.isVisible();
43935         },
43936         
43937         collapse : function()
43938         {
43939             if(!this.isExpanded()){
43940                 return;
43941             }
43942             this.list.hide();
43943             Roo.get(document).un('mousedown', this.collapseIf, this);
43944             Roo.get(document).un('mousewheel', this.collapseIf, this);
43945             this.fireEvent('collapse', this);
43946             this.validate();
43947         },
43948         
43949         expand : function()
43950         {
43951             Roo.log('expand');
43952
43953             if(this.isExpanded() || !this.hasFocus){
43954                 return;
43955             }
43956             
43957             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43958             this.list.setWidth(lw);
43959             
43960             this.list.show();
43961             this.restrictHeight();
43962             
43963             Roo.get(document).on('mousedown', this.collapseIf, this);
43964             Roo.get(document).on('mousewheel', this.collapseIf, this);
43965             
43966             this.fireEvent('expand', this);
43967         },
43968         
43969         restrictHeight : function()
43970         {
43971             this.list.alignTo(this.inputEl(), this.listAlign);
43972             this.list.alignTo(this.inputEl(), this.listAlign);
43973         },
43974         
43975         onViewOver : function(e, t)
43976         {
43977             if(this.inKeyMode){
43978                 return;
43979             }
43980             var item = this.view.findItemFromChild(t);
43981             
43982             if(item){
43983                 var index = this.view.indexOf(item);
43984                 this.select(index, false);
43985             }
43986         },
43987
43988         // private
43989         onViewClick : function(view, doFocus, el, e)
43990         {
43991             var index = this.view.getSelectedIndexes()[0];
43992             
43993             var r = this.store.getAt(index);
43994             
43995             if(r){
43996                 this.onSelect(r, index);
43997             }
43998             if(doFocus !== false && !this.blockFocus){
43999                 this.inputEl().focus();
44000             }
44001         },
44002         
44003         onViewMove : function(e, t)
44004         {
44005             this.inKeyMode = false;
44006         },
44007         
44008         select : function(index, scrollIntoView)
44009         {
44010             this.selectedIndex = index;
44011             this.view.select(index);
44012             if(scrollIntoView !== false){
44013                 var el = this.view.getNode(index);
44014                 if(el){
44015                     this.list.scrollChildIntoView(el, false);
44016                 }
44017             }
44018         },
44019         
44020         createList : function()
44021         {
44022             this.list = Roo.get(document.body).createChild({
44023                 tag: 'ul',
44024                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44025                 style: 'display:none'
44026             });
44027             
44028             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44029         },
44030         
44031         collapseIf : function(e)
44032         {
44033             var in_combo  = e.within(this.el);
44034             var in_list =  e.within(this.list);
44035             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44036             
44037             if (in_combo || in_list || is_list) {
44038                 return;
44039             }
44040             this.collapse();
44041         },
44042         
44043         onSelect : function(record, index)
44044         {
44045             if(this.fireEvent('beforeselect', this, record, index) !== false){
44046                 
44047                 this.setFlagClass(record.data.iso2);
44048                 this.setDialCode(record.data.dialCode);
44049                 this.hasFocus = false;
44050                 this.collapse();
44051                 this.fireEvent('select', this, record, index);
44052             }
44053         },
44054         
44055         flagEl : function()
44056         {
44057             var flag = this.el.select('div.flag',true).first();
44058             if(!flag){
44059                 return false;
44060             }
44061             return flag;
44062         },
44063         
44064         dialCodeHolderEl : function()
44065         {
44066             var d = this.el.select('input.dial-code-holder',true).first();
44067             if(!d){
44068                 return false;
44069             }
44070             return d;
44071         },
44072         
44073         setDialCode : function(v)
44074         {
44075             this.dialCodeHolder.dom.value = '+'+v;
44076         },
44077         
44078         setFlagClass : function(n)
44079         {
44080             this.flag.dom.className = 'flag '+n;
44081         },
44082         
44083         getValue : function()
44084         {
44085             var v = this.inputEl().getValue();
44086             if(this.dialCodeHolder) {
44087                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44088             }
44089             return v;
44090         },
44091         
44092         setValue : function(v)
44093         {
44094             var d = this.getDialCode(v);
44095             
44096             //invalid dial code
44097             if(v.length == 0 || !d || d.length == 0) {
44098                 if(this.rendered){
44099                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44100                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44101                 }
44102                 return;
44103             }
44104             
44105             //valid dial code
44106             this.setFlagClass(this.dialCodeMapping[d].iso2);
44107             this.setDialCode(d);
44108             this.inputEl().dom.value = v.replace('+'+d,'');
44109             this.hiddenEl().dom.value = this.getValue();
44110             
44111             this.validate();
44112         },
44113         
44114         getDialCode : function(v)
44115         {
44116             v = v ||  '';
44117             
44118             if (v.length == 0) {
44119                 return this.dialCodeHolder.dom.value;
44120             }
44121             
44122             var dialCode = "";
44123             if (v.charAt(0) != "+") {
44124                 return false;
44125             }
44126             var numericChars = "";
44127             for (var i = 1; i < v.length; i++) {
44128               var c = v.charAt(i);
44129               if (!isNaN(c)) {
44130                 numericChars += c;
44131                 if (this.dialCodeMapping[numericChars]) {
44132                   dialCode = v.substr(1, i);
44133                 }
44134                 if (numericChars.length == 4) {
44135                   break;
44136                 }
44137               }
44138             }
44139             return dialCode;
44140         },
44141         
44142         reset : function()
44143         {
44144             this.setValue(this.defaultDialCode);
44145             this.validate();
44146         },
44147         
44148         hiddenEl : function()
44149         {
44150             return this.el.select('input.hidden-tel-input',true).first();
44151         },
44152         
44153         // after setting val
44154         onKeyUp : function(e){
44155             this.setValue(this.getValue());
44156         },
44157         
44158         onKeyPress : function(e){
44159             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44160                 e.stopEvent();
44161             }
44162         }
44163         
44164 });
44165 /**
44166  * @class Roo.bootstrap.MoneyField
44167  * @extends Roo.bootstrap.ComboBox
44168  * Bootstrap MoneyField class
44169  * 
44170  * @constructor
44171  * Create a new MoneyField.
44172  * @param {Object} config Configuration options
44173  */
44174
44175 Roo.bootstrap.MoneyField = function(config) {
44176     
44177     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44178     
44179 };
44180
44181 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44182     
44183     /**
44184      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44185      */
44186     allowDecimals : true,
44187     /**
44188      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44189      */
44190     decimalSeparator : ".",
44191     /**
44192      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44193      */
44194     decimalPrecision : 0,
44195     /**
44196      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44197      */
44198     allowNegative : true,
44199     /**
44200      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44201      */
44202     allowZero: true,
44203     /**
44204      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44205      */
44206     minValue : Number.NEGATIVE_INFINITY,
44207     /**
44208      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44209      */
44210     maxValue : Number.MAX_VALUE,
44211     /**
44212      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44213      */
44214     minText : "The minimum value for this field is {0}",
44215     /**
44216      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44217      */
44218     maxText : "The maximum value for this field is {0}",
44219     /**
44220      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44221      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44222      */
44223     nanText : "{0} is not a valid number",
44224     /**
44225      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44226      */
44227     castInt : true,
44228     /**
44229      * @cfg {String} defaults currency of the MoneyField
44230      * value should be in lkey
44231      */
44232     defaultCurrency : false,
44233     /**
44234      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44235      */
44236     thousandsDelimiter : false,
44237     /**
44238      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44239      */
44240     max_length: false,
44241     
44242     inputlg : 9,
44243     inputmd : 9,
44244     inputsm : 9,
44245     inputxs : 6,
44246     
44247     store : false,
44248     
44249     getAutoCreate : function()
44250     {
44251         var align = this.labelAlign || this.parentLabelAlign();
44252         
44253         var id = Roo.id();
44254
44255         var cfg = {
44256             cls: 'form-group',
44257             cn: []
44258         };
44259
44260         var input =  {
44261             tag: 'input',
44262             id : id,
44263             cls : 'form-control roo-money-amount-input',
44264             autocomplete: 'new-password'
44265         };
44266         
44267         var hiddenInput = {
44268             tag: 'input',
44269             type: 'hidden',
44270             id: Roo.id(),
44271             cls: 'hidden-number-input'
44272         };
44273         
44274         if(this.max_length) {
44275             input.maxlength = this.max_length; 
44276         }
44277         
44278         if (this.name) {
44279             hiddenInput.name = this.name;
44280         }
44281
44282         if (this.disabled) {
44283             input.disabled = true;
44284         }
44285
44286         var clg = 12 - this.inputlg;
44287         var cmd = 12 - this.inputmd;
44288         var csm = 12 - this.inputsm;
44289         var cxs = 12 - this.inputxs;
44290         
44291         var container = {
44292             tag : 'div',
44293             cls : 'row roo-money-field',
44294             cn : [
44295                 {
44296                     tag : 'div',
44297                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44298                     cn : [
44299                         {
44300                             tag : 'div',
44301                             cls: 'roo-select2-container input-group',
44302                             cn: [
44303                                 {
44304                                     tag : 'input',
44305                                     cls : 'form-control roo-money-currency-input',
44306                                     autocomplete: 'new-password',
44307                                     readOnly : 1,
44308                                     name : this.currencyName
44309                                 },
44310                                 {
44311                                     tag :'span',
44312                                     cls : 'input-group-addon',
44313                                     cn : [
44314                                         {
44315                                             tag: 'span',
44316                                             cls: 'caret'
44317                                         }
44318                                     ]
44319                                 }
44320                             ]
44321                         }
44322                     ]
44323                 },
44324                 {
44325                     tag : 'div',
44326                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44327                     cn : [
44328                         {
44329                             tag: 'div',
44330                             cls: this.hasFeedback ? 'has-feedback' : '',
44331                             cn: [
44332                                 input
44333                             ]
44334                         }
44335                     ]
44336                 }
44337             ]
44338             
44339         };
44340         
44341         if (this.fieldLabel.length) {
44342             var indicator = {
44343                 tag: 'i',
44344                 tooltip: 'This field is required'
44345             };
44346
44347             var label = {
44348                 tag: 'label',
44349                 'for':  id,
44350                 cls: 'control-label',
44351                 cn: []
44352             };
44353
44354             var label_text = {
44355                 tag: 'span',
44356                 html: this.fieldLabel
44357             };
44358
44359             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44360             label.cn = [
44361                 indicator,
44362                 label_text
44363             ];
44364
44365             if(this.indicatorpos == 'right') {
44366                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44367                 label.cn = [
44368                     label_text,
44369                     indicator
44370                 ];
44371             }
44372
44373             if(align == 'left') {
44374                 container = {
44375                     tag: 'div',
44376                     cn: [
44377                         container
44378                     ]
44379                 };
44380
44381                 if(this.labelWidth > 12){
44382                     label.style = "width: " + this.labelWidth + 'px';
44383                 }
44384                 if(this.labelWidth < 13 && this.labelmd == 0){
44385                     this.labelmd = this.labelWidth;
44386                 }
44387                 if(this.labellg > 0){
44388                     label.cls += ' col-lg-' + this.labellg;
44389                     input.cls += ' col-lg-' + (12 - this.labellg);
44390                 }
44391                 if(this.labelmd > 0){
44392                     label.cls += ' col-md-' + this.labelmd;
44393                     container.cls += ' col-md-' + (12 - this.labelmd);
44394                 }
44395                 if(this.labelsm > 0){
44396                     label.cls += ' col-sm-' + this.labelsm;
44397                     container.cls += ' col-sm-' + (12 - this.labelsm);
44398                 }
44399                 if(this.labelxs > 0){
44400                     label.cls += ' col-xs-' + this.labelxs;
44401                     container.cls += ' col-xs-' + (12 - this.labelxs);
44402                 }
44403             }
44404         }
44405
44406         cfg.cn = [
44407             label,
44408             container,
44409             hiddenInput
44410         ];
44411         
44412         var settings = this;
44413
44414         ['xs','sm','md','lg'].map(function(size){
44415             if (settings[size]) {
44416                 cfg.cls += ' col-' + size + '-' + settings[size];
44417             }
44418         });
44419         
44420         return cfg;
44421     },
44422     
44423     initEvents : function()
44424     {
44425         this.indicator = this.indicatorEl();
44426         
44427         this.initCurrencyEvent();
44428         
44429         this.initNumberEvent();
44430     },
44431     
44432     initCurrencyEvent : function()
44433     {
44434         if (!this.store) {
44435             throw "can not find store for combo";
44436         }
44437         
44438         this.store = Roo.factory(this.store, Roo.data);
44439         this.store.parent = this;
44440         
44441         this.createList();
44442         
44443         this.triggerEl = this.el.select('.input-group-addon', true).first();
44444         
44445         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44446         
44447         var _this = this;
44448         
44449         (function(){
44450             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44451             _this.list.setWidth(lw);
44452         }).defer(100);
44453         
44454         this.list.on('mouseover', this.onViewOver, this);
44455         this.list.on('mousemove', this.onViewMove, this);
44456         this.list.on('scroll', this.onViewScroll, this);
44457         
44458         if(!this.tpl){
44459             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44460         }
44461         
44462         this.view = new Roo.View(this.list, this.tpl, {
44463             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44464         });
44465         
44466         this.view.on('click', this.onViewClick, this);
44467         
44468         this.store.on('beforeload', this.onBeforeLoad, this);
44469         this.store.on('load', this.onLoad, this);
44470         this.store.on('loadexception', this.onLoadException, this);
44471         
44472         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44473             "up" : function(e){
44474                 this.inKeyMode = true;
44475                 this.selectPrev();
44476             },
44477
44478             "down" : function(e){
44479                 if(!this.isExpanded()){
44480                     this.onTriggerClick();
44481                 }else{
44482                     this.inKeyMode = true;
44483                     this.selectNext();
44484                 }
44485             },
44486
44487             "enter" : function(e){
44488                 this.collapse();
44489                 
44490                 if(this.fireEvent("specialkey", this, e)){
44491                     this.onViewClick(false);
44492                 }
44493                 
44494                 return true;
44495             },
44496
44497             "esc" : function(e){
44498                 this.collapse();
44499             },
44500
44501             "tab" : function(e){
44502                 this.collapse();
44503                 
44504                 if(this.fireEvent("specialkey", this, e)){
44505                     this.onViewClick(false);
44506                 }
44507                 
44508                 return true;
44509             },
44510
44511             scope : this,
44512
44513             doRelay : function(foo, bar, hname){
44514                 if(hname == 'down' || this.scope.isExpanded()){
44515                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44516                 }
44517                 return true;
44518             },
44519
44520             forceKeyDown: true
44521         });
44522         
44523         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44524         
44525     },
44526     
44527     initNumberEvent : function(e)
44528     {
44529         this.inputEl().on("keydown" , this.fireKey,  this);
44530         this.inputEl().on("focus", this.onFocus,  this);
44531         this.inputEl().on("blur", this.onBlur,  this);
44532         
44533         this.inputEl().relayEvent('keyup', this);
44534         
44535         if(this.indicator){
44536             this.indicator.addClass('invisible');
44537         }
44538  
44539         this.originalValue = this.getValue();
44540         
44541         if(this.validationEvent == 'keyup'){
44542             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44543             this.inputEl().on('keyup', this.filterValidation, this);
44544         }
44545         else if(this.validationEvent !== false){
44546             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44547         }
44548         
44549         if(this.selectOnFocus){
44550             this.on("focus", this.preFocus, this);
44551             
44552         }
44553         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44554             this.inputEl().on("keypress", this.filterKeys, this);
44555         } else {
44556             this.inputEl().relayEvent('keypress', this);
44557         }
44558         
44559         var allowed = "0123456789";
44560         
44561         if(this.allowDecimals){
44562             allowed += this.decimalSeparator;
44563         }
44564         
44565         if(this.allowNegative){
44566             allowed += "-";
44567         }
44568         
44569         if(this.thousandsDelimiter) {
44570             allowed += ",";
44571         }
44572         
44573         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44574         
44575         var keyPress = function(e){
44576             
44577             var k = e.getKey();
44578             
44579             var c = e.getCharCode();
44580             
44581             if(
44582                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44583                     allowed.indexOf(String.fromCharCode(c)) === -1
44584             ){
44585                 e.stopEvent();
44586                 return;
44587             }
44588             
44589             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44590                 return;
44591             }
44592             
44593             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44594                 e.stopEvent();
44595             }
44596         };
44597         
44598         this.inputEl().on("keypress", keyPress, this);
44599         
44600     },
44601     
44602     onTriggerClick : function(e)
44603     {   
44604         if(this.disabled){
44605             return;
44606         }
44607         
44608         this.page = 0;
44609         this.loadNext = false;
44610         
44611         if(this.isExpanded()){
44612             this.collapse();
44613             return;
44614         }
44615         
44616         this.hasFocus = true;
44617         
44618         if(this.triggerAction == 'all') {
44619             this.doQuery(this.allQuery, true);
44620             return;
44621         }
44622         
44623         this.doQuery(this.getRawValue());
44624     },
44625     
44626     getCurrency : function()
44627     {   
44628         var v = this.currencyEl().getValue();
44629         
44630         return v;
44631     },
44632     
44633     restrictHeight : function()
44634     {
44635         this.list.alignTo(this.currencyEl(), this.listAlign);
44636         this.list.alignTo(this.currencyEl(), this.listAlign);
44637     },
44638     
44639     onViewClick : function(view, doFocus, el, e)
44640     {
44641         var index = this.view.getSelectedIndexes()[0];
44642         
44643         var r = this.store.getAt(index);
44644         
44645         if(r){
44646             this.onSelect(r, index);
44647         }
44648     },
44649     
44650     onSelect : function(record, index){
44651         
44652         if(this.fireEvent('beforeselect', this, record, index) !== false){
44653         
44654             this.setFromCurrencyData(index > -1 ? record.data : false);
44655             
44656             this.collapse();
44657             
44658             this.fireEvent('select', this, record, index);
44659         }
44660     },
44661     
44662     setFromCurrencyData : function(o)
44663     {
44664         var currency = '';
44665         
44666         this.lastCurrency = o;
44667         
44668         if (this.currencyField) {
44669             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44670         } else {
44671             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44672         }
44673         
44674         this.lastSelectionText = currency;
44675         
44676         //setting default currency
44677         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44678             this.setCurrency(this.defaultCurrency);
44679             return;
44680         }
44681         
44682         this.setCurrency(currency);
44683     },
44684     
44685     setFromData : function(o)
44686     {
44687         var c = {};
44688         
44689         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44690         
44691         this.setFromCurrencyData(c);
44692         
44693         var value = '';
44694         
44695         if (this.name) {
44696             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44697         } else {
44698             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44699         }
44700         
44701         this.setValue(value);
44702         
44703     },
44704     
44705     setCurrency : function(v)
44706     {   
44707         this.currencyValue = v;
44708         
44709         if(this.rendered){
44710             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44711             this.validate();
44712         }
44713     },
44714     
44715     setValue : function(v)
44716     {
44717         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44718         
44719         this.value = v;
44720         
44721         if(this.rendered){
44722             
44723             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44724             
44725             this.inputEl().dom.value = (v == '') ? '' :
44726                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44727             
44728             if(!this.allowZero && v === '0') {
44729                 this.hiddenEl().dom.value = '';
44730                 this.inputEl().dom.value = '';
44731             }
44732             
44733             this.validate();
44734         }
44735     },
44736     
44737     getRawValue : function()
44738     {
44739         var v = this.inputEl().getValue();
44740         
44741         return v;
44742     },
44743     
44744     getValue : function()
44745     {
44746         return this.fixPrecision(this.parseValue(this.getRawValue()));
44747     },
44748     
44749     parseValue : function(value)
44750     {
44751         if(this.thousandsDelimiter) {
44752             value += "";
44753             r = new RegExp(",", "g");
44754             value = value.replace(r, "");
44755         }
44756         
44757         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44758         return isNaN(value) ? '' : value;
44759         
44760     },
44761     
44762     fixPrecision : function(value)
44763     {
44764         if(this.thousandsDelimiter) {
44765             value += "";
44766             r = new RegExp(",", "g");
44767             value = value.replace(r, "");
44768         }
44769         
44770         var nan = isNaN(value);
44771         
44772         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44773             return nan ? '' : value;
44774         }
44775         return parseFloat(value).toFixed(this.decimalPrecision);
44776     },
44777     
44778     decimalPrecisionFcn : function(v)
44779     {
44780         return Math.floor(v);
44781     },
44782     
44783     validateValue : function(value)
44784     {
44785         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44786             return false;
44787         }
44788         
44789         var num = this.parseValue(value);
44790         
44791         if(isNaN(num)){
44792             this.markInvalid(String.format(this.nanText, value));
44793             return false;
44794         }
44795         
44796         if(num < this.minValue){
44797             this.markInvalid(String.format(this.minText, this.minValue));
44798             return false;
44799         }
44800         
44801         if(num > this.maxValue){
44802             this.markInvalid(String.format(this.maxText, this.maxValue));
44803             return false;
44804         }
44805         
44806         return true;
44807     },
44808     
44809     validate : function()
44810     {
44811         if(this.disabled || this.allowBlank){
44812             this.markValid();
44813             return true;
44814         }
44815         
44816         var currency = this.getCurrency();
44817         
44818         if(this.validateValue(this.getRawValue()) && currency.length){
44819             this.markValid();
44820             return true;
44821         }
44822         
44823         this.markInvalid();
44824         return false;
44825     },
44826     
44827     getName: function()
44828     {
44829         return this.name;
44830     },
44831     
44832     beforeBlur : function()
44833     {
44834         if(!this.castInt){
44835             return;
44836         }
44837         
44838         var v = this.parseValue(this.getRawValue());
44839         
44840         if(v || v == 0){
44841             this.setValue(v);
44842         }
44843     },
44844     
44845     onBlur : function()
44846     {
44847         this.beforeBlur();
44848         
44849         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44850             //this.el.removeClass(this.focusClass);
44851         }
44852         
44853         this.hasFocus = false;
44854         
44855         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44856             this.validate();
44857         }
44858         
44859         var v = this.getValue();
44860         
44861         if(String(v) !== String(this.startValue)){
44862             this.fireEvent('change', this, v, this.startValue);
44863         }
44864         
44865         this.fireEvent("blur", this);
44866     },
44867     
44868     inputEl : function()
44869     {
44870         return this.el.select('.roo-money-amount-input', true).first();
44871     },
44872     
44873     currencyEl : function()
44874     {
44875         return this.el.select('.roo-money-currency-input', true).first();
44876     },
44877     
44878     hiddenEl : function()
44879     {
44880         return this.el.select('input.hidden-number-input',true).first();
44881     }
44882     
44883 });/**
44884  * @class Roo.bootstrap.BezierSignature
44885  * @extends Roo.bootstrap.Component
44886  * Bootstrap BezierSignature class
44887  * This script refer to:
44888  *    Title: Signature Pad
44889  *    Author: szimek
44890  *    Availability: https://github.com/szimek/signature_pad
44891  *
44892  * @constructor
44893  * Create a new BezierSignature
44894  * @param {Object} config The config object
44895  */
44896
44897 Roo.bootstrap.BezierSignature = function(config){
44898     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44899     this.addEvents({
44900         "resize" : true
44901     });
44902 };
44903
44904 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44905 {
44906      
44907     curve_data: [],
44908     
44909     is_empty: true,
44910     
44911     mouse_btn_down: true,
44912     
44913     /**
44914      * @cfg {int} canvas height
44915      */
44916     canvas_height: '200px',
44917     
44918     /**
44919      * @cfg {float|function} Radius of a single dot.
44920      */ 
44921     dot_size: false,
44922     
44923     /**
44924      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44925      */
44926     min_width: 0.5,
44927     
44928     /**
44929      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44930      */
44931     max_width: 2.5,
44932     
44933     /**
44934      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44935      */
44936     throttle: 16,
44937     
44938     /**
44939      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44940      */
44941     min_distance: 5,
44942     
44943     /**
44944      * @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.
44945      */
44946     bg_color: 'rgba(0, 0, 0, 0)',
44947     
44948     /**
44949      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44950      */
44951     dot_color: 'black',
44952     
44953     /**
44954      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44955      */ 
44956     velocity_filter_weight: 0.7,
44957     
44958     /**
44959      * @cfg {function} Callback when stroke begin. 
44960      */
44961     onBegin: false,
44962     
44963     /**
44964      * @cfg {function} Callback when stroke end.
44965      */
44966     onEnd: false,
44967     
44968     getAutoCreate : function()
44969     {
44970         var cls = 'roo-signature column';
44971         
44972         if(this.cls){
44973             cls += ' ' + this.cls;
44974         }
44975         
44976         var col_sizes = [
44977             'lg',
44978             'md',
44979             'sm',
44980             'xs'
44981         ];
44982         
44983         for(var i = 0; i < col_sizes.length; i++) {
44984             if(this[col_sizes[i]]) {
44985                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44986             }
44987         }
44988         
44989         var cfg = {
44990             tag: 'div',
44991             cls: cls,
44992             cn: [
44993                 {
44994                     tag: 'div',
44995                     cls: 'roo-signature-body',
44996                     cn: [
44997                         {
44998                             tag: 'canvas',
44999                             cls: 'roo-signature-body-canvas',
45000                             height: this.canvas_height,
45001                             width: this.canvas_width
45002                         }
45003                     ]
45004                 },
45005                 {
45006                     tag: 'input',
45007                     type: 'file',
45008                     style: 'display: none'
45009                 }
45010             ]
45011         };
45012         
45013         return cfg;
45014     },
45015     
45016     initEvents: function() 
45017     {
45018         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45019         
45020         var canvas = this.canvasEl();
45021         
45022         // mouse && touch event swapping...
45023         canvas.dom.style.touchAction = 'none';
45024         canvas.dom.style.msTouchAction = 'none';
45025         
45026         this.mouse_btn_down = false;
45027         canvas.on('mousedown', this._handleMouseDown, this);
45028         canvas.on('mousemove', this._handleMouseMove, this);
45029         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45030         
45031         if (window.PointerEvent) {
45032             canvas.on('pointerdown', this._handleMouseDown, this);
45033             canvas.on('pointermove', this._handleMouseMove, this);
45034             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45035         }
45036         
45037         if ('ontouchstart' in window) {
45038             canvas.on('touchstart', this._handleTouchStart, this);
45039             canvas.on('touchmove', this._handleTouchMove, this);
45040             canvas.on('touchend', this._handleTouchEnd, this);
45041         }
45042         
45043         Roo.EventManager.onWindowResize(this.resize, this, true);
45044         
45045         // file input event
45046         this.fileEl().on('change', this.uploadImage, this);
45047         
45048         this.clear();
45049         
45050         this.resize();
45051     },
45052     
45053     resize: function(){
45054         
45055         var canvas = this.canvasEl().dom;
45056         var ctx = this.canvasElCtx();
45057         var img_data = false;
45058         
45059         if(canvas.width > 0) {
45060             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45061         }
45062         // setting canvas width will clean img data
45063         canvas.width = 0;
45064         
45065         var style = window.getComputedStyle ? 
45066             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45067             
45068         var padding_left = parseInt(style.paddingLeft) || 0;
45069         var padding_right = parseInt(style.paddingRight) || 0;
45070         
45071         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45072         
45073         if(img_data) {
45074             ctx.putImageData(img_data, 0, 0);
45075         }
45076     },
45077     
45078     _handleMouseDown: function(e)
45079     {
45080         if (e.browserEvent.which === 1) {
45081             this.mouse_btn_down = true;
45082             this.strokeBegin(e);
45083         }
45084     },
45085     
45086     _handleMouseMove: function (e)
45087     {
45088         if (this.mouse_btn_down) {
45089             this.strokeMoveUpdate(e);
45090         }
45091     },
45092     
45093     _handleMouseUp: function (e)
45094     {
45095         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45096             this.mouse_btn_down = false;
45097             this.strokeEnd(e);
45098         }
45099     },
45100     
45101     _handleTouchStart: function (e) {
45102         
45103         e.preventDefault();
45104         if (e.browserEvent.targetTouches.length === 1) {
45105             // var touch = e.browserEvent.changedTouches[0];
45106             // this.strokeBegin(touch);
45107             
45108              this.strokeBegin(e); // assume e catching the correct xy...
45109         }
45110     },
45111     
45112     _handleTouchMove: function (e) {
45113         e.preventDefault();
45114         // var touch = event.targetTouches[0];
45115         // _this._strokeMoveUpdate(touch);
45116         this.strokeMoveUpdate(e);
45117     },
45118     
45119     _handleTouchEnd: function (e) {
45120         var wasCanvasTouched = e.target === this.canvasEl().dom;
45121         if (wasCanvasTouched) {
45122             e.preventDefault();
45123             // var touch = event.changedTouches[0];
45124             // _this._strokeEnd(touch);
45125             this.strokeEnd(e);
45126         }
45127     },
45128     
45129     reset: function () {
45130         this._lastPoints = [];
45131         this._lastVelocity = 0;
45132         this._lastWidth = (this.min_width + this.max_width) / 2;
45133         this.canvasElCtx().fillStyle = this.dot_color;
45134     },
45135     
45136     strokeMoveUpdate: function(e)
45137     {
45138         this.strokeUpdate(e);
45139         
45140         if (this.throttle) {
45141             this.throttleStroke(this.strokeUpdate, this.throttle);
45142         }
45143         else {
45144             this.strokeUpdate(e);
45145         }
45146     },
45147     
45148     strokeBegin: function(e)
45149     {
45150         var newPointGroup = {
45151             color: this.dot_color,
45152             points: []
45153         };
45154         
45155         if (typeof this.onBegin === 'function') {
45156             this.onBegin(e);
45157         }
45158         
45159         this.curve_data.push(newPointGroup);
45160         this.reset();
45161         this.strokeUpdate(e);
45162     },
45163     
45164     strokeUpdate: function(e)
45165     {
45166         var rect = this.canvasEl().dom.getBoundingClientRect();
45167         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45168         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45169         var lastPoints = lastPointGroup.points;
45170         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45171         var isLastPointTooClose = lastPoint
45172             ? point.distanceTo(lastPoint) <= this.min_distance
45173             : false;
45174         var color = lastPointGroup.color;
45175         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45176             var curve = this.addPoint(point);
45177             if (!lastPoint) {
45178                 this.drawDot({color: color, point: point});
45179             }
45180             else if (curve) {
45181                 this.drawCurve({color: color, curve: curve});
45182             }
45183             lastPoints.push({
45184                 time: point.time,
45185                 x: point.x,
45186                 y: point.y
45187             });
45188         }
45189     },
45190     
45191     strokeEnd: function(e)
45192     {
45193         this.strokeUpdate(e);
45194         if (typeof this.onEnd === 'function') {
45195             this.onEnd(e);
45196         }
45197     },
45198     
45199     addPoint:  function (point) {
45200         var _lastPoints = this._lastPoints;
45201         _lastPoints.push(point);
45202         if (_lastPoints.length > 2) {
45203             if (_lastPoints.length === 3) {
45204                 _lastPoints.unshift(_lastPoints[0]);
45205             }
45206             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45207             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45208             _lastPoints.shift();
45209             return curve;
45210         }
45211         return null;
45212     },
45213     
45214     calculateCurveWidths: function (startPoint, endPoint) {
45215         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45216             (1 - this.velocity_filter_weight) * this._lastVelocity;
45217
45218         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45219         var widths = {
45220             end: newWidth,
45221             start: this._lastWidth
45222         };
45223         
45224         this._lastVelocity = velocity;
45225         this._lastWidth = newWidth;
45226         return widths;
45227     },
45228     
45229     drawDot: function (_a) {
45230         var color = _a.color, point = _a.point;
45231         var ctx = this.canvasElCtx();
45232         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45233         ctx.beginPath();
45234         this.drawCurveSegment(point.x, point.y, width);
45235         ctx.closePath();
45236         ctx.fillStyle = color;
45237         ctx.fill();
45238     },
45239     
45240     drawCurve: function (_a) {
45241         var color = _a.color, curve = _a.curve;
45242         var ctx = this.canvasElCtx();
45243         var widthDelta = curve.endWidth - curve.startWidth;
45244         var drawSteps = Math.floor(curve.length()) * 2;
45245         ctx.beginPath();
45246         ctx.fillStyle = color;
45247         for (var i = 0; i < drawSteps; i += 1) {
45248         var t = i / drawSteps;
45249         var tt = t * t;
45250         var ttt = tt * t;
45251         var u = 1 - t;
45252         var uu = u * u;
45253         var uuu = uu * u;
45254         var x = uuu * curve.startPoint.x;
45255         x += 3 * uu * t * curve.control1.x;
45256         x += 3 * u * tt * curve.control2.x;
45257         x += ttt * curve.endPoint.x;
45258         var y = uuu * curve.startPoint.y;
45259         y += 3 * uu * t * curve.control1.y;
45260         y += 3 * u * tt * curve.control2.y;
45261         y += ttt * curve.endPoint.y;
45262         var width = curve.startWidth + ttt * widthDelta;
45263         this.drawCurveSegment(x, y, width);
45264         }
45265         ctx.closePath();
45266         ctx.fill();
45267     },
45268     
45269     drawCurveSegment: function (x, y, width) {
45270         var ctx = this.canvasElCtx();
45271         ctx.moveTo(x, y);
45272         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45273         this.is_empty = false;
45274     },
45275     
45276     clear: function()
45277     {
45278         var ctx = this.canvasElCtx();
45279         var canvas = this.canvasEl().dom;
45280         ctx.fillStyle = this.bg_color;
45281         ctx.clearRect(0, 0, canvas.width, canvas.height);
45282         ctx.fillRect(0, 0, canvas.width, canvas.height);
45283         this.curve_data = [];
45284         this.reset();
45285         this.is_empty = true;
45286     },
45287     
45288     fileEl: function()
45289     {
45290         return  this.el.select('input',true).first();
45291     },
45292     
45293     canvasEl: function()
45294     {
45295         return this.el.select('canvas',true).first();
45296     },
45297     
45298     canvasElCtx: function()
45299     {
45300         return this.el.select('canvas',true).first().dom.getContext('2d');
45301     },
45302     
45303     getImage: function(type)
45304     {
45305         if(this.is_empty) {
45306             return false;
45307         }
45308         
45309         // encryption ?
45310         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45311     },
45312     
45313     drawFromImage: function(img_src)
45314     {
45315         var img = new Image();
45316         
45317         img.onload = function(){
45318             this.canvasElCtx().drawImage(img, 0, 0);
45319         }.bind(this);
45320         
45321         img.src = img_src;
45322         
45323         this.is_empty = false;
45324     },
45325     
45326     selectImage: function()
45327     {
45328         this.fileEl().dom.click();
45329     },
45330     
45331     uploadImage: function(e)
45332     {
45333         var reader = new FileReader();
45334         
45335         reader.onload = function(e){
45336             var img = new Image();
45337             img.onload = function(){
45338                 this.reset();
45339                 this.canvasElCtx().drawImage(img, 0, 0);
45340             }.bind(this);
45341             img.src = e.target.result;
45342         }.bind(this);
45343         
45344         reader.readAsDataURL(e.target.files[0]);
45345     },
45346     
45347     // Bezier Point Constructor
45348     Point: (function () {
45349         function Point(x, y, time) {
45350             this.x = x;
45351             this.y = y;
45352             this.time = time || Date.now();
45353         }
45354         Point.prototype.distanceTo = function (start) {
45355             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45356         };
45357         Point.prototype.equals = function (other) {
45358             return this.x === other.x && this.y === other.y && this.time === other.time;
45359         };
45360         Point.prototype.velocityFrom = function (start) {
45361             return this.time !== start.time
45362             ? this.distanceTo(start) / (this.time - start.time)
45363             : 0;
45364         };
45365         return Point;
45366     }()),
45367     
45368     
45369     // Bezier Constructor
45370     Bezier: (function () {
45371         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45372             this.startPoint = startPoint;
45373             this.control2 = control2;
45374             this.control1 = control1;
45375             this.endPoint = endPoint;
45376             this.startWidth = startWidth;
45377             this.endWidth = endWidth;
45378         }
45379         Bezier.fromPoints = function (points, widths, scope) {
45380             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45381             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45382             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45383         };
45384         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45385             var dx1 = s1.x - s2.x;
45386             var dy1 = s1.y - s2.y;
45387             var dx2 = s2.x - s3.x;
45388             var dy2 = s2.y - s3.y;
45389             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45390             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45391             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45392             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45393             var dxm = m1.x - m2.x;
45394             var dym = m1.y - m2.y;
45395             var k = l2 / (l1 + l2);
45396             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45397             var tx = s2.x - cm.x;
45398             var ty = s2.y - cm.y;
45399             return {
45400                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45401                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45402             };
45403         };
45404         Bezier.prototype.length = function () {
45405             var steps = 10;
45406             var length = 0;
45407             var px;
45408             var py;
45409             for (var i = 0; i <= steps; i += 1) {
45410                 var t = i / steps;
45411                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45412                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45413                 if (i > 0) {
45414                     var xdiff = cx - px;
45415                     var ydiff = cy - py;
45416                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45417                 }
45418                 px = cx;
45419                 py = cy;
45420             }
45421             return length;
45422         };
45423         Bezier.prototype.point = function (t, start, c1, c2, end) {
45424             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45425             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45426             + (3.0 * c2 * (1.0 - t) * t * t)
45427             + (end * t * t * t);
45428         };
45429         return Bezier;
45430     }()),
45431     
45432     throttleStroke: function(fn, wait) {
45433       if (wait === void 0) { wait = 250; }
45434       var previous = 0;
45435       var timeout = null;
45436       var result;
45437       var storedContext;
45438       var storedArgs;
45439       var later = function () {
45440           previous = Date.now();
45441           timeout = null;
45442           result = fn.apply(storedContext, storedArgs);
45443           if (!timeout) {
45444               storedContext = null;
45445               storedArgs = [];
45446           }
45447       };
45448       return function wrapper() {
45449           var args = [];
45450           for (var _i = 0; _i < arguments.length; _i++) {
45451               args[_i] = arguments[_i];
45452           }
45453           var now = Date.now();
45454           var remaining = wait - (now - previous);
45455           storedContext = this;
45456           storedArgs = args;
45457           if (remaining <= 0 || remaining > wait) {
45458               if (timeout) {
45459                   clearTimeout(timeout);
45460                   timeout = null;
45461               }
45462               previous = now;
45463               result = fn.apply(storedContext, storedArgs);
45464               if (!timeout) {
45465                   storedContext = null;
45466                   storedArgs = [];
45467               }
45468           }
45469           else if (!timeout) {
45470               timeout = window.setTimeout(later, remaining);
45471           }
45472           return result;
45473       };
45474   }
45475   
45476 });
45477
45478  
45479
45480