roojs-ui.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * Bootstrap Component base class
227  * @cfg {String} cls css class
228  * @cfg {String} style any extra css
229  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
230  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
231  * @cfg {string} dataId cutomer id
232  * @cfg {string} name Specifies name attribute
233  * @cfg {string} tooltip  Text for the tooltip
234  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
235  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
236  
237  * @constructor
238  * Do not use directly - it does not do anything..
239  * @param {Object} config The config object
240  */
241
242
243
244 Roo.bootstrap.Component = function(config){
245     Roo.bootstrap.Component.superclass.constructor.call(this, config);
246        
247     this.addEvents({
248         /**
249          * @event childrenrendered
250          * Fires when the children have been rendered..
251          * @param {Roo.bootstrap.Component} this
252          */
253         "childrenrendered" : true
254         
255         
256         
257     });
258     
259     
260 };
261
262 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
263     
264     
265     allowDomMove : false, // to stop relocations in parent onRender...
266     
267     cls : false,
268     
269     style : false,
270     
271     autoCreate : false,
272     
273     tooltip : null,
274     /**
275      * Initialize Events for the element
276      */
277     initEvents : function() { },
278     
279     xattr : false,
280     
281     parentId : false,
282     
283     can_build_overlaid : true,
284     
285     container_method : false,
286     
287     dataId : false,
288     
289     name : false,
290     
291     parent: function() {
292         // returns the parent component..
293         return Roo.ComponentMgr.get(this.parentId)
294         
295         
296     },
297     
298     // private
299     onRender : function(ct, position)
300     {
301        // Roo.log("Call onRender: " + this.xtype);
302         
303         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
304         
305         if(this.el){
306             if (this.el.attr('xtype')) {
307                 this.el.attr('xtypex', this.el.attr('xtype'));
308                 this.el.dom.removeAttribute('xtype');
309                 
310                 this.initEvents();
311             }
312             
313             return;
314         }
315         
316          
317         
318         var cfg = Roo.apply({},  this.getAutoCreate());
319         
320         cfg.id = this.id || Roo.id();
321         
322         // fill in the extra attributes 
323         if (this.xattr && typeof(this.xattr) =='object') {
324             for (var i in this.xattr) {
325                 cfg[i] = this.xattr[i];
326             }
327         }
328         
329         if(this.dataId){
330             cfg.dataId = this.dataId;
331         }
332         
333         if (this.cls) {
334             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
335         }
336         
337         if (this.style) { // fixme needs to support more complex style data.
338             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
339         }
340         
341         if(this.name){
342             cfg.name = this.name;
343         }
344         
345         this.el = ct.createChild(cfg, position);
346         
347         if (this.tooltip) {
348             this.tooltipEl().attr('tooltip', this.tooltip);
349         }
350         
351         if(this.tabIndex !== undefined){
352             this.el.dom.setAttribute('tabIndex', this.tabIndex);
353         }
354         
355         this.initEvents();
356         
357     },
358     /**
359      * Fetch the element to add children to
360      * @return {Roo.Element} defaults to this.el
361      */
362     getChildContainer : function()
363     {
364         return this.el;
365     },
366     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367     {
368         return Roo.get(document.body);
369     },
370     
371     /**
372      * Fetch the element to display the tooltip on.
373      * @return {Roo.Element} defaults to this.el
374      */
375     tooltipEl : function()
376     {
377         return this.el;
378     },
379         
380     addxtype  : function(tree,cntr)
381     {
382         var cn = this;
383         
384         cn = Roo.factory(tree);
385         //Roo.log(['addxtype', cn]);
386            
387         cn.parentType = this.xtype; //??
388         cn.parentId = this.id;
389         
390         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
391         if (typeof(cn.container_method) == 'string') {
392             cntr = cn.container_method;
393         }
394         
395         
396         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
397         
398         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
399         
400         var build_from_html =  Roo.XComponent.build_from_html;
401           
402         var is_body  = (tree.xtype == 'Body') ;
403           
404         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405           
406         var self_cntr_el = Roo.get(this[cntr](false));
407         
408         // do not try and build conditional elements 
409         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
410             return false;
411         }
412         
413         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
414             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
415                 return this.addxtypeChild(tree,cntr, is_body);
416             }
417             
418             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
419                 
420             if(echild){
421                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
422             }
423             
424             Roo.log('skipping render');
425             return cn;
426             
427         }
428         
429         var ret = false;
430         if (!build_from_html) {
431             return false;
432         }
433         
434         // this i think handles overlaying multiple children of the same type
435         // with the sam eelement.. - which might be buggy..
436         while (true) {
437             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
438             
439             if (!echild) {
440                 break;
441             }
442             
443             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
444                 break;
445             }
446             
447             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448         }
449        
450         return ret;
451     },
452     
453     
454     addxtypeChild : function (tree, cntr, is_body)
455     {
456         Roo.debug && Roo.log('addxtypeChild:' + cntr);
457         var cn = this;
458         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
459         
460         
461         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
462                     (typeof(tree['flexy:foreach']) != 'undefined');
463           
464     
465         
466         skip_children = false;
467         // render the element if it's not BODY.
468         if (!is_body) {
469             
470             // if parent was disabled, then do not try and create the children..
471             if(!this[cntr](true)){
472                 tree.items = [];
473                 return tree;
474             }
475            
476             cn = Roo.factory(tree);
477            
478             cn.parentType = this.xtype; //??
479             cn.parentId = this.id;
480             
481             var build_from_html =  Roo.XComponent.build_from_html;
482             
483             
484             // does the container contain child eleemnts with 'xtype' attributes.
485             // that match this xtype..
486             // note - when we render we create these as well..
487             // so we should check to see if body has xtype set.
488             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489                
490                 var self_cntr_el = Roo.get(this[cntr](false));
491                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492                 if (echild) { 
493                     //Roo.log(Roo.XComponent.build_from_html);
494                     //Roo.log("got echild:");
495                     //Roo.log(echild);
496                 }
497                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
498                 // and are not displayed -this causes this to use up the wrong element when matching.
499                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
500                 
501                 
502                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
503                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
504                   
505                   
506                   
507                     cn.el = echild;
508                   //  Roo.log("GOT");
509                     //echild.dom.removeAttribute('xtype');
510                 } else {
511                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
512                     Roo.debug && Roo.log(self_cntr_el);
513                     Roo.debug && Roo.log(echild);
514                     Roo.debug && Roo.log(cn);
515                 }
516             }
517            
518             
519            
520             // if object has flexy:if - then it may or may not be rendered.
521             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
522                 // skip a flexy if element.
523                 Roo.debug && Roo.log('skipping render');
524                 Roo.debug && Roo.log(tree);
525                 if (!cn.el) {
526                     Roo.debug && Roo.log('skipping all children');
527                     skip_children = true;
528                 }
529                 
530              } else {
531                  
532                 // actually if flexy:foreach is found, we really want to create 
533                 // multiple copies here...
534                 //Roo.log('render');
535                 //Roo.log(this[cntr]());
536                 // some elements do not have render methods.. like the layouts...
537                 /*
538                 if(this[cntr](true) === false){
539                     cn.items = [];
540                     return cn;
541                 }
542                 */
543                 cn.render && cn.render(this[cntr](true));
544                 
545              }
546             // then add the element..
547         }
548          
549         // handle the kids..
550         
551         var nitems = [];
552         /*
553         if (typeof (tree.menu) != 'undefined') {
554             tree.menu.parentType = cn.xtype;
555             tree.menu.triggerEl = cn.el;
556             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
557             
558         }
559         */
560         if (!tree.items || !tree.items.length) {
561             cn.items = nitems;
562             //Roo.log(["no children", this]);
563             
564             return cn;
565         }
566          
567         var items = tree.items;
568         delete tree.items;
569         
570         //Roo.log(items.length);
571             // add the items..
572         if (!skip_children) {    
573             for(var i =0;i < items.length;i++) {
574               //  Roo.log(['add child', items[i]]);
575                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
576             }
577         }
578         
579         cn.items = nitems;
580         
581         //Roo.log("fire childrenrendered");
582         
583         cn.fireEvent('childrenrendered', this);
584         
585         return cn;
586     },
587     
588     /**
589      * Set the element that will be used to show or hide
590      */
591     setVisibilityEl : function(el)
592     {
593         this.visibilityEl = el;
594     },
595     
596      /**
597      * Get the element that will be used to show or hide
598      */
599     getVisibilityEl : function()
600     {
601         if (typeof(this.visibilityEl) == 'object') {
602             return this.visibilityEl;
603         }
604         
605         if (typeof(this.visibilityEl) == 'string') {
606             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607         }
608         
609         return this.getEl();
610     },
611     
612     /**
613      * Show a component - removes 'hidden' class
614      */
615     show : function()
616     {
617         if(!this.getVisibilityEl()){
618             return;
619         }
620          
621         this.getVisibilityEl().removeClass(['hidden','d-none']);
622         
623         this.fireEvent('show', this);
624         
625         
626     },
627     /**
628      * Hide a component - adds 'hidden' class
629      */
630     hide: function()
631     {
632         if(!this.getVisibilityEl()){
633             return;
634         }
635         
636         this.getVisibilityEl().addClass(['hidden','d-none']);
637         
638         this.fireEvent('hide', this);
639         
640     }
641 });
642
643  /*
644  * - LGPL
645  *
646  * element
647  * 
648  */
649
650 /**
651  * @class Roo.bootstrap.Element
652  * @extends Roo.bootstrap.Component
653  * Bootstrap Element class
654  * @cfg {String} html contents of the element
655  * @cfg {String} tag tag of the element
656  * @cfg {String} cls class of the element
657  * @cfg {Boolean} preventDefault (true|false) default false
658  * @cfg {Boolean} clickable (true|false) default false
659  * @cfg {String} role default blank - set to button to force cursor pointer
660  
661  * 
662  * @constructor
663  * Create a new Element
664  * @param {Object} config The config object
665  */
666
667 Roo.bootstrap.Element = function(config){
668     Roo.bootstrap.Element.superclass.constructor.call(this, config);
669     
670     this.addEvents({
671         // raw events
672         /**
673          * @event click
674          * When a element is chick
675          * @param {Roo.bootstrap.Element} this
676          * @param {Roo.EventObject} e
677          */
678         "click" : true 
679         
680       
681     });
682 };
683
684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
685     
686     tag: 'div',
687     cls: '',
688     html: '',
689     preventDefault: false, 
690     clickable: false,
691     tapedTwice : false,
692     role : false,
693     
694     getAutoCreate : function(){
695         
696         var cfg = {
697             tag: this.tag,
698             // cls: this.cls, double assign in parent class Component.js :: onRender
699             html: this.html
700         };
701         if (this.role !== false) {
702             cfg.role = this.role;
703         }
704         
705         return cfg;
706     },
707     
708     initEvents: function() 
709     {
710         Roo.bootstrap.Element.superclass.initEvents.call(this);
711         
712         if(this.clickable){
713             this.el.on('click', this.onClick, this);
714         }
715         
716         
717     },
718     
719     onClick : function(e)
720     {
721         if(this.preventDefault){
722             e.preventDefault();
723         }
724         
725         this.fireEvent('click', this, e); // why was this double click before?
726     },
727     
728     
729     
730
731     
732     
733     getValue : function()
734     {
735         return this.el.dom.innerHTML;
736     },
737     
738     setValue : function(value)
739     {
740         this.el.dom.innerHTML = value;
741     }
742    
743 });
744
745  
746
747  /*
748  * - LGPL
749  *
750  * dropable area
751  * 
752  */
753
754 /**
755  * @class Roo.bootstrap.DropTarget
756  * @extends Roo.bootstrap.Element
757  * Bootstrap DropTarget class
758  
759  * @cfg {string} name dropable name
760  * 
761  * @constructor
762  * Create a new Dropable Area
763  * @param {Object} config The config object
764  */
765
766 Roo.bootstrap.DropTarget = function(config){
767     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
768     
769     this.addEvents({
770         // raw events
771         /**
772          * @event click
773          * When a element is chick
774          * @param {Roo.bootstrap.Element} this
775          * @param {Roo.EventObject} e
776          */
777         "drop" : true
778     });
779 };
780
781 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
782     
783     
784     getAutoCreate : function(){
785         
786          
787     },
788     
789     initEvents: function() 
790     {
791         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
792         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
793             ddGroup: this.name,
794             listeners : {
795                 drop : this.dragDrop.createDelegate(this),
796                 enter : this.dragEnter.createDelegate(this),
797                 out : this.dragOut.createDelegate(this),
798                 over : this.dragOver.createDelegate(this)
799             }
800             
801         });
802         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
803     },
804     
805     dragDrop : function(source,e,data)
806     {
807         // user has to decide how to impliment this.
808         Roo.log('drop');
809         Roo.log(this);
810         //this.fireEvent('drop', this, source, e ,data);
811         return false;
812     },
813     
814     dragEnter : function(n, dd, e, data)
815     {
816         // probably want to resize the element to match the dropped element..
817         Roo.log("enter");
818         this.originalSize = this.el.getSize();
819         this.el.setSize( n.el.getSize());
820         this.dropZone.DDM.refreshCache(this.name);
821         Roo.log([n, dd, e, data]);
822     },
823     
824     dragOut : function(value)
825     {
826         // resize back to normal
827         Roo.log("out");
828         this.el.setSize(this.originalSize);
829         this.dropZone.resetConstraints();
830     },
831     
832     dragOver : function()
833     {
834         // ??? do nothing?
835     }
836    
837 });
838
839  
840
841  /*
842  * - LGPL
843  *
844  * Body
845  *
846  */
847
848 /**
849  * @class Roo.bootstrap.Body
850  * @extends Roo.bootstrap.Component
851  * Bootstrap Body class
852  *
853  * @constructor
854  * Create a new body
855  * @param {Object} config The config object
856  */
857
858 Roo.bootstrap.Body = function(config){
859
860     config = config || {};
861
862     Roo.bootstrap.Body.superclass.constructor.call(this, config);
863     this.el = Roo.get(config.el ? config.el : document.body );
864     if (this.cls && this.cls.length) {
865         Roo.get(document.body).addClass(this.cls);
866     }
867 };
868
869 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
870
871     is_body : true,// just to make sure it's constructed?
872
873         autoCreate : {
874         cls: 'container'
875     },
876     onRender : function(ct, position)
877     {
878        /* Roo.log("Roo.bootstrap.Body - onRender");
879         if (this.cls && this.cls.length) {
880             Roo.get(document.body).addClass(this.cls);
881         }
882         // style??? xttr???
883         */
884     }
885
886
887
888
889 });
890 /*
891  * - LGPL
892  *
893  * button group
894  * 
895  */
896
897
898 /**
899  * @class Roo.bootstrap.ButtonGroup
900  * @extends Roo.bootstrap.Component
901  * Bootstrap ButtonGroup class
902  * @cfg {String} size lg | sm | xs (default empty normal)
903  * @cfg {String} align vertical | justified  (default none)
904  * @cfg {String} direction up | down (default down)
905  * @cfg {Boolean} toolbar false | true
906  * @cfg {Boolean} btn true | false
907  * 
908  * 
909  * @constructor
910  * Create a new Input
911  * @param {Object} config The config object
912  */
913
914 Roo.bootstrap.ButtonGroup = function(config){
915     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
916 };
917
918 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
919     
920     size: '',
921     align: '',
922     direction: '',
923     toolbar: false,
924     btn: true,
925
926     getAutoCreate : function(){
927         var cfg = {
928             cls: 'btn-group',
929             html : null
930         };
931         
932         cfg.html = this.html || cfg.html;
933         
934         if (this.toolbar) {
935             cfg = {
936                 cls: 'btn-toolbar',
937                 html: null
938             };
939             
940             return cfg;
941         }
942         
943         if (['vertical','justified'].indexOf(this.align)!==-1) {
944             cfg.cls = 'btn-group-' + this.align;
945             
946             if (this.align == 'justified') {
947                 console.log(this.items);
948             }
949         }
950         
951         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
952             cfg.cls += ' btn-group-' + this.size;
953         }
954         
955         if (this.direction == 'up') {
956             cfg.cls += ' dropup' ;
957         }
958         
959         return cfg;
960     },
961     /**
962      * Add a button to the group (similar to NavItem API.)
963      */
964     addItem : function(cfg)
965     {
966         var cn = new Roo.bootstrap.Button(cfg);
967         //this.register(cn);
968         cn.parentId = this.id;
969         cn.onRender(this.el, null);
970         return cn;
971     }
972    
973 });
974
975  /*
976  * - LGPL
977  *
978  * button
979  * 
980  */
981
982 /**
983  * @class Roo.bootstrap.Button
984  * @extends Roo.bootstrap.Component
985  * Bootstrap Button class
986  * @cfg {String} html The button content
987  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
988  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
989  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
990  * @cfg {String} size (lg|sm|xs)
991  * @cfg {String} tag (a|input|submit)
992  * @cfg {String} href empty or href
993  * @cfg {Boolean} disabled default false;
994  * @cfg {Boolean} isClose default false;
995  * @cfg {String} glyphicon depricated - use fa
996  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
997  * @cfg {String} badge text for badge
998  * @cfg {String} theme (default|glow)  
999  * @cfg {Boolean} inverse dark themed version
1000  * @cfg {Boolean} toggle is it a slidy toggle button
1001  * @cfg {Boolean} pressed   default null - if the button ahs active state
1002  * @cfg {String} ontext text for on slidy toggle state
1003  * @cfg {String} offtext text for off slidy toggle state
1004  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1005  * @cfg {Boolean} removeClass remove the standard class..
1006  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1007  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1008  * 
1009  * @constructor
1010  * Create a new button
1011  * @param {Object} config The config object
1012  */
1013
1014
1015 Roo.bootstrap.Button = function(config){
1016     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020         /**
1021          * @event click
1022          * When a button is pressed
1023          * @param {Roo.bootstrap.Button} btn
1024          * @param {Roo.EventObject} e
1025          */
1026         "click" : true,
1027         /**
1028          * @event dblclick
1029          * When a button is double clicked
1030          * @param {Roo.bootstrap.Button} btn
1031          * @param {Roo.EventObject} e
1032          */
1033         "dblclick" : true,
1034          /**
1035          * @event toggle
1036          * After the button has been toggles
1037          * @param {Roo.bootstrap.Button} btn
1038          * @param {Roo.EventObject} e
1039          * @param {boolean} pressed (also available as button.pressed)
1040          */
1041         "toggle" : true
1042     });
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1046     html: false,
1047     active: false,
1048     weight: '',
1049     badge_weight: '',
1050     outline : false,
1051     size: '',
1052     tag: 'button',
1053     href: '',
1054     disabled: false,
1055     isClose: false,
1056     glyphicon: '',
1057     fa: '',
1058     badge: '',
1059     theme: 'default',
1060     inverse: false,
1061     
1062     toggle: false,
1063     ontext: 'ON',
1064     offtext: 'OFF',
1065     defaulton: true,
1066     preventDefault: true,
1067     removeClass: false,
1068     name: false,
1069     target: false,
1070     group : false,
1071      
1072     pressed : null,
1073      
1074     
1075     getAutoCreate : function(){
1076         
1077         var cfg = {
1078             tag : 'button',
1079             cls : 'roo-button',
1080             html: ''
1081         };
1082         
1083         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1084             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1085             this.tag = 'button';
1086         } else {
1087             cfg.tag = this.tag;
1088         }
1089         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090         
1091         if (this.toggle == true) {
1092             cfg={
1093                 tag: 'div',
1094                 cls: 'slider-frame roo-button',
1095                 cn: [
1096                     {
1097                         tag: 'span',
1098                         'data-on-text':'ON',
1099                         'data-off-text':'OFF',
1100                         cls: 'slider-button',
1101                         html: this.offtext
1102                     }
1103                 ]
1104             };
1105             // why are we validating the weights?
1106             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1107                 cfg.cls +=  ' ' + this.weight;
1108             }
1109             
1110             return cfg;
1111         }
1112         
1113         if (this.isClose) {
1114             cfg.cls += ' close';
1115             
1116             cfg["aria-hidden"] = true;
1117             
1118             cfg.html = "&times;";
1119             
1120             return cfg;
1121         }
1122              
1123         
1124         if (this.theme==='default') {
1125             cfg.cls = 'btn roo-button';
1126             
1127             //if (this.parentType != 'Navbar') {
1128             this.weight = this.weight.length ?  this.weight : 'default';
1129             //}
1130             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131                 
1132                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1133                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1134                 cfg.cls += ' btn-' + outline + weight;
1135                 if (this.weight == 'default') {
1136                     // BC
1137                     cfg.cls += ' btn-' + this.weight;
1138                 }
1139             }
1140         } else if (this.theme==='glow') {
1141             
1142             cfg.tag = 'a';
1143             cfg.cls = 'btn-glow roo-button';
1144             
1145             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146                 
1147                 cfg.cls += ' ' + this.weight;
1148             }
1149         }
1150    
1151         
1152         if (this.inverse) {
1153             this.cls += ' inverse';
1154         }
1155         
1156         
1157         if (this.active || this.pressed === true) {
1158             cfg.cls += ' active';
1159         }
1160         
1161         if (this.disabled) {
1162             cfg.disabled = 'disabled';
1163         }
1164         
1165         if (this.items) {
1166             Roo.log('changing to ul' );
1167             cfg.tag = 'ul';
1168             this.glyphicon = 'caret';
1169             if (Roo.bootstrap.version == 4) {
1170                 this.fa = 'caret-down';
1171             }
1172             
1173         }
1174         
1175         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176          
1177         //gsRoo.log(this.parentType);
1178         if (this.parentType === 'Navbar' && !this.parent().bar) {
1179             Roo.log('changing to li?');
1180             
1181             cfg.tag = 'li';
1182             
1183             cfg.cls = '';
1184             cfg.cn =  [{
1185                 tag : 'a',
1186                 cls : 'roo-button',
1187                 html : this.html,
1188                 href : this.href || '#'
1189             }];
1190             if (this.menu) {
1191                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1192                 cfg.cls += ' dropdown';
1193             }   
1194             
1195             delete cfg.html;
1196             
1197         }
1198         
1199        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1200         
1201         if (this.glyphicon) {
1202             cfg.html = ' ' + cfg.html;
1203             
1204             cfg.cn = [
1205                 {
1206                     tag: 'span',
1207                     cls: 'glyphicon glyphicon-' + this.glyphicon
1208                 }
1209             ];
1210         }
1211         if (this.fa) {
1212             cfg.html = ' ' + cfg.html;
1213             
1214             cfg.cn = [
1215                 {
1216                     tag: 'i',
1217                     cls: 'fa fas fa-' + this.fa
1218                 }
1219             ];
1220         }
1221         
1222         if (this.badge) {
1223             cfg.html += ' ';
1224             
1225             cfg.tag = 'a';
1226             
1227 //            cfg.cls='btn roo-button';
1228             
1229             cfg.href=this.href;
1230             
1231             var value = cfg.html;
1232             
1233             if(this.glyphicon){
1234                 value = {
1235                     tag: 'span',
1236                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1237                     html: this.html
1238                 };
1239             }
1240             if(this.fa){
1241                 value = {
1242                     tag: 'i',
1243                     cls: 'fa fas fa-' + this.fa,
1244                     html: this.html
1245                 };
1246             }
1247             
1248             var bw = this.badge_weight.length ? this.badge_weight :
1249                 (this.weight.length ? this.weight : 'secondary');
1250             bw = bw == 'default' ? 'secondary' : bw;
1251             
1252             cfg.cn = [
1253                 value,
1254                 {
1255                     tag: 'span',
1256                     cls: 'badge badge-' + bw,
1257                     html: this.badge
1258                 }
1259             ];
1260             
1261             cfg.html='';
1262         }
1263         
1264         if (this.menu) {
1265             cfg.cls += ' dropdown';
1266             cfg.html = typeof(cfg.html) != 'undefined' ?
1267                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1268         }
1269         
1270         if (cfg.tag !== 'a' && this.href !== '') {
1271             throw "Tag must be a to set href.";
1272         } else if (this.href.length > 0) {
1273             cfg.href = this.href;
1274         }
1275         
1276         if(this.removeClass){
1277             cfg.cls = '';
1278         }
1279         
1280         if(this.target){
1281             cfg.target = this.target;
1282         }
1283         
1284         return cfg;
1285     },
1286     initEvents: function() {
1287        // Roo.log('init events?');
1288 //        Roo.log(this.el.dom);
1289         // add the menu...
1290         
1291         if (typeof (this.menu) != 'undefined') {
1292             this.menu.parentType = this.xtype;
1293             this.menu.triggerEl = this.el;
1294             this.addxtype(Roo.apply({}, this.menu));
1295         }
1296
1297
1298         if (this.el.hasClass('roo-button')) {
1299              this.el.on('click', this.onClick, this);
1300              this.el.on('dblclick', this.onDblClick, this);
1301         } else {
1302              this.el.select('.roo-button').on('click', this.onClick, this);
1303              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1304              
1305         }
1306         // why?
1307         if(this.removeClass){
1308             this.el.on('click', this.onClick, this);
1309         }
1310         
1311         if (this.group === true) {
1312              if (this.pressed === false || this.pressed === true) {
1313                 // nothing
1314             } else {
1315                 this.pressed = false;
1316                 this.setActive(this.pressed);
1317             }
1318             
1319         }
1320         
1321         this.el.enableDisplayMode();
1322         
1323     },
1324     onClick : function(e)
1325     {
1326         if (this.disabled) {
1327             return;
1328         }
1329         
1330         Roo.log('button on click ');
1331         if(this.preventDefault){
1332             e.preventDefault();
1333         }
1334         
1335         if (this.group) {
1336             if (this.pressed) {
1337                 // do nothing -
1338                 return;
1339             }
1340             this.setActive(true);
1341             var pi = this.parent().items;
1342             for (var i = 0;i < pi.length;i++) {
1343                 if (this == pi[i]) {
1344                     continue;
1345                 }
1346                 if (pi[i].el.hasClass('roo-button')) {
1347                     pi[i].setActive(false);
1348                 }
1349             }
1350             this.fireEvent('click', this, e);            
1351             return;
1352         }
1353         
1354         if (this.pressed === true || this.pressed === false) {
1355             this.toggleActive(e);
1356         }
1357         
1358         
1359         this.fireEvent('click', this, e);
1360     },
1361     onDblClick: function(e)
1362     {
1363         if (this.disabled) {
1364             return;
1365         }
1366         if(this.preventDefault){
1367             e.preventDefault();
1368         }
1369         this.fireEvent('dblclick', this, e);
1370     },
1371     /**
1372      * Enables this button
1373      */
1374     enable : function()
1375     {
1376         this.disabled = false;
1377         this.el.removeClass('disabled');
1378         this.el.dom.removeAttribute("disabled");
1379     },
1380     
1381     /**
1382      * Disable this button
1383      */
1384     disable : function()
1385     {
1386         this.disabled = true;
1387         this.el.addClass('disabled');
1388         this.el.attr("disabled", "disabled")
1389     },
1390      /**
1391      * sets the active state on/off, 
1392      * @param {Boolean} state (optional) Force a particular state
1393      */
1394     setActive : function(v) {
1395         
1396         this.el[v ? 'addClass' : 'removeClass']('active');
1397         this.pressed = v;
1398     },
1399      /**
1400      * toggles the current active state 
1401      */
1402     toggleActive : function(e)
1403     {
1404         this.setActive(!this.pressed); // this modifies pressed...
1405         this.fireEvent('toggle', this, e, this.pressed);
1406     },
1407      /**
1408      * get the current active state
1409      * @return {boolean} true if it's active
1410      */
1411     isActive : function()
1412     {
1413         return this.el.hasClass('active');
1414     },
1415     /**
1416      * set the text of the first selected button
1417      */
1418     setText : function(str)
1419     {
1420         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1421     },
1422     /**
1423      * get the text of the first selected button
1424      */
1425     getText : function()
1426     {
1427         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1428     },
1429     
1430     setWeight : function(str)
1431     {
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1433         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434         this.weight = str;
1435         var outline = this.outline ? 'outline-' : '';
1436         if (str == 'default') {
1437             this.el.addClass('btn-default btn-outline-secondary');        
1438             return;
1439         }
1440         this.el.addClass('btn-' + outline + str);        
1441     }
1442     
1443     
1444 });
1445 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446
1447 Roo.bootstrap.Button.weights = [
1448     'default',
1449     'secondary' ,
1450     'primary',
1451     'success',
1452     'info',
1453     'warning',
1454     'danger',
1455     'link',
1456     'light',
1457     'dark'              
1458    
1459 ];/*
1460  * - LGPL
1461  *
1462  * column
1463  * 
1464  */
1465
1466 /**
1467  * @class Roo.bootstrap.Column
1468  * @extends Roo.bootstrap.Component
1469  * Bootstrap Column class
1470  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1471  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1472  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1473  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1474  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1475  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1476  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1477  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1478  *
1479  * 
1480  * @cfg {Boolean} hidden (true|false) hide the element
1481  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1482  * @cfg {String} fa (ban|check|...) font awesome icon
1483  * @cfg {Number} fasize (1|2|....) font awsome size
1484
1485  * @cfg {String} icon (info-sign|check|...) glyphicon name
1486
1487  * @cfg {String} html content of column.
1488  * 
1489  * @constructor
1490  * Create a new Column
1491  * @param {Object} config The config object
1492  */
1493
1494 Roo.bootstrap.Column = function(config){
1495     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1496 };
1497
1498 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1499     
1500     xs: false,
1501     sm: false,
1502     md: false,
1503     lg: false,
1504     xsoff: false,
1505     smoff: false,
1506     mdoff: false,
1507     lgoff: false,
1508     html: '',
1509     offset: 0,
1510     alert: false,
1511     fa: false,
1512     icon : false,
1513     hidden : false,
1514     fasize : 1,
1515     
1516     getAutoCreate : function(){
1517         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1518         
1519         cfg = {
1520             tag: 'div',
1521             cls: 'column'
1522         };
1523         
1524         var settings=this;
1525         var sizes =   ['xs','sm','md','lg'];
1526         sizes.map(function(size ,ix){
1527             //Roo.log( size + ':' + settings[size]);
1528             
1529             if (settings[size+'off'] !== false) {
1530                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1531             }
1532             
1533             if (settings[size] === false) {
1534                 return;
1535             }
1536             
1537             if (!settings[size]) { // 0 = hidden
1538                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539                 // bootsrap4
1540                 for (var i = ix; i > -1; i--) {
1541                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1542                 }
1543                 
1544                 
1545                 return;
1546             }
1547             cfg.cls += ' col-' + size + '-' + settings[size] + (
1548                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1549             );
1550             
1551         });
1552         
1553         if (this.hidden) {
1554             cfg.cls += ' hidden';
1555         }
1556         
1557         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1558             cfg.cls +=' alert alert-' + this.alert;
1559         }
1560         
1561         
1562         if (this.html.length) {
1563             cfg.html = this.html;
1564         }
1565         if (this.fa) {
1566             var fasize = '';
1567             if (this.fasize > 1) {
1568                 fasize = ' fa-' + this.fasize + 'x';
1569             }
1570             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1571             
1572             
1573         }
1574         if (this.icon) {
1575             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1576         }
1577         
1578         return cfg;
1579     }
1580    
1581 });
1582
1583  
1584
1585  /*
1586  * - LGPL
1587  *
1588  * page container.
1589  * 
1590  */
1591
1592
1593 /**
1594  * @class Roo.bootstrap.Container
1595  * @extends Roo.bootstrap.Component
1596  * Bootstrap Container class
1597  * @cfg {Boolean} jumbotron is it a jumbotron element
1598  * @cfg {String} html content of element
1599  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1600  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1601  * @cfg {String} header content of header (for panel)
1602  * @cfg {String} footer content of footer (for panel)
1603  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1604  * @cfg {String} tag (header|aside|section) type of HTML tag.
1605  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1606  * @cfg {String} fa font awesome icon
1607  * @cfg {String} icon (info-sign|check|...) glyphicon name
1608  * @cfg {Boolean} hidden (true|false) hide the element
1609  * @cfg {Boolean} expandable (true|false) default false
1610  * @cfg {Boolean} expanded (true|false) default true
1611  * @cfg {String} rheader contet on the right of header
1612  * @cfg {Boolean} clickable (true|false) default false
1613
1614  *     
1615  * @constructor
1616  * Create a new Container
1617  * @param {Object} config The config object
1618  */
1619
1620 Roo.bootstrap.Container = function(config){
1621     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1622     
1623     this.addEvents({
1624         // raw events
1625          /**
1626          * @event expand
1627          * After the panel has been expand
1628          * 
1629          * @param {Roo.bootstrap.Container} this
1630          */
1631         "expand" : true,
1632         /**
1633          * @event collapse
1634          * After the panel has been collapsed
1635          * 
1636          * @param {Roo.bootstrap.Container} this
1637          */
1638         "collapse" : true,
1639         /**
1640          * @event click
1641          * When a element is chick
1642          * @param {Roo.bootstrap.Container} this
1643          * @param {Roo.EventObject} e
1644          */
1645         "click" : true
1646     });
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1650     
1651     jumbotron : false,
1652     well: '',
1653     panel : '',
1654     header: '',
1655     footer : '',
1656     sticky: '',
1657     tag : false,
1658     alert : false,
1659     fa: false,
1660     icon : false,
1661     expandable : false,
1662     rheader : '',
1663     expanded : true,
1664     clickable: false,
1665   
1666      
1667     getChildContainer : function() {
1668         
1669         if(!this.el){
1670             return false;
1671         }
1672         
1673         if (this.panel.length) {
1674             return this.el.select('.panel-body',true).first();
1675         }
1676         
1677         return this.el;
1678     },
1679     
1680     
1681     getAutoCreate : function(){
1682         
1683         var cfg = {
1684             tag : this.tag || 'div',
1685             html : '',
1686             cls : ''
1687         };
1688         if (this.jumbotron) {
1689             cfg.cls = 'jumbotron';
1690         }
1691         
1692         
1693         
1694         // - this is applied by the parent..
1695         //if (this.cls) {
1696         //    cfg.cls = this.cls + '';
1697         //}
1698         
1699         if (this.sticky.length) {
1700             
1701             var bd = Roo.get(document.body);
1702             if (!bd.hasClass('bootstrap-sticky')) {
1703                 bd.addClass('bootstrap-sticky');
1704                 Roo.select('html',true).setStyle('height', '100%');
1705             }
1706              
1707             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1708         }
1709         
1710         
1711         if (this.well.length) {
1712             switch (this.well) {
1713                 case 'lg':
1714                 case 'sm':
1715                     cfg.cls +=' well well-' +this.well;
1716                     break;
1717                 default:
1718                     cfg.cls +=' well';
1719                     break;
1720             }
1721         }
1722         
1723         if (this.hidden) {
1724             cfg.cls += ' hidden';
1725         }
1726         
1727         
1728         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1729             cfg.cls +=' alert alert-' + this.alert;
1730         }
1731         
1732         var body = cfg;
1733         
1734         if (this.panel.length) {
1735             cfg.cls += ' panel panel-' + this.panel;
1736             cfg.cn = [];
1737             if (this.header.length) {
1738                 
1739                 var h = [];
1740                 
1741                 if(this.expandable){
1742                     
1743                     cfg.cls = cfg.cls + ' expandable';
1744                     
1745                     h.push({
1746                         tag: 'i',
1747                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1748                     });
1749                     
1750                 }
1751                 
1752                 h.push(
1753                     {
1754                         tag: 'span',
1755                         cls : 'panel-title',
1756                         html : (this.expandable ? '&nbsp;' : '') + this.header
1757                     },
1758                     {
1759                         tag: 'span',
1760                         cls: 'panel-header-right',
1761                         html: this.rheader
1762                     }
1763                 );
1764                 
1765                 cfg.cn.push({
1766                     cls : 'panel-heading',
1767                     style : this.expandable ? 'cursor: pointer' : '',
1768                     cn : h
1769                 });
1770                 
1771             }
1772             
1773             body = false;
1774             cfg.cn.push({
1775                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1776                 html : this.html
1777             });
1778             
1779             
1780             if (this.footer.length) {
1781                 cfg.cn.push({
1782                     cls : 'panel-footer',
1783                     html : this.footer
1784                     
1785                 });
1786             }
1787             
1788         }
1789         
1790         if (body) {
1791             body.html = this.html || cfg.html;
1792             // prefix with the icons..
1793             if (this.fa) {
1794                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1795             }
1796             if (this.icon) {
1797                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1798             }
1799             
1800             
1801         }
1802         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1803             cfg.cls =  'container';
1804         }
1805         
1806         return cfg;
1807     },
1808     
1809     initEvents: function() 
1810     {
1811         if(this.expandable){
1812             var headerEl = this.headerEl();
1813         
1814             if(headerEl){
1815                 headerEl.on('click', this.onToggleClick, this);
1816             }
1817         }
1818         
1819         if(this.clickable){
1820             this.el.on('click', this.onClick, this);
1821         }
1822         
1823     },
1824     
1825     onToggleClick : function()
1826     {
1827         var headerEl = this.headerEl();
1828         
1829         if(!headerEl){
1830             return;
1831         }
1832         
1833         if(this.expanded){
1834             this.collapse();
1835             return;
1836         }
1837         
1838         this.expand();
1839     },
1840     
1841     expand : function()
1842     {
1843         if(this.fireEvent('expand', this)) {
1844             
1845             this.expanded = true;
1846             
1847             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848             
1849             this.el.select('.panel-body',true).first().removeClass('hide');
1850             
1851             var toggleEl = this.toggleEl();
1852
1853             if(!toggleEl){
1854                 return;
1855             }
1856
1857             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1858         }
1859         
1860     },
1861     
1862     collapse : function()
1863     {
1864         if(this.fireEvent('collapse', this)) {
1865             
1866             this.expanded = false;
1867             
1868             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1869             this.el.select('.panel-body',true).first().addClass('hide');
1870         
1871             var toggleEl = this.toggleEl();
1872
1873             if(!toggleEl){
1874                 return;
1875             }
1876
1877             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1878         }
1879     },
1880     
1881     toggleEl : function()
1882     {
1883         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1884             return;
1885         }
1886         
1887         return this.el.select('.panel-heading .fa',true).first();
1888     },
1889     
1890     headerEl : function()
1891     {
1892         if(!this.el || !this.panel.length || !this.header.length){
1893             return;
1894         }
1895         
1896         return this.el.select('.panel-heading',true).first()
1897     },
1898     
1899     bodyEl : function()
1900     {
1901         if(!this.el || !this.panel.length){
1902             return;
1903         }
1904         
1905         return this.el.select('.panel-body',true).first()
1906     },
1907     
1908     titleEl : function()
1909     {
1910         if(!this.el || !this.panel.length || !this.header.length){
1911             return;
1912         }
1913         
1914         return this.el.select('.panel-title',true).first();
1915     },
1916     
1917     setTitle : function(v)
1918     {
1919         var titleEl = this.titleEl();
1920         
1921         if(!titleEl){
1922             return;
1923         }
1924         
1925         titleEl.dom.innerHTML = v;
1926     },
1927     
1928     getTitle : function()
1929     {
1930         
1931         var titleEl = this.titleEl();
1932         
1933         if(!titleEl){
1934             return '';
1935         }
1936         
1937         return titleEl.dom.innerHTML;
1938     },
1939     
1940     setRightTitle : function(v)
1941     {
1942         var t = this.el.select('.panel-header-right',true).first();
1943         
1944         if(!t){
1945             return;
1946         }
1947         
1948         t.dom.innerHTML = v;
1949     },
1950     
1951     onClick : function(e)
1952     {
1953         e.preventDefault();
1954         
1955         this.fireEvent('click', this, e);
1956     }
1957 });
1958
1959  /*
1960  *  - LGPL
1961  *
1962  *  This is BS4's Card element.. - similar to our containers probably..
1963  * 
1964  */
1965 /**
1966  * @class Roo.bootstrap.Card
1967  * @extends Roo.bootstrap.Component
1968  * Bootstrap Card class
1969  *
1970  *
1971  * possible... may not be implemented..
1972  * @cfg {String} header_image  src url of image.
1973  * @cfg {String|Object} header
1974  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1975  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1976  * 
1977  * @cfg {String} title
1978  * @cfg {String} subtitle
1979  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1980  * @cfg {String} footer
1981  
1982  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983  * 
1984  * @cfg {String} margin (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1990  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991  *
1992  * @cfg {String} padding (0|1|2|3|4|5)
1993  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1994  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1995  * @cfg {String} padding_left (0|1|2|3|4|5)
1996  * @cfg {String} padding_right (0|1|2|3|4|5)
1997  * @cfg {String} padding_x (0|1|2|3|4|5)
1998  * @cfg {String} padding_y (0|1|2|3|4|5)
1999  *
2000  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005  
2006  * @config {Boolean} dragable  if this card can be dragged.
2007  * @config {String} drag_group  group for drag
2008  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2009  * @config {String} drop_group  group for drag
2010  * 
2011  * @config {Boolean} collapsable can the body be collapsed.
2012  * @config {Boolean} collapsed is the body collapsed when rendered...
2013  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2014  * @config {Boolean} rotated is the body rotated when rendered...
2015  * 
2016  * @constructor
2017  * Create a new Container
2018  * @param {Object} config The config object
2019  */
2020
2021 Roo.bootstrap.Card = function(config){
2022     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2023     
2024     this.addEvents({
2025          // raw events
2026         /**
2027          * @event drop
2028          * When a element a card is dropped
2029          * @param {Roo.bootstrap.Card} this
2030          *
2031          * 
2032          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2033          * @param {String} position 'above' or 'below'
2034          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2035         
2036          */
2037         'drop' : true,
2038          /**
2039          * @event rotate
2040          * When a element a card is rotate
2041          * @param {Roo.bootstrap.Card} this
2042          * @param {Roo.Element} n the node being dropped?
2043          * @param {Boolean} rotate status
2044          */
2045         'rotate' : true,
2046         /**
2047          * @event cardover
2048          * When a card element is dragged over ready to drop (return false to block dropable)
2049          * @param {Roo.bootstrap.Card} this
2050          * @param {Object} data from dragdrop 
2051          */
2052          'cardover' : true
2053          
2054     });
2055 };
2056
2057
2058 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2059     
2060     
2061     weight : '',
2062     
2063     margin: '', /// may be better in component?
2064     margin_top: '', 
2065     margin_bottom: '', 
2066     margin_left: '',
2067     margin_right: '',
2068     margin_x: '',
2069     margin_y: '',
2070     
2071     padding : '',
2072     padding_top: '', 
2073     padding_bottom: '', 
2074     padding_left: '',
2075     padding_right: '',
2076     padding_x: '',
2077     padding_y: '',
2078     
2079     display: '', 
2080     display_xs: '', 
2081     display_sm: '', 
2082     display_lg: '',
2083     display_xl: '',
2084  
2085     header_image  : '',
2086     header : '',
2087     header_size : 0,
2088     title : '',
2089     subtitle : '',
2090     html : '',
2091     footer: '',
2092
2093     collapsable : false,
2094     collapsed : false,
2095     rotateable : false,
2096     rotated : false,
2097     
2098     dragable : false,
2099     drag_group : false,
2100     dropable : false,
2101     drop_group : false,
2102     childContainer : false,
2103     dropEl : false, /// the dom placeholde element that indicates drop location.
2104     containerEl: false, // body container
2105     bodyEl: false, // card-body
2106     headerContainerEl : false, //
2107     headerEl : false,
2108     header_imageEl : false,
2109     
2110     
2111     layoutCls : function()
2112     {
2113         var cls = '';
2114         var t = this;
2115         Roo.log(this.margin_bottom.length);
2116         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2117             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118             
2119             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2120                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2121             }
2122             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2123                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2124             }
2125         });
2126         
2127         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2128             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2130             }
2131         });
2132         
2133         // more generic support?
2134         if (this.hidden) {
2135             cls += ' d-none';
2136         }
2137         
2138         return cls;
2139     },
2140  
2141        // Roo.log("Call onRender: " + this.xtype);
2142         /*  We are looking at something like this.
2143 <div class="card">
2144     <img src="..." class="card-img-top" alt="...">
2145     <div class="card-body">
2146         <h5 class="card-title">Card title</h5>
2147          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148
2149         >> this bit is really the body...
2150         <div> << we will ad dthis in hopefully it will not break shit.
2151         
2152         ** card text does not actually have any styling...
2153         
2154             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2155         
2156         </div> <<
2157           <a href="#" class="card-link">Card link</a>
2158           
2159     </div>
2160     <div class="card-footer">
2161         <small class="text-muted">Last updated 3 mins ago</small>
2162     </div>
2163 </div>
2164          */
2165     getAutoCreate : function(){
2166         
2167         var cfg = {
2168             tag : 'div',
2169             cls : 'card',
2170             cn : [ ]
2171         };
2172         
2173         if (this.weight.length && this.weight != 'light') {
2174             cfg.cls += ' text-white';
2175         } else {
2176             cfg.cls += ' text-dark'; // need as it's nested..
2177         }
2178         if (this.weight.length) {
2179             cfg.cls += ' bg-' + this.weight;
2180         }
2181         
2182         cfg.cls += ' ' + this.layoutCls(); 
2183         
2184         var hdr = false;
2185         var hdr_ctr = false;
2186         if (this.header.length) {
2187             hdr = {
2188                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2189                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2190                 cn : []
2191             };
2192             cfg.cn.push(hdr);
2193             hdr_ctr = hdr;
2194         } else {
2195             hdr = {
2196                 tag : 'div',
2197                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2198                 cn : []
2199             };
2200             cfg.cn.push(hdr);
2201             hdr_ctr = hdr;
2202         }
2203         if (this.collapsable) {
2204             hdr_ctr = {
2205             tag : 'a',
2206             cls : 'd-block user-select-none',
2207             cn: [
2208                     {
2209                         tag: 'i',
2210                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2211                     }
2212                    
2213                 ]
2214             };
2215             hdr.cn.push(hdr_ctr);
2216         }
2217         
2218         hdr_ctr.cn.push(        {
2219             tag: 'span',
2220             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2221             html : this.header
2222         });
2223         
2224         
2225         if (this.header_image.length) {
2226             cfg.cn.push({
2227                 tag : 'img',
2228                 cls : 'card-img-top',
2229                 src: this.header_image // escape?
2230             });
2231         } else {
2232             cfg.cn.push({
2233                     tag : 'div',
2234                     cls : 'card-img-top d-none' 
2235                 });
2236         }
2237             
2238         var body = {
2239             tag : 'div',
2240             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2241             cn : []
2242         };
2243         var obody = body;
2244         if (this.collapsable || this.rotateable) {
2245             obody = {
2246                 tag: 'div',
2247                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2248                 cn : [  body ]
2249             };
2250         }
2251         
2252         cfg.cn.push(obody);
2253         
2254         if (this.title.length) {
2255             body.cn.push({
2256                 tag : 'div',
2257                 cls : 'card-title',
2258                 src: this.title // escape?
2259             });
2260         }  
2261         
2262         if (this.subtitle.length) {
2263             body.cn.push({
2264                 tag : 'div',
2265                 cls : 'card-title',
2266                 src: this.subtitle // escape?
2267             });
2268         }
2269         
2270         body.cn.push({
2271             tag : 'div',
2272             cls : 'roo-card-body-ctr'
2273         });
2274         
2275         if (this.html.length) {
2276             body.cn.push({
2277                 tag: 'div',
2278                 html : this.html
2279             });
2280         }
2281         // fixme ? handle objects?
2282         
2283         if (this.footer.length) {
2284            
2285             cfg.cn.push({
2286                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2287                 html : this.footer
2288             });
2289             
2290         } else {
2291             cfg.cn.push({cls : 'card-footer d-none'});
2292         }
2293         
2294         // footer...
2295         
2296         return cfg;
2297     },
2298     
2299     
2300     getCardHeader : function()
2301     {
2302         var  ret = this.el.select('.card-header',true).first();
2303         if (ret.hasClass('d-none')) {
2304             ret.removeClass('d-none');
2305         }
2306         
2307         return ret;
2308     },
2309     getCardFooter : function()
2310     {
2311         var  ret = this.el.select('.card-footer',true).first();
2312         if (ret.hasClass('d-none')) {
2313             ret.removeClass('d-none');
2314         }
2315         
2316         return ret;
2317     },
2318     getCardImageTop : function()
2319     {
2320         var  ret = this.header_imageEl;
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324             
2325         return ret;
2326     },
2327     
2328     getChildContainer : function()
2329     {
2330         
2331         if(!this.el){
2332             return false;
2333         }
2334         return this.el.select('.roo-card-body-ctr',true).first();    
2335     },
2336     
2337     initEvents: function() 
2338     {
2339         this.bodyEl = this.el.select('.card-body',true).first(); 
2340         this.containerEl = this.getChildContainer();
2341         if(this.dragable){
2342             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2343                     containerScroll: true,
2344                     ddGroup: this.drag_group || 'default_card_drag_group'
2345             });
2346             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347         }
2348         if (this.dropable) {
2349             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2350                 containerScroll: true,
2351                 ddGroup: this.drop_group || 'default_card_drag_group'
2352             });
2353             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2354             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2355             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2356             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2357             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2358         }
2359         
2360         if (this.collapsable) {
2361             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362         }
2363         if (this.rotateable) {
2364             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365         }
2366         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367          
2368         this.footerEl = this.el.select('.card-footer',true).first();
2369         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2370         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2371         this.headerEl = this.el.select('.card-header',true).first();
2372         
2373         if (this.rotated) {
2374             this.el.addClass('roo-card-rotated');
2375             this.fireEvent('rotate', this, true);
2376         }
2377         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2378         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2379         
2380     },
2381     getDragData : function(e)
2382     {
2383         var target = this.getEl();
2384         if (target) {
2385             //this.handleSelection(e);
2386             
2387             var dragData = {
2388                 source: this,
2389                 copy: false,
2390                 nodes: this.getEl(),
2391                 records: []
2392             };
2393             
2394             
2395             dragData.ddel = target.dom ;    // the div element
2396             Roo.log(target.getWidth( ));
2397             dragData.ddel.style.width = target.getWidth() + 'px';
2398             
2399             return dragData;
2400         }
2401         return false;
2402     },
2403     /**
2404     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2405     *    whole Element becomes the target, and this causes the drop gesture to append.
2406     *
2407     *    Returns an object:
2408     *     {
2409            
2410            position : 'below' or 'above'
2411            card  : relateive to card OBJECT (or true for no cards listed)
2412            items_n : relative to nth item in list
2413            card_n : relative to  nth card in list
2414     }
2415     *
2416     *    
2417     */
2418     getTargetFromEvent : function(e, dragged_card_el)
2419     {
2420         var target = e.getTarget();
2421         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2422             target = target.parentNode;
2423         }
2424         
2425         var ret = {
2426             position: '',
2427             cards : [],
2428             card_n : -1,
2429             items_n : -1,
2430             card : false 
2431         };
2432         
2433         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2434         // see if target is one of the 'cards'...
2435         
2436         
2437         //Roo.log(this.items.length);
2438         var pos = false;
2439         
2440         var last_card_n = 0;
2441         var cards_len  = 0;
2442         for (var i = 0;i< this.items.length;i++) {
2443             
2444             if (!this.items[i].el.hasClass('card')) {
2445                  continue;
2446             }
2447             pos = this.getDropPoint(e, this.items[i].el.dom);
2448             
2449             cards_len = ret.cards.length;
2450             //Roo.log(this.items[i].el.dom.id);
2451             ret.cards.push(this.items[i]);
2452             last_card_n  = i;
2453             if (ret.card_n < 0 && pos == 'above') {
2454                 ret.position = cards_len > 0 ? 'below' : pos;
2455                 ret.items_n = i > 0 ? i - 1 : 0;
2456                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2457                 ret.card = ret.cards[ret.card_n];
2458             }
2459         }
2460         if (!ret.cards.length) {
2461             ret.card = true;
2462             ret.position = 'below';
2463             ret.items_n;
2464             return ret;
2465         }
2466         // could not find a card.. stick it at the end..
2467         if (ret.card_n < 0) {
2468             ret.card_n = last_card_n;
2469             ret.card = ret.cards[last_card_n];
2470             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2471             ret.position = 'below';
2472         }
2473         
2474         if (this.items[ret.items_n].el == dragged_card_el) {
2475             return false;
2476         }
2477         
2478         if (ret.position == 'below') {
2479             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480             
2481             if (card_after  && card_after.el == dragged_card_el) {
2482                 return false;
2483             }
2484             return ret;
2485         }
2486         
2487         // its's after ..
2488         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489         
2490         if (card_before  && card_before.el == dragged_card_el) {
2491             return false;
2492         }
2493         
2494         return ret;
2495     },
2496     
2497     onNodeEnter : function(n, dd, e, data){
2498         return false;
2499     },
2500     onNodeOver : function(n, dd, e, data)
2501     {
2502        
2503         var target_info = this.getTargetFromEvent(e,data.source.el);
2504         if (target_info === false) {
2505             this.dropPlaceHolder('hide');
2506             return false;
2507         }
2508         Roo.log(['getTargetFromEvent', target_info ]);
2509         
2510         
2511         if (this.fireEvent('cardover', this, [ data ]) === false) {
2512             return false;
2513         }
2514         
2515         this.dropPlaceHolder('show', target_info,data);
2516         
2517         return false; 
2518     },
2519     onNodeOut : function(n, dd, e, data){
2520         this.dropPlaceHolder('hide');
2521      
2522     },
2523     onNodeDrop : function(n, dd, e, data)
2524     {
2525         
2526         // call drop - return false if
2527         
2528         // this could actually fail - if the Network drops..
2529         // we will ignore this at present..- client should probably reload
2530         // the whole set of cards if stuff like that fails.
2531         
2532         
2533         var info = this.getTargetFromEvent(e,data.source.el);
2534         if (info === false) {
2535             return false;
2536         }
2537         this.dropPlaceHolder('hide');
2538   
2539           
2540     
2541         this.acceptCard(data.source, info.position, info.card, info.items_n);
2542         return true;
2543          
2544     },
2545     firstChildCard : function()
2546     {
2547         for (var i = 0;i< this.items.length;i++) {
2548             
2549             if (!this.items[i].el.hasClass('card')) {
2550                  continue;
2551             }
2552             return this.items[i];
2553         }
2554         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2555     },
2556     /**
2557      * accept card
2558      *
2559      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2560      */
2561     acceptCard : function(move_card,  position, next_to_card )
2562     {
2563         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2564             return false;
2565         }
2566         
2567         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568         
2569         move_card.parent().removeCard(move_card);
2570         
2571         
2572         var dom = move_card.el.dom;
2573         dom.style.width = ''; // clear with - which is set by drag.
2574         
2575         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2576             var cardel = next_to_card.el.dom;
2577             
2578             if (position == 'above' ) {
2579                 cardel.parentNode.insertBefore(dom, cardel);
2580             } else if (cardel.nextSibling) {
2581                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582             } else {
2583                 cardel.parentNode.append(dom);
2584             }
2585         } else {
2586             // card container???
2587             this.containerEl.dom.append(dom);
2588         }
2589         
2590         //FIXME HANDLE card = true 
2591         
2592         // add this to the correct place in items.
2593         
2594         // remove Card from items.
2595         
2596        
2597         if (this.items.length) {
2598             var nitems = [];
2599             //Roo.log([info.items_n, info.position, this.items.length]);
2600             for (var i =0; i < this.items.length; i++) {
2601                 if (i == to_items_n && position == 'above') {
2602                     nitems.push(move_card);
2603                 }
2604                 nitems.push(this.items[i]);
2605                 if (i == to_items_n && position == 'below') {
2606                     nitems.push(move_card);
2607                 }
2608             }
2609             this.items = nitems;
2610             Roo.log(this.items);
2611         } else {
2612             this.items.push(move_card);
2613         }
2614         
2615         move_card.parentId = this.id;
2616         
2617         return true;
2618         
2619         
2620     },
2621     removeCard : function(c)
2622     {
2623         this.items = this.items.filter(function(e) { return e != c });
2624  
2625         var dom = c.el.dom;
2626         dom.parentNode.removeChild(dom);
2627         dom.style.width = ''; // clear with - which is set by drag.
2628         c.parentId = false;
2629         
2630     },
2631     
2632     /**    Decide whether to drop above or below a View node. */
2633     getDropPoint : function(e, n, dd)
2634     {
2635         if (dd) {
2636              return false;
2637         }
2638         if (n == this.containerEl.dom) {
2639             return "above";
2640         }
2641         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2642         var c = t + (b - t) / 2;
2643         var y = Roo.lib.Event.getPageY(e);
2644         if(y <= c) {
2645             return "above";
2646         }else{
2647             return "below";
2648         }
2649     },
2650     onToggleCollapse : function(e)
2651         {
2652         if (this.collapsed) {
2653             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2654             this.collapsableEl.addClass('show');
2655             this.collapsed = false;
2656             return;
2657         }
2658         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2659         this.collapsableEl.removeClass('show');
2660         this.collapsed = true;
2661         
2662     
2663     },
2664     
2665     onToggleRotate : function(e)
2666     {
2667         this.collapsableEl.removeClass('show');
2668         this.footerEl.removeClass('d-none');
2669         this.el.removeClass('roo-card-rotated');
2670         this.el.removeClass('d-none');
2671         if (this.rotated) {
2672             
2673             this.collapsableEl.addClass('show');
2674             this.rotated = false;
2675             this.fireEvent('rotate', this, this.rotated);
2676             return;
2677         }
2678         this.el.addClass('roo-card-rotated');
2679         this.footerEl.addClass('d-none');
2680         this.el.select('.roo-collapsable').removeClass('show');
2681         
2682         this.rotated = true;
2683         this.fireEvent('rotate', this, this.rotated);
2684     
2685     },
2686     
2687     dropPlaceHolder: function (action, info, data)
2688     {
2689         if (this.dropEl === false) {
2690             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2691             cls : 'd-none'
2692             },true);
2693         }
2694         this.dropEl.removeClass(['d-none', 'd-block']);        
2695         if (action == 'hide') {
2696             
2697             this.dropEl.addClass('d-none');
2698             return;
2699         }
2700         // FIXME - info.card == true!!!
2701         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702         
2703         if (info.card !== true) {
2704             var cardel = info.card.el.dom;
2705             
2706             if (info.position == 'above') {
2707                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2708             } else if (cardel.nextSibling) {
2709                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710             } else {
2711                 cardel.parentNode.append(this.dropEl.dom);
2712             }
2713         } else {
2714             // card container???
2715             this.containerEl.dom.append(this.dropEl.dom);
2716         }
2717         
2718         this.dropEl.addClass('d-block roo-card-dropzone');
2719         
2720         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2721         
2722         
2723     
2724     
2725     
2726     },
2727     setHeaderText: function(html)
2728     {
2729         this.header = html;
2730         if (this.headerContainerEl) {
2731             this.headerContainerEl.dom.innerHTML = html;
2732         }
2733     },
2734     onHeaderImageLoad : function(ev, he)
2735     {
2736         if (!this.header_image_fit_square) {
2737             return;
2738         }
2739         
2740         var hw = he.naturalHeight / he.naturalWidth;
2741         // wide image = < 0
2742         // tall image = > 1
2743         //var w = he.dom.naturalWidth;
2744         var ww = he.width;
2745         he.style.left =  0;
2746         he.style.position =  'relative';
2747         if (hw > 1) {
2748             var nw = (ww * (1/hw));
2749             Roo.get(he).setSize( ww * (1/hw),  ww);
2750             he.style.left =  ((ww - nw)/ 2) + 'px';
2751             he.style.position =  'relative';
2752         }
2753
2754     }
2755
2756     
2757 });
2758
2759 /*
2760  * - LGPL
2761  *
2762  * Card header - holder for the card header elements.
2763  * 
2764  */
2765
2766 /**
2767  * @class Roo.bootstrap.CardHeader
2768  * @extends Roo.bootstrap.Element
2769  * Bootstrap CardHeader class
2770  * @constructor
2771  * Create a new Card Header - that you can embed children into
2772  * @param {Object} config The config object
2773  */
2774
2775 Roo.bootstrap.CardHeader = function(config){
2776     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2777 };
2778
2779 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2780     
2781     
2782     container_method : 'getCardHeader' 
2783     
2784      
2785     
2786     
2787    
2788 });
2789
2790  
2791
2792  /*
2793  * - LGPL
2794  *
2795  * Card footer - holder for the card footer elements.
2796  * 
2797  */
2798
2799 /**
2800  * @class Roo.bootstrap.CardFooter
2801  * @extends Roo.bootstrap.Element
2802  * Bootstrap CardFooter class
2803  * @constructor
2804  * Create a new Card Footer - that you can embed children into
2805  * @param {Object} config The config object
2806  */
2807
2808 Roo.bootstrap.CardFooter = function(config){
2809     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2810 };
2811
2812 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2813     
2814     
2815     container_method : 'getCardFooter' 
2816     
2817      
2818     
2819     
2820    
2821 });
2822
2823  
2824
2825  /*
2826  * - LGPL
2827  *
2828  * Card header - holder for the card header elements.
2829  * 
2830  */
2831
2832 /**
2833  * @class Roo.bootstrap.CardImageTop
2834  * @extends Roo.bootstrap.Element
2835  * Bootstrap CardImageTop class
2836  * @constructor
2837  * Create a new Card Image Top container
2838  * @param {Object} config The config object
2839  */
2840
2841 Roo.bootstrap.CardImageTop = function(config){
2842     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2843 };
2844
2845 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2846     
2847    
2848     container_method : 'getCardImageTop' 
2849     
2850      
2851     
2852    
2853 });
2854
2855  
2856
2857  
2858 /*
2859 * Licence: LGPL
2860 */
2861
2862 /**
2863  * @class Roo.bootstrap.ButtonUploader
2864  * @extends Roo.bootstrap.Button
2865  * Bootstrap Button Uploader class - it's a button which when you add files to it
2866  *
2867  * 
2868  * @cfg {Number} errorTimeout default 3000
2869  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2870  * @cfg {Array}  html The button text.
2871  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2872  *
2873  * @constructor
2874  * Create a new CardUploader
2875  * @param {Object} config The config object
2876  */
2877
2878 Roo.bootstrap.ButtonUploader = function(config){
2879     
2880  
2881     
2882     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2883     
2884      
2885      this.addEvents({
2886          // raw events
2887         /**
2888          * @event beforeselect
2889          * When button is pressed, before show upload files dialog is shown
2890          * @param {Roo.bootstrap.UploaderButton} this
2891          *
2892          */
2893         'beforeselect' : true,
2894          /**
2895          * @event fired when files have been selected, 
2896          * When a the download link is clicked
2897          * @param {Roo.bootstrap.UploaderButton} this
2898          * @param {Array} Array of files that have been uploaded
2899          */
2900         'uploaded' : true
2901         
2902     });
2903 };
2904  
2905 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2906     
2907      
2908     errorTimeout : 3000,
2909      
2910     images : false,
2911    
2912     fileCollection : false,
2913     allowBlank : true,
2914     
2915     multiple : true,
2916     
2917     getAutoCreate : function()
2918     {
2919         var im = {
2920             tag: 'input',
2921             type : 'file',
2922             cls : 'd-none  roo-card-upload-selector' 
2923           
2924         };
2925         if (this.multiple) {
2926             im.multiple = 'multiple';
2927         }
2928         
2929         return  {
2930             cls :'div' ,
2931             cn : [
2932                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2933                 im
2934
2935             ]
2936         };
2937            
2938          
2939     },
2940      
2941    
2942     initEvents : function()
2943     {
2944         
2945         Roo.bootstrap.Button.prototype.initEvents.call(this);
2946         
2947         
2948         
2949         
2950         
2951         this.urlAPI = (window.createObjectURL && window) || 
2952                                 (window.URL && URL.revokeObjectURL && URL) || 
2953                                 (window.webkitURL && webkitURL);
2954                         
2955          
2956          
2957          
2958         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959         
2960         this.selectorEl.on('change', this.onFileSelected, this);
2961          
2962          
2963        
2964     },
2965     
2966    
2967     onClick : function(e)
2968     {
2969         e.preventDefault();
2970         
2971         if ( this.fireEvent('beforeselect', this) === false) {
2972             return;
2973         }
2974          
2975         this.selectorEl.dom.click();
2976          
2977     },
2978     
2979     onFileSelected : function(e)
2980     {
2981         e.preventDefault();
2982         
2983         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2984             return;
2985         }
2986         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2987         this.selectorEl.dom.value  = '';// hopefully reset..
2988         
2989         this.fireEvent('uploaded', this,  files );
2990         
2991     },
2992     
2993        
2994    
2995     
2996     /**
2997      * addCard - add an Attachment to the uploader
2998      * @param data - the data about the image to upload
2999      *
3000      * {
3001           id : 123
3002           title : "Title of file",
3003           is_uploaded : false,
3004           src : "http://.....",
3005           srcfile : { the File upload object },
3006           mimetype : file.type,
3007           preview : false,
3008           is_deleted : 0
3009           .. any other data...
3010         }
3011      *
3012      * 
3013     */
3014      
3015     reset: function()
3016     {
3017          
3018          this.selectorEl
3019     } 
3020     
3021     
3022     
3023     
3024 });
3025  /*
3026  * - LGPL
3027  *
3028  * image
3029  * 
3030  */
3031
3032
3033 /**
3034  * @class Roo.bootstrap.Img
3035  * @extends Roo.bootstrap.Component
3036  * Bootstrap Img class
3037  * @cfg {Boolean} imgResponsive false | true
3038  * @cfg {String} border rounded | circle | thumbnail
3039  * @cfg {String} src image source
3040  * @cfg {String} alt image alternative text
3041  * @cfg {String} href a tag href
3042  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3043  * @cfg {String} xsUrl xs image source
3044  * @cfg {String} smUrl sm image source
3045  * @cfg {String} mdUrl md image source
3046  * @cfg {String} lgUrl lg image source
3047  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3048  * 
3049  * @constructor
3050  * Create a new Input
3051  * @param {Object} config The config object
3052  */
3053
3054 Roo.bootstrap.Img = function(config){
3055     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3056     
3057     this.addEvents({
3058         // img events
3059         /**
3060          * @event click
3061          * The img click event for the img.
3062          * @param {Roo.EventObject} e
3063          */
3064         "click" : true,
3065         /**
3066          * @event load
3067          * The when any image loads
3068          * @param {Roo.EventObject} e
3069          */
3070         "load" : true
3071     });
3072 };
3073
3074 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3075     
3076     imgResponsive: true,
3077     border: '',
3078     src: 'about:blank',
3079     href: false,
3080     target: false,
3081     xsUrl: '',
3082     smUrl: '',
3083     mdUrl: '',
3084     lgUrl: '',
3085     backgroundContain : false,
3086
3087     getAutoCreate : function()
3088     {   
3089         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3090             return this.createSingleImg();
3091         }
3092         
3093         var cfg = {
3094             tag: 'div',
3095             cls: 'roo-image-responsive-group',
3096             cn: []
3097         };
3098         var _this = this;
3099         
3100         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101             
3102             if(!_this[size + 'Url']){
3103                 return;
3104             }
3105             
3106             var img = {
3107                 tag: 'img',
3108                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3109                 html: _this.html || cfg.html,
3110                 src: _this[size + 'Url']
3111             };
3112             
3113             img.cls += ' roo-image-responsive-' + size;
3114             
3115             var s = ['xs', 'sm', 'md', 'lg'];
3116             
3117             s.splice(s.indexOf(size), 1);
3118             
3119             Roo.each(s, function(ss){
3120                 img.cls += ' hidden-' + ss;
3121             });
3122             
3123             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3124                 cfg.cls += ' img-' + _this.border;
3125             }
3126             
3127             if(_this.alt){
3128                 cfg.alt = _this.alt;
3129             }
3130             
3131             if(_this.href){
3132                 var a = {
3133                     tag: 'a',
3134                     href: _this.href,
3135                     cn: [
3136                         img
3137                     ]
3138                 };
3139
3140                 if(this.target){
3141                     a.target = _this.target;
3142                 }
3143             }
3144             
3145             cfg.cn.push((_this.href) ? a : img);
3146             
3147         });
3148         
3149         return cfg;
3150     },
3151     
3152     createSingleImg : function()
3153     {
3154         var cfg = {
3155             tag: 'img',
3156             cls: (this.imgResponsive) ? 'img-responsive' : '',
3157             html : null,
3158             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3159         };
3160         
3161         if (this.backgroundContain) {
3162             cfg.cls += ' background-contain';
3163         }
3164         
3165         cfg.html = this.html || cfg.html;
3166         
3167         if (this.backgroundContain) {
3168             cfg.style="background-image: url(" + this.src + ')';
3169         } else {
3170             cfg.src = this.src || cfg.src;
3171         }
3172         
3173         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3174             cfg.cls += ' img-' + this.border;
3175         }
3176         
3177         if(this.alt){
3178             cfg.alt = this.alt;
3179         }
3180         
3181         if(this.href){
3182             var a = {
3183                 tag: 'a',
3184                 href: this.href,
3185                 cn: [
3186                     cfg
3187                 ]
3188             };
3189             
3190             if(this.target){
3191                 a.target = this.target;
3192             }
3193             
3194         }
3195         
3196         return (this.href) ? a : cfg;
3197     },
3198     
3199     initEvents: function() 
3200     {
3201         if(!this.href){
3202             this.el.on('click', this.onClick, this);
3203         }
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.on('load', this.onImageLoad, this);
3206         } else {
3207             // not sure if this works.. not tested
3208             this.el.select('img', true).on('load', this.onImageLoad, this);
3209         }
3210         
3211     },
3212     
3213     onClick : function(e)
3214     {
3215         Roo.log('img onclick');
3216         this.fireEvent('click', this, e);
3217     },
3218     onImageLoad: function(e)
3219     {
3220         Roo.log('img load');
3221         this.fireEvent('load', this, e);
3222     },
3223     
3224     /**
3225      * Sets the url of the image - used to update it
3226      * @param {String} url the url of the image
3227      */
3228     
3229     setSrc : function(url)
3230     {
3231         this.src =  url;
3232         
3233         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3234             if (this.backgroundContain) {
3235                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3236             } else {
3237                 this.el.dom.src =  url;
3238             }
3239             return;
3240         }
3241         
3242         this.el.select('img', true).first().dom.src =  url;
3243     }
3244     
3245     
3246    
3247 });
3248
3249  /*
3250  * - LGPL
3251  *
3252  * image
3253  * 
3254  */
3255
3256
3257 /**
3258  * @class Roo.bootstrap.Link
3259  * @extends Roo.bootstrap.Component
3260  * Bootstrap Link Class
3261  * @cfg {String} alt image alternative text
3262  * @cfg {String} href a tag href
3263  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3264  * @cfg {String} html the content of the link.
3265  * @cfg {String} anchor name for the anchor link
3266  * @cfg {String} fa - favicon
3267
3268  * @cfg {Boolean} preventDefault (true | false) default false
3269
3270  * 
3271  * @constructor
3272  * Create a new Input
3273  * @param {Object} config The config object
3274  */
3275
3276 Roo.bootstrap.Link = function(config){
3277     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3278     
3279     this.addEvents({
3280         // img events
3281         /**
3282          * @event click
3283          * The img click event for the img.
3284          * @param {Roo.EventObject} e
3285          */
3286         "click" : true
3287     });
3288 };
3289
3290 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3291     
3292     href: false,
3293     target: false,
3294     preventDefault: false,
3295     anchor : false,
3296     alt : false,
3297     fa: false,
3298
3299
3300     getAutoCreate : function()
3301     {
3302         var html = this.html || '';
3303         
3304         if (this.fa !== false) {
3305             html = '<i class="fa fa-' + this.fa + '"></i>';
3306         }
3307         var cfg = {
3308             tag: 'a'
3309         };
3310         // anchor's do not require html/href...
3311         if (this.anchor === false) {
3312             cfg.html = html;
3313             cfg.href = this.href || '#';
3314         } else {
3315             cfg.name = this.anchor;
3316             if (this.html !== false || this.fa !== false) {
3317                 cfg.html = html;
3318             }
3319             if (this.href !== false) {
3320                 cfg.href = this.href;
3321             }
3322         }
3323         
3324         if(this.alt !== false){
3325             cfg.alt = this.alt;
3326         }
3327         
3328         
3329         if(this.target !== false) {
3330             cfg.target = this.target;
3331         }
3332         
3333         return cfg;
3334     },
3335     
3336     initEvents: function() {
3337         
3338         if(!this.href || this.preventDefault){
3339             this.el.on('click', this.onClick, this);
3340         }
3341     },
3342     
3343     onClick : function(e)
3344     {
3345         if(this.preventDefault){
3346             e.preventDefault();
3347         }
3348         //Roo.log('img onclick');
3349         this.fireEvent('click', this, e);
3350     }
3351    
3352 });
3353
3354  /*
3355  * - LGPL
3356  *
3357  * header
3358  * 
3359  */
3360
3361 /**
3362  * @class Roo.bootstrap.Header
3363  * @extends Roo.bootstrap.Component
3364  * Bootstrap Header class
3365  * @cfg {String} html content of header
3366  * @cfg {Number} level (1|2|3|4|5|6) default 1
3367  * 
3368  * @constructor
3369  * Create a new Header
3370  * @param {Object} config The config object
3371  */
3372
3373
3374 Roo.bootstrap.Header  = function(config){
3375     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3376 };
3377
3378 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3379     
3380     //href : false,
3381     html : false,
3382     level : 1,
3383     
3384     
3385     
3386     getAutoCreate : function(){
3387         
3388         
3389         
3390         var cfg = {
3391             tag: 'h' + (1 *this.level),
3392             html: this.html || ''
3393         } ;
3394         
3395         return cfg;
3396     }
3397    
3398 });
3399
3400  
3401
3402  /*
3403  * Based on:
3404  * Ext JS Library 1.1.1
3405  * Copyright(c) 2006-2007, Ext JS, LLC.
3406  *
3407  * Originally Released Under LGPL - original licence link has changed is not relivant.
3408  *
3409  * Fork - LGPL
3410  * <script type="text/javascript">
3411  */
3412  
3413 /**
3414  * @class Roo.bootstrap.MenuMgr
3415  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3416  * @singleton
3417  */
3418 Roo.bootstrap.MenuMgr = function(){
3419    var menus, active, groups = {}, attached = false, lastShow = new Date();
3420
3421    // private - called when first menu is created
3422    function init(){
3423        menus = {};
3424        active = new Roo.util.MixedCollection();
3425        Roo.get(document).addKeyListener(27, function(){
3426            if(active.length > 0){
3427                hideAll();
3428            }
3429        });
3430    }
3431
3432    // private
3433    function hideAll(){
3434        if(active && active.length > 0){
3435            var c = active.clone();
3436            c.each(function(m){
3437                m.hide();
3438            });
3439        }
3440    }
3441
3442    // private
3443    function onHide(m){
3444        active.remove(m);
3445        if(active.length < 1){
3446            Roo.get(document).un("mouseup", onMouseDown);
3447             
3448            attached = false;
3449        }
3450    }
3451
3452    // private
3453    function onShow(m){
3454        var last = active.last();
3455        lastShow = new Date();
3456        active.add(m);
3457        if(!attached){
3458           Roo.get(document).on("mouseup", onMouseDown);
3459            
3460            attached = true;
3461        }
3462        if(m.parentMenu){
3463           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3464           m.parentMenu.activeChild = m;
3465        }else if(last && last.isVisible()){
3466           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3467        }
3468    }
3469
3470    // private
3471    function onBeforeHide(m){
3472        if(m.activeChild){
3473            m.activeChild.hide();
3474        }
3475        if(m.autoHideTimer){
3476            clearTimeout(m.autoHideTimer);
3477            delete m.autoHideTimer;
3478        }
3479    }
3480
3481    // private
3482    function onBeforeShow(m){
3483        var pm = m.parentMenu;
3484        if(!pm && !m.allowOtherMenus){
3485            hideAll();
3486        }else if(pm && pm.activeChild && active != m){
3487            pm.activeChild.hide();
3488        }
3489    }
3490
3491    // private this should really trigger on mouseup..
3492    function onMouseDown(e){
3493         Roo.log("on Mouse Up");
3494         
3495         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3496             Roo.log("MenuManager hideAll");
3497             hideAll();
3498             e.stopEvent();
3499         }
3500         
3501         
3502    }
3503
3504    // private
3505    function onBeforeCheck(mi, state){
3506        if(state){
3507            var g = groups[mi.group];
3508            for(var i = 0, l = g.length; i < l; i++){
3509                if(g[i] != mi){
3510                    g[i].setChecked(false);
3511                }
3512            }
3513        }
3514    }
3515
3516    return {
3517
3518        /**
3519         * Hides all menus that are currently visible
3520         */
3521        hideAll : function(){
3522             hideAll();  
3523        },
3524
3525        // private
3526        register : function(menu){
3527            if(!menus){
3528                init();
3529            }
3530            menus[menu.id] = menu;
3531            menu.on("beforehide", onBeforeHide);
3532            menu.on("hide", onHide);
3533            menu.on("beforeshow", onBeforeShow);
3534            menu.on("show", onShow);
3535            var g = menu.group;
3536            if(g && menu.events["checkchange"]){
3537                if(!groups[g]){
3538                    groups[g] = [];
3539                }
3540                groups[g].push(menu);
3541                menu.on("checkchange", onCheck);
3542            }
3543        },
3544
3545         /**
3546          * Returns a {@link Roo.menu.Menu} object
3547          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3548          * be used to generate and return a new Menu instance.
3549          */
3550        get : function(menu){
3551            if(typeof menu == "string"){ // menu id
3552                return menus[menu];
3553            }else if(menu.events){  // menu instance
3554                return menu;
3555            }
3556            /*else if(typeof menu.length == 'number'){ // array of menu items?
3557                return new Roo.bootstrap.Menu({items:menu});
3558            }else{ // otherwise, must be a config
3559                return new Roo.bootstrap.Menu(menu);
3560            }
3561            */
3562            return false;
3563        },
3564
3565        // private
3566        unregister : function(menu){
3567            delete menus[menu.id];
3568            menu.un("beforehide", onBeforeHide);
3569            menu.un("hide", onHide);
3570            menu.un("beforeshow", onBeforeShow);
3571            menu.un("show", onShow);
3572            var g = menu.group;
3573            if(g && menu.events["checkchange"]){
3574                groups[g].remove(menu);
3575                menu.un("checkchange", onCheck);
3576            }
3577        },
3578
3579        // private
3580        registerCheckable : function(menuItem){
3581            var g = menuItem.group;
3582            if(g){
3583                if(!groups[g]){
3584                    groups[g] = [];
3585                }
3586                groups[g].push(menuItem);
3587                menuItem.on("beforecheckchange", onBeforeCheck);
3588            }
3589        },
3590
3591        // private
3592        unregisterCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                groups[g].remove(menuItem);
3596                menuItem.un("beforecheckchange", onBeforeCheck);
3597            }
3598        }
3599    };
3600 }();/*
3601  * - LGPL
3602  *
3603  * menu
3604  * 
3605  */
3606
3607 /**
3608  * @class Roo.bootstrap.Menu
3609  * @extends Roo.bootstrap.Component
3610  * Bootstrap Menu class - container for MenuItems
3611  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3612  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3613  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3614  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3615   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3616   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3617  
3618  * @constructor
3619  * Create a new Menu
3620  * @param {Object} config The config object
3621  */
3622
3623
3624 Roo.bootstrap.Menu = function(config){
3625     
3626     if (config.type == 'treeview') {
3627         // normally menu's are drawn attached to the document to handle layering etc..
3628         // however treeview (used by the docs menu is drawn into the parent element)
3629         this.container_method = 'getChildContainer'; 
3630     }
3631     
3632     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3633     if (this.registerMenu && this.type != 'treeview')  {
3634         Roo.bootstrap.MenuMgr.register(this);
3635     }
3636     
3637     
3638     this.addEvents({
3639         /**
3640          * @event beforeshow
3641          * Fires before this menu is displayed (return false to block)
3642          * @param {Roo.menu.Menu} this
3643          */
3644         beforeshow : true,
3645         /**
3646          * @event beforehide
3647          * Fires before this menu is hidden (return false to block)
3648          * @param {Roo.menu.Menu} this
3649          */
3650         beforehide : true,
3651         /**
3652          * @event show
3653          * Fires after this menu is displayed
3654          * @param {Roo.menu.Menu} this
3655          */
3656         show : true,
3657         /**
3658          * @event hide
3659          * Fires after this menu is hidden
3660          * @param {Roo.menu.Menu} this
3661          */
3662         hide : true,
3663         /**
3664          * @event click
3665          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3666          * @param {Roo.menu.Menu} this
3667          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3668          * @param {Roo.EventObject} e
3669          */
3670         click : true,
3671         /**
3672          * @event mouseover
3673          * Fires when the mouse is hovering over this menu
3674          * @param {Roo.menu.Menu} this
3675          * @param {Roo.EventObject} e
3676          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3677          */
3678         mouseover : true,
3679         /**
3680          * @event mouseout
3681          * Fires when the mouse exits this menu
3682          * @param {Roo.menu.Menu} this
3683          * @param {Roo.EventObject} e
3684          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3685          */
3686         mouseout : true,
3687         /**
3688          * @event itemclick
3689          * Fires when a menu item contained in this menu is clicked
3690          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3691          * @param {Roo.EventObject} e
3692          */
3693         itemclick: true
3694     });
3695     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3696 };
3697
3698 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3699     
3700    /// html : false,
3701    
3702     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3703     type: false,
3704     /**
3705      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706      */
3707     registerMenu : true,
3708     
3709     menuItems :false, // stores the menu items..
3710     
3711     hidden:true,
3712         
3713     parentMenu : false,
3714     
3715     stopEvent : true,
3716     
3717     isLink : false,
3718     
3719     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720     
3721     hideTrigger : false,
3722     
3723     align : 'tl-bl?',
3724     
3725     
3726     getChildContainer : function() {
3727         return this.el;  
3728     },
3729     
3730     getAutoCreate : function(){
3731          
3732         //if (['right'].indexOf(this.align)!==-1) {
3733         //    cfg.cn[1].cls += ' pull-right'
3734         //}
3735          
3736         var cfg = {
3737             tag : 'ul',
3738             cls : 'dropdown-menu shadow' ,
3739             style : 'z-index:1000'
3740             
3741         };
3742         
3743         if (this.type === 'submenu') {
3744             cfg.cls = 'submenu active';
3745         }
3746         if (this.type === 'treeview') {
3747             cfg.cls = 'treeview-menu';
3748         }
3749         
3750         return cfg;
3751     },
3752     initEvents : function() {
3753         
3754        // Roo.log("ADD event");
3755        // Roo.log(this.triggerEl.dom);
3756         if (this.triggerEl) {
3757             
3758             this.triggerEl.on('click', this.onTriggerClick, this);
3759             
3760             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761             
3762             if (!this.hideTrigger) {
3763                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3764                     // dropdown toggle on the 'a' in BS4?
3765                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766                 } else {
3767                     this.triggerEl.addClass('dropdown-toggle');
3768                 }
3769             }
3770         }
3771         
3772         if (Roo.isTouch) {
3773             this.el.on('touchstart'  , this.onTouch, this);
3774         }
3775         this.el.on('click' , this.onClick, this);
3776
3777         this.el.on("mouseover", this.onMouseOver, this);
3778         this.el.on("mouseout", this.onMouseOut, this);
3779         
3780     },
3781     
3782     findTargetItem : function(e)
3783     {
3784         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3785         if(!t){
3786             return false;
3787         }
3788         //Roo.log(t);         Roo.log(t.id);
3789         if(t && t.id){
3790             //Roo.log(this.menuitems);
3791             return this.menuitems.get(t.id);
3792             
3793             //return this.items.get(t.menuItemId);
3794         }
3795         
3796         return false;
3797     },
3798     
3799     onTouch : function(e) 
3800     {
3801         Roo.log("menu.onTouch");
3802         //e.stopEvent(); this make the user popdown broken
3803         this.onClick(e);
3804     },
3805     
3806     onClick : function(e)
3807     {
3808         Roo.log("menu.onClick");
3809         
3810         var t = this.findTargetItem(e);
3811         if(!t || t.isContainer){
3812             return;
3813         }
3814         Roo.log(e);
3815         /*
3816         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3817             if(t == this.activeItem && t.shouldDeactivate(e)){
3818                 this.activeItem.deactivate();
3819                 delete this.activeItem;
3820                 return;
3821             }
3822             if(t.canActivate){
3823                 this.setActiveItem(t, true);
3824             }
3825             return;
3826             
3827             
3828         }
3829         */
3830        
3831         Roo.log('pass click event');
3832         
3833         t.onClick(e);
3834         
3835         this.fireEvent("click", this, t, e);
3836         
3837         var _this = this;
3838         
3839         if(!t.href.length || t.href == '#'){
3840             (function() { _this.hide(); }).defer(100);
3841         }
3842         
3843     },
3844     
3845     onMouseOver : function(e){
3846         var t  = this.findTargetItem(e);
3847         //Roo.log(t);
3848         //if(t){
3849         //    if(t.canActivate && !t.disabled){
3850         //        this.setActiveItem(t, true);
3851         //    }
3852         //}
3853         
3854         this.fireEvent("mouseover", this, e, t);
3855     },
3856     isVisible : function(){
3857         return !this.hidden;
3858     },
3859     onMouseOut : function(e){
3860         var t  = this.findTargetItem(e);
3861         
3862         //if(t ){
3863         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3864         //        this.activeItem.deactivate();
3865         //        delete this.activeItem;
3866         //    }
3867         //}
3868         this.fireEvent("mouseout", this, e, t);
3869     },
3870     
3871     
3872     /**
3873      * Displays this menu relative to another element
3874      * @param {String/HTMLElement/Roo.Element} element The element to align to
3875      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3876      * the element (defaults to this.defaultAlign)
3877      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878      */
3879     show : function(el, pos, parentMenu)
3880     {
3881         if (false === this.fireEvent("beforeshow", this)) {
3882             Roo.log("show canceled");
3883             return;
3884         }
3885         this.parentMenu = parentMenu;
3886         if(!this.el){
3887             this.render();
3888         }
3889         this.el.addClass('show'); // show otherwise we do not know how big we are..
3890          
3891         var xy = this.el.getAlignToXY(el, pos);
3892         
3893         // bl-tl << left align  below
3894         // tl-bl << left align 
3895         
3896         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3897             // if it goes to far to the right.. -> align left.
3898             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3899         }
3900         if(xy[0] < 0){
3901             // was left align - go right?
3902             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3903         }
3904         
3905         // goes down the bottom
3906         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907            xy[1]  < 0 ){
3908             var a = this.align.replace('?', '').split('-');
3909             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3910             
3911         }
3912         
3913         this.showAt(  xy , parentMenu, false);
3914     },
3915      /**
3916      * Displays this menu at a specific xy position
3917      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3918      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919      */
3920     showAt : function(xy, parentMenu, /* private: */_e){
3921         this.parentMenu = parentMenu;
3922         if(!this.el){
3923             this.render();
3924         }
3925         if(_e !== false){
3926             this.fireEvent("beforeshow", this);
3927             //xy = this.el.adjustForConstraints(xy);
3928         }
3929         
3930         //this.el.show();
3931         this.hideMenuItems();
3932         this.hidden = false;
3933         if (this.triggerEl) {
3934             this.triggerEl.addClass('open');
3935         }
3936         
3937         this.el.addClass('show');
3938         
3939         
3940         
3941         // reassign x when hitting right
3942         
3943         // reassign y when hitting bottom
3944         
3945         // but the list may align on trigger left or trigger top... should it be a properity?
3946         
3947         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3948             this.el.setXY(xy);
3949         }
3950         
3951         this.focus();
3952         this.fireEvent("show", this);
3953     },
3954     
3955     focus : function(){
3956         return;
3957         if(!this.hidden){
3958             this.doFocus.defer(50, this);
3959         }
3960     },
3961
3962     doFocus : function(){
3963         if(!this.hidden){
3964             this.focusEl.focus();
3965         }
3966     },
3967
3968     /**
3969      * Hides this menu and optionally all parent menus
3970      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971      */
3972     hide : function(deep)
3973     {
3974         if (false === this.fireEvent("beforehide", this)) {
3975             Roo.log("hide canceled");
3976             return;
3977         }
3978         this.hideMenuItems();
3979         if(this.el && this.isVisible()){
3980            
3981             if(this.activeItem){
3982                 this.activeItem.deactivate();
3983                 this.activeItem = null;
3984             }
3985             if (this.triggerEl) {
3986                 this.triggerEl.removeClass('open');
3987             }
3988             
3989             this.el.removeClass('show');
3990             this.hidden = true;
3991             this.fireEvent("hide", this);
3992         }
3993         if(deep === true && this.parentMenu){
3994             this.parentMenu.hide(true);
3995         }
3996     },
3997     
3998     onTriggerClick : function(e)
3999     {
4000         Roo.log('trigger click');
4001         
4002         var target = e.getTarget();
4003         
4004         Roo.log(target.nodeName.toLowerCase());
4005         
4006         if(target.nodeName.toLowerCase() === 'i'){
4007             e.preventDefault();
4008         }
4009         
4010     },
4011     
4012     onTriggerPress  : function(e)
4013     {
4014         Roo.log('trigger press');
4015         //Roo.log(e.getTarget());
4016        // Roo.log(this.triggerEl.dom);
4017        
4018         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4019         var pel = Roo.get(e.getTarget());
4020         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4021             Roo.log('is treeview or dropdown?');
4022             return;
4023         }
4024         
4025         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4026             return;
4027         }
4028         
4029         if (this.isVisible()) {
4030             Roo.log('hide');
4031             this.hide();
4032         } else {
4033             Roo.log('show');
4034             
4035             this.show(this.triggerEl, this.align, false);
4036         }
4037         
4038         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4039             e.stopEvent();
4040         }
4041         
4042     },
4043        
4044     
4045     hideMenuItems : function()
4046     {
4047         Roo.log("hide Menu Items");
4048         if (!this.el) { 
4049             return;
4050         }
4051         
4052         this.el.select('.open',true).each(function(aa) {
4053             
4054             aa.removeClass('open');
4055          
4056         });
4057     },
4058     addxtypeChild : function (tree, cntr) {
4059         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060           
4061         this.menuitems.add(comp);
4062         return comp;
4063
4064     },
4065     getEl : function()
4066     {
4067         Roo.log(this.el);
4068         return this.el;
4069     },
4070     
4071     clear : function()
4072     {
4073         this.getEl().dom.innerHTML = '';
4074         this.menuitems.clear();
4075     }
4076 });
4077
4078  
4079  /*
4080  * - LGPL
4081  *
4082  * menu item
4083  * 
4084  */
4085
4086
4087 /**
4088  * @class Roo.bootstrap.MenuItem
4089  * @extends Roo.bootstrap.Component
4090  * Bootstrap MenuItem class
4091  * @cfg {String} html the menu label
4092  * @cfg {String} href the link
4093  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4094  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4095  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4096  * @cfg {String} fa favicon to show on left of menu item.
4097  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4098  * 
4099  * 
4100  * @constructor
4101  * Create a new MenuItem
4102  * @param {Object} config The config object
4103  */
4104
4105
4106 Roo.bootstrap.MenuItem = function(config){
4107     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4108     this.addEvents({
4109         // raw events
4110         /**
4111          * @event click
4112          * The raw click event for the entire grid.
4113          * @param {Roo.bootstrap.MenuItem} this
4114          * @param {Roo.EventObject} e
4115          */
4116         "click" : true
4117     });
4118 };
4119
4120 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4121     
4122     href : false,
4123     html : false,
4124     preventDefault: false,
4125     isContainer : false,
4126     active : false,
4127     fa: false,
4128     
4129     getAutoCreate : function(){
4130         
4131         if(this.isContainer){
4132             return {
4133                 tag: 'li',
4134                 cls: 'dropdown-menu-item '
4135             };
4136         }
4137         var ctag = {
4138             tag: 'span',
4139             html: 'Link'
4140         };
4141         
4142         var anc = {
4143             tag : 'a',
4144             cls : 'dropdown-item',
4145             href : '#',
4146             cn : [  ]
4147         };
4148         
4149         if (this.fa !== false) {
4150             anc.cn.push({
4151                 tag : 'i',
4152                 cls : 'fa fa-' + this.fa
4153             });
4154         }
4155         
4156         anc.cn.push(ctag);
4157         
4158         
4159         var cfg= {
4160             tag: 'li',
4161             cls: 'dropdown-menu-item',
4162             cn: [ anc ]
4163         };
4164         if (this.parent().type == 'treeview') {
4165             cfg.cls = 'treeview-menu';
4166         }
4167         if (this.active) {
4168             cfg.cls += ' active';
4169         }
4170         
4171         
4172         
4173         anc.href = this.href || cfg.cn[0].href ;
4174         ctag.html = this.html || cfg.cn[0].html ;
4175         return cfg;
4176     },
4177     
4178     initEvents: function()
4179     {
4180         if (this.parent().type == 'treeview') {
4181             this.el.select('a').on('click', this.onClick, this);
4182         }
4183         
4184         if (this.menu) {
4185             this.menu.parentType = this.xtype;
4186             this.menu.triggerEl = this.el;
4187             this.menu = this.addxtype(Roo.apply({}, this.menu));
4188         }
4189         
4190     },
4191     onClick : function(e)
4192     {
4193         Roo.log('item on click ');
4194         
4195         if(this.preventDefault){
4196             e.preventDefault();
4197         }
4198         //this.parent().hideMenuItems();
4199         
4200         this.fireEvent('click', this, e);
4201     },
4202     getEl : function()
4203     {
4204         return this.el;
4205     } 
4206 });
4207
4208  
4209
4210  /*
4211  * - LGPL
4212  *
4213  * menu separator
4214  * 
4215  */
4216
4217
4218 /**
4219  * @class Roo.bootstrap.MenuSeparator
4220  * @extends Roo.bootstrap.Component
4221  * Bootstrap MenuSeparator class
4222  * 
4223  * @constructor
4224  * Create a new MenuItem
4225  * @param {Object} config The config object
4226  */
4227
4228
4229 Roo.bootstrap.MenuSeparator = function(config){
4230     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4231 };
4232
4233 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4234     
4235     getAutoCreate : function(){
4236         var cfg = {
4237             cls: 'divider',
4238             tag : 'li'
4239         };
4240         
4241         return cfg;
4242     }
4243    
4244 });
4245
4246  
4247
4248  
4249 /*
4250 * Licence: LGPL
4251 */
4252
4253 /**
4254  * @class Roo.bootstrap.Modal
4255  * @extends Roo.bootstrap.Component
4256  * Bootstrap Modal class
4257  * @cfg {String} title Title of dialog
4258  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4259  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4260  * @cfg {Boolean} specificTitle default false
4261  * @cfg {Array} buttons Array of buttons or standard button set..
4262  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4263  * @cfg {Boolean} animate default true
4264  * @cfg {Boolean} allow_close default true
4265  * @cfg {Boolean} fitwindow default false
4266  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4267  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4268  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4269  * @cfg {String} size (sm|lg|xl) default empty
4270  * @cfg {Number} max_width set the max width of modal
4271  * @cfg {Boolean} editableTitle can the title be edited
4272
4273  *
4274  *
4275  * @constructor
4276  * Create a new Modal Dialog
4277  * @param {Object} config The config object
4278  */
4279
4280 Roo.bootstrap.Modal = function(config){
4281     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4282     this.addEvents({
4283         // raw events
4284         /**
4285          * @event btnclick
4286          * The raw btnclick event for the button
4287          * @param {Roo.EventObject} e
4288          */
4289         "btnclick" : true,
4290         /**
4291          * @event resize
4292          * Fire when dialog resize
4293          * @param {Roo.bootstrap.Modal} this
4294          * @param {Roo.EventObject} e
4295          */
4296         "resize" : true,
4297         /**
4298          * @event titlechanged
4299          * Fire when the editable title has been changed
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} value
4302          */
4303         "titlechanged" : true 
4304         
4305     });
4306     this.buttons = this.buttons || [];
4307
4308     if (this.tmpl) {
4309         this.tmpl = Roo.factory(this.tmpl);
4310     }
4311
4312 };
4313
4314 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4315
4316     title : 'test dialog',
4317
4318     buttons : false,
4319
4320     // set on load...
4321
4322     html: false,
4323
4324     tmp: false,
4325
4326     specificTitle: false,
4327
4328     buttonPosition: 'right',
4329
4330     allow_close : true,
4331
4332     animate : true,
4333
4334     fitwindow: false,
4335     
4336      // private
4337     dialogEl: false,
4338     bodyEl:  false,
4339     footerEl:  false,
4340     titleEl:  false,
4341     closeEl:  false,
4342
4343     size: '',
4344     
4345     max_width: 0,
4346     
4347     max_height: 0,
4348     
4349     fit_content: false,
4350     editableTitle  : false,
4351
4352     onRender : function(ct, position)
4353     {
4354         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4355
4356         if(!this.el){
4357             var cfg = Roo.apply({},  this.getAutoCreate());
4358             cfg.id = Roo.id();
4359             //if(!cfg.name){
4360             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361             //}
4362             //if (!cfg.name.length) {
4363             //    delete cfg.name;
4364            // }
4365             if (this.cls) {
4366                 cfg.cls += ' ' + this.cls;
4367             }
4368             if (this.style) {
4369                 cfg.style = this.style;
4370             }
4371             this.el = Roo.get(document.body).createChild(cfg, position);
4372         }
4373         //var type = this.el.dom.type;
4374
4375
4376         if(this.tabIndex !== undefined){
4377             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4378         }
4379
4380         this.dialogEl = this.el.select('.modal-dialog',true).first();
4381         this.bodyEl = this.el.select('.modal-body',true).first();
4382         this.closeEl = this.el.select('.modal-header .close', true).first();
4383         this.headerEl = this.el.select('.modal-header',true).first();
4384         this.titleEl = this.el.select('.modal-title',true).first();
4385         this.footerEl = this.el.select('.modal-footer',true).first();
4386
4387         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388         
4389         //this.el.addClass("x-dlg-modal");
4390
4391         if (this.buttons.length) {
4392             Roo.each(this.buttons, function(bb) {
4393                 var b = Roo.apply({}, bb);
4394                 b.xns = b.xns || Roo.bootstrap;
4395                 b.xtype = b.xtype || 'Button';
4396                 if (typeof(b.listeners) == 'undefined') {
4397                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4398                 }
4399
4400                 var btn = Roo.factory(b);
4401
4402                 btn.render(this.getButtonContainer());
4403
4404             },this);
4405         }
4406         // render the children.
4407         var nitems = [];
4408
4409         if(typeof(this.items) != 'undefined'){
4410             var items = this.items;
4411             delete this.items;
4412
4413             for(var i =0;i < items.length;i++) {
4414                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4415             }
4416         }
4417
4418         this.items = nitems;
4419
4420         // where are these used - they used to be body/close/footer
4421
4422
4423         this.initEvents();
4424         //this.el.addClass([this.fieldClass, this.cls]);
4425
4426     },
4427
4428     getAutoCreate : function()
4429     {
4430         // we will default to modal-body-overflow - might need to remove or make optional later.
4431         var bdy = {
4432                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4433                 html : this.html || ''
4434         };
4435
4436         var title = {
4437             tag: 'h5',
4438             cls : 'modal-title',
4439             html : this.title
4440         };
4441
4442         if(this.specificTitle){ // WTF is this?
4443             title = this.title;
4444         }
4445
4446         var header = [];
4447         if (this.allow_close && Roo.bootstrap.version == 3) {
4448             header.push({
4449                 tag: 'button',
4450                 cls : 'close',
4451                 html : '&times'
4452             });
4453         }
4454
4455         header.push(title);
4456
4457         if (this.editableTitle) {
4458             header.push({
4459                 cls: 'form-control roo-editable-title d-none',
4460                 tag: 'input',
4461                 type: 'text'
4462             });
4463         }
4464         
4465         if (this.allow_close && Roo.bootstrap.version == 4) {
4466             header.push({
4467                 tag: 'button',
4468                 cls : 'close',
4469                 html : '&times'
4470             });
4471         }
4472         
4473         var size = '';
4474
4475         if(this.size.length){
4476             size = 'modal-' + this.size;
4477         }
4478         
4479         var footer = Roo.bootstrap.version == 3 ?
4480             {
4481                 cls : 'modal-footer',
4482                 cn : [
4483                     {
4484                         tag: 'div',
4485                         cls: 'btn-' + this.buttonPosition
4486                     }
4487                 ]
4488
4489             } :
4490             {  // BS4 uses mr-auto on left buttons....
4491                 cls : 'modal-footer'
4492             };
4493
4494             
4495
4496         
4497         
4498         var modal = {
4499             cls: "modal",
4500              cn : [
4501                 {
4502                     cls: "modal-dialog " + size,
4503                     cn : [
4504                         {
4505                             cls : "modal-content",
4506                             cn : [
4507                                 {
4508                                     cls : 'modal-header',
4509                                     cn : header
4510                                 },
4511                                 bdy,
4512                                 footer
4513                             ]
4514
4515                         }
4516                     ]
4517
4518                 }
4519             ]
4520         };
4521
4522         if(this.animate){
4523             modal.cls += ' fade';
4524         }
4525
4526         return modal;
4527
4528     },
4529     getChildContainer : function() {
4530
4531          return this.bodyEl;
4532
4533     },
4534     getButtonContainer : function() {
4535         
4536          return Roo.bootstrap.version == 4 ?
4537             this.el.select('.modal-footer',true).first()
4538             : this.el.select('.modal-footer div',true).first();
4539
4540     },
4541     initEvents : function()
4542     {
4543         if (this.allow_close) {
4544             this.closeEl.on('click', this.hide, this);
4545         }
4546         Roo.EventManager.onWindowResize(this.resize, this, true);
4547         if (this.editableTitle) {
4548             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4549             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4550             this.headerEditEl.on('keyup', function(e) {
4551                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4552                         this.toggleHeaderInput(false)
4553                     }
4554                 }, this);
4555             this.headerEditEl.on('blur', function(e) {
4556                 this.toggleHeaderInput(false)
4557             },this);
4558         }
4559
4560     },
4561   
4562
4563     resize : function()
4564     {
4565         this.maskEl.setSize(
4566             Roo.lib.Dom.getViewWidth(true),
4567             Roo.lib.Dom.getViewHeight(true)
4568         );
4569         
4570         if (this.fitwindow) {
4571             
4572            this.dialogEl.setStyle( { 'max-width' : '100%' });
4573             this.setSize(
4574                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4575                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4576             );
4577             return;
4578         }
4579         
4580         if(this.max_width !== 0) {
4581             
4582             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4583             
4584             if(this.height) {
4585                 this.setSize(w, this.height);
4586                 return;
4587             }
4588             
4589             if(this.max_height) {
4590                 this.setSize(w,Math.min(
4591                     this.max_height,
4592                     Roo.lib.Dom.getViewportHeight(true) - 60
4593                 ));
4594                 
4595                 return;
4596             }
4597             
4598             if(!this.fit_content) {
4599                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4600                 return;
4601             }
4602             
4603             this.setSize(w, Math.min(
4604                 60 +
4605                 this.headerEl.getHeight() + 
4606                 this.footerEl.getHeight() + 
4607                 this.getChildHeight(this.bodyEl.dom.childNodes),
4608                 Roo.lib.Dom.getViewportHeight(true) - 60)
4609             );
4610         }
4611         
4612     },
4613
4614     setSize : function(w,h)
4615     {
4616         if (!w && !h) {
4617             return;
4618         }
4619         
4620         this.resizeTo(w,h);
4621     },
4622
4623     show : function() {
4624
4625         if (!this.rendered) {
4626             this.render();
4627         }
4628         this.toggleHeaderInput(false);
4629         //this.el.setStyle('display', 'block');
4630         this.el.removeClass('hideing');
4631         this.el.dom.style.display='block';
4632         
4633         Roo.get(document.body).addClass('modal-open');
4634  
4635         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4636             
4637             (function(){
4638                 this.el.addClass('show');
4639                 this.el.addClass('in');
4640             }).defer(50, this);
4641         }else{
4642             this.el.addClass('show');
4643             this.el.addClass('in');
4644         }
4645
4646         // not sure how we can show data in here..
4647         //if (this.tmpl) {
4648         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4649         //}
4650
4651         Roo.get(document.body).addClass("x-body-masked");
4652         
4653         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4654         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4655         this.maskEl.dom.style.display = 'block';
4656         this.maskEl.addClass('show');
4657         
4658         
4659         this.resize();
4660         
4661         this.fireEvent('show', this);
4662
4663         // set zindex here - otherwise it appears to be ignored...
4664         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4665
4666         (function () {
4667             this.items.forEach( function(e) {
4668                 e.layout ? e.layout() : false;
4669
4670             });
4671         }).defer(100,this);
4672
4673     },
4674     hide : function()
4675     {
4676         if(this.fireEvent("beforehide", this) !== false){
4677             
4678             this.maskEl.removeClass('show');
4679             
4680             this.maskEl.dom.style.display = '';
4681             Roo.get(document.body).removeClass("x-body-masked");
4682             this.el.removeClass('in');
4683             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684
4685             if(this.animate){ // why
4686                 this.el.addClass('hideing');
4687                 this.el.removeClass('show');
4688                 (function(){
4689                     if (!this.el.hasClass('hideing')) {
4690                         return; // it's been shown again...
4691                     }
4692                     
4693                     this.el.dom.style.display='';
4694
4695                     Roo.get(document.body).removeClass('modal-open');
4696                     this.el.removeClass('hideing');
4697                 }).defer(150,this);
4698                 
4699             }else{
4700                 this.el.removeClass('show');
4701                 this.el.dom.style.display='';
4702                 Roo.get(document.body).removeClass('modal-open');
4703
4704             }
4705             this.fireEvent('hide', this);
4706         }
4707     },
4708     isVisible : function()
4709     {
4710         
4711         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4712         
4713     },
4714
4715     addButton : function(str, cb)
4716     {
4717
4718
4719         var b = Roo.apply({}, { html : str } );
4720         b.xns = b.xns || Roo.bootstrap;
4721         b.xtype = b.xtype || 'Button';
4722         if (typeof(b.listeners) == 'undefined') {
4723             b.listeners = { click : cb.createDelegate(this)  };
4724         }
4725
4726         var btn = Roo.factory(b);
4727
4728         btn.render(this.getButtonContainer());
4729
4730         return btn;
4731
4732     },
4733
4734     setDefaultButton : function(btn)
4735     {
4736         //this.el.select('.modal-footer').()
4737     },
4738
4739     resizeTo: function(w,h)
4740     {
4741         this.dialogEl.setWidth(w);
4742         
4743         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4744
4745         this.bodyEl.setHeight(h - diff);
4746         
4747         this.fireEvent('resize', this);
4748     },
4749     
4750     setContentSize  : function(w, h)
4751     {
4752
4753     },
4754     onButtonClick: function(btn,e)
4755     {
4756         //Roo.log([a,b,c]);
4757         this.fireEvent('btnclick', btn.name, e);
4758     },
4759      /**
4760      * Set the title of the Dialog
4761      * @param {String} str new Title
4762      */
4763     setTitle: function(str) {
4764         this.titleEl.dom.innerHTML = str;
4765         this.title = str;
4766     },
4767     /**
4768      * Set the body of the Dialog
4769      * @param {String} str new Title
4770      */
4771     setBody: function(str) {
4772         this.bodyEl.dom.innerHTML = str;
4773     },
4774     /**
4775      * Set the body of the Dialog using the template
4776      * @param {Obj} data - apply this data to the template and replace the body contents.
4777      */
4778     applyBody: function(obj)
4779     {
4780         if (!this.tmpl) {
4781             Roo.log("Error - using apply Body without a template");
4782             //code
4783         }
4784         this.tmpl.overwrite(this.bodyEl, obj);
4785     },
4786     
4787     getChildHeight : function(child_nodes)
4788     {
4789         if(
4790             !child_nodes ||
4791             child_nodes.length == 0
4792         ) {
4793             return 0;
4794         }
4795         
4796         var child_height = 0;
4797         
4798         for(var i = 0; i < child_nodes.length; i++) {
4799             
4800             /*
4801             * for modal with tabs...
4802             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803                 
4804                 var layout_childs = child_nodes[i].childNodes;
4805                 
4806                 for(var j = 0; j < layout_childs.length; j++) {
4807                     
4808                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809                         
4810                         var layout_body_childs = layout_childs[j].childNodes;
4811                         
4812                         for(var k = 0; k < layout_body_childs.length; k++) {
4813                             
4814                             if(layout_body_childs[k].classList.contains('navbar')) {
4815                                 child_height += layout_body_childs[k].offsetHeight;
4816                                 continue;
4817                             }
4818                             
4819                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820                                 
4821                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822                                 
4823                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824                                     
4825                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4826                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4827                                         continue;
4828                                     }
4829                                     
4830                                 }
4831                                 
4832                             }
4833                             
4834                         }
4835                     }
4836                 }
4837                 continue;
4838             }
4839             */
4840             
4841             child_height += child_nodes[i].offsetHeight;
4842             // Roo.log(child_nodes[i].offsetHeight);
4843         }
4844         
4845         return child_height;
4846     },
4847     toggleHeaderInput : function(is_edit)
4848     {
4849         if (!this.editableTitle) {
4850             return; // not editable.
4851         }
4852         if (is_edit && this.is_header_editing) {
4853             return; // already editing..
4854         }
4855         if (is_edit) {
4856     
4857             this.headerEditEl.dom.value = this.title;
4858             this.headerEditEl.removeClass('d-none');
4859             this.headerEditEl.dom.focus();
4860             this.titleEl.addClass('d-none');
4861             
4862             this.is_header_editing = true;
4863             return
4864         }
4865         // flip back to not editing.
4866         this.title = this.headerEditEl.dom.value;
4867         this.headerEditEl.addClass('d-none');
4868         this.titleEl.removeClass('d-none');
4869         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4870         this.is_header_editing = false;
4871         this.fireEvent('titlechanged', this, this.title);
4872     
4873             
4874         
4875     }
4876
4877 });
4878
4879
4880 Roo.apply(Roo.bootstrap.Modal,  {
4881     /**
4882          * Button config that displays a single OK button
4883          * @type Object
4884          */
4885         OK :  [{
4886             name : 'ok',
4887             weight : 'primary',
4888             html : 'OK'
4889         }],
4890         /**
4891          * Button config that displays Yes and No buttons
4892          * @type Object
4893          */
4894         YESNO : [
4895             {
4896                 name  : 'no',
4897                 html : 'No'
4898             },
4899             {
4900                 name  :'yes',
4901                 weight : 'primary',
4902                 html : 'Yes'
4903             }
4904         ],
4905
4906         /**
4907          * Button config that displays OK and Cancel buttons
4908          * @type Object
4909          */
4910         OKCANCEL : [
4911             {
4912                name : 'cancel',
4913                 html : 'Cancel'
4914             },
4915             {
4916                 name : 'ok',
4917                 weight : 'primary',
4918                 html : 'OK'
4919             }
4920         ],
4921         /**
4922          * Button config that displays Yes, No and Cancel buttons
4923          * @type Object
4924          */
4925         YESNOCANCEL : [
4926             {
4927                 name : 'yes',
4928                 weight : 'primary',
4929                 html : 'Yes'
4930             },
4931             {
4932                 name : 'no',
4933                 html : 'No'
4934             },
4935             {
4936                 name : 'cancel',
4937                 html : 'Cancel'
4938             }
4939         ],
4940         
4941         zIndex : 10001
4942 });
4943
4944 /*
4945  * - LGPL
4946  *
4947  * messagebox - can be used as a replace
4948  * 
4949  */
4950 /**
4951  * @class Roo.MessageBox
4952  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4953  * Example usage:
4954  *<pre><code>
4955 // Basic alert:
4956 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957
4958 // Prompt for user data:
4959 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960     if (btn == 'ok'){
4961         // process text value...
4962     }
4963 });
4964
4965 // Show a dialog using config options:
4966 Roo.Msg.show({
4967    title:'Save Changes?',
4968    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4969    buttons: Roo.Msg.YESNOCANCEL,
4970    fn: processResult,
4971    animEl: 'elId'
4972 });
4973 </code></pre>
4974  * @singleton
4975  */
4976 Roo.bootstrap.MessageBox = function(){
4977     var dlg, opt, mask, waitTimer;
4978     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4979     var buttons, activeTextEl, bwidth;
4980
4981     
4982     // private
4983     var handleButton = function(button){
4984         dlg.hide();
4985         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4986     };
4987
4988     // private
4989     var handleHide = function(){
4990         if(opt && opt.cls){
4991             dlg.el.removeClass(opt.cls);
4992         }
4993         //if(waitTimer){
4994         //    Roo.TaskMgr.stop(waitTimer);
4995         //    waitTimer = null;
4996         //}
4997     };
4998
4999     // private
5000     var updateButtons = function(b){
5001         var width = 0;
5002         if(!b){
5003             buttons["ok"].hide();
5004             buttons["cancel"].hide();
5005             buttons["yes"].hide();
5006             buttons["no"].hide();
5007             dlg.footerEl.hide();
5008             
5009             return width;
5010         }
5011         dlg.footerEl.show();
5012         for(var k in buttons){
5013             if(typeof buttons[k] != "function"){
5014                 if(b[k]){
5015                     buttons[k].show();
5016                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5017                     width += buttons[k].el.getWidth()+15;
5018                 }else{
5019                     buttons[k].hide();
5020                 }
5021             }
5022         }
5023         return width;
5024     };
5025
5026     // private
5027     var handleEsc = function(d, k, e){
5028         if(opt && opt.closable !== false){
5029             dlg.hide();
5030         }
5031         if(e){
5032             e.stopEvent();
5033         }
5034     };
5035
5036     return {
5037         /**
5038          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5039          * @return {Roo.BasicDialog} The BasicDialog element
5040          */
5041         getDialog : function(){
5042            if(!dlg){
5043                 dlg = new Roo.bootstrap.Modal( {
5044                     //draggable: true,
5045                     //resizable:false,
5046                     //constraintoviewport:false,
5047                     //fixedcenter:true,
5048                     //collapsible : false,
5049                     //shim:true,
5050                     //modal: true,
5051                 //    width: 'auto',
5052                   //  height:100,
5053                     //buttonAlign:"center",
5054                     closeClick : function(){
5055                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5056                             handleButton("no");
5057                         }else{
5058                             handleButton("cancel");
5059                         }
5060                     }
5061                 });
5062                 dlg.render();
5063                 dlg.on("hide", handleHide);
5064                 mask = dlg.mask;
5065                 //dlg.addKeyListener(27, handleEsc);
5066                 buttons = {};
5067                 this.buttons = buttons;
5068                 var bt = this.buttonText;
5069                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5070                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5071                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5072                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073                 //Roo.log(buttons);
5074                 bodyEl = dlg.bodyEl.createChild({
5075
5076                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5077                         '<textarea class="roo-mb-textarea"></textarea>' +
5078                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5079                 });
5080                 msgEl = bodyEl.dom.firstChild;
5081                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5082                 textboxEl.enableDisplayMode();
5083                 textboxEl.addKeyListener([10,13], function(){
5084                     if(dlg.isVisible() && opt && opt.buttons){
5085                         if(opt.buttons.ok){
5086                             handleButton("ok");
5087                         }else if(opt.buttons.yes){
5088                             handleButton("yes");
5089                         }
5090                     }
5091                 });
5092                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5093                 textareaEl.enableDisplayMode();
5094                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5095                 progressEl.enableDisplayMode();
5096                 
5097                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5098                 var pf = progressEl.dom.firstChild;
5099                 if (pf) {
5100                     pp = Roo.get(pf.firstChild);
5101                     pp.setHeight(pf.offsetHeight);
5102                 }
5103                 
5104             }
5105             return dlg;
5106         },
5107
5108         /**
5109          * Updates the message box body text
5110          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5111          * the XHTML-compliant non-breaking space character '&amp;#160;')
5112          * @return {Roo.MessageBox} This message box
5113          */
5114         updateText : function(text)
5115         {
5116             if(!dlg.isVisible() && !opt.width){
5117                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5118                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119             }
5120             msgEl.innerHTML = text || '&#160;';
5121       
5122             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5123             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124             var w = Math.max(
5125                     Math.min(opt.width || cw , this.maxWidth), 
5126                     Math.max(opt.minWidth || this.minWidth, bwidth)
5127             );
5128             if(opt.prompt){
5129                 activeTextEl.setWidth(w);
5130             }
5131             if(dlg.isVisible()){
5132                 dlg.fixedcenter = false;
5133             }
5134             // to big, make it scroll. = But as usual stupid IE does not support
5135             // !important..
5136             
5137             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5138                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5139                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140             } else {
5141                 bodyEl.dom.style.height = '';
5142                 bodyEl.dom.style.overflowY = '';
5143             }
5144             if (cw > w) {
5145                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146             } else {
5147                 bodyEl.dom.style.overflowX = '';
5148             }
5149             
5150             dlg.setContentSize(w, bodyEl.getHeight());
5151             if(dlg.isVisible()){
5152                 dlg.fixedcenter = true;
5153             }
5154             return this;
5155         },
5156
5157         /**
5158          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5159          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5160          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5161          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5162          * @return {Roo.MessageBox} This message box
5163          */
5164         updateProgress : function(value, text){
5165             if(text){
5166                 this.updateText(text);
5167             }
5168             
5169             if (pp) { // weird bug on my firefox - for some reason this is not defined
5170                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5171                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5172             }
5173             return this;
5174         },        
5175
5176         /**
5177          * Returns true if the message box is currently displayed
5178          * @return {Boolean} True if the message box is visible, else false
5179          */
5180         isVisible : function(){
5181             return dlg && dlg.isVisible();  
5182         },
5183
5184         /**
5185          * Hides the message box if it is displayed
5186          */
5187         hide : function(){
5188             if(this.isVisible()){
5189                 dlg.hide();
5190             }  
5191         },
5192
5193         /**
5194          * Displays a new message box, or reinitializes an existing message box, based on the config options
5195          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5196          * The following config object properties are supported:
5197          * <pre>
5198 Property    Type             Description
5199 ----------  ---------------  ------------------------------------------------------------------------------------
5200 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5201                                    closes (defaults to undefined)
5202 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5203                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5204 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5205                                    progress and wait dialogs will ignore this property and always hide the
5206                                    close button as they can only be closed programmatically.
5207 cls               String           A custom CSS class to apply to the message box element
5208 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5209                                    displayed (defaults to 75)
5210 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5211                                    function will be btn (the name of the button that was clicked, if applicable,
5212                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5213                                    Progress and wait dialogs will ignore this option since they do not respond to
5214                                    user actions and can only be closed programmatically, so any required function
5215                                    should be called by the same code after it closes the dialog.
5216 icon              String           A CSS class that provides a background image to be used as an icon for
5217                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5218 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5219 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5220 modal             Boolean          False to allow user interaction with the page while the message box is
5221                                    displayed (defaults to true)
5222 msg               String           A string that will replace the existing message box body text (defaults
5223                                    to the XHTML-compliant non-breaking space character '&#160;')
5224 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5225 progress          Boolean          True to display a progress bar (defaults to false)
5226 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5227 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5228 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5229 title             String           The title text
5230 value             String           The string value to set into the active textbox element if displayed
5231 wait              Boolean          True to display a progress bar (defaults to false)
5232 width             Number           The width of the dialog in pixels
5233 </pre>
5234          *
5235          * Example usage:
5236          * <pre><code>
5237 Roo.Msg.show({
5238    title: 'Address',
5239    msg: 'Please enter your address:',
5240    width: 300,
5241    buttons: Roo.MessageBox.OKCANCEL,
5242    multiline: true,
5243    fn: saveAddress,
5244    animEl: 'addAddressBtn'
5245 });
5246 </code></pre>
5247          * @param {Object} config Configuration options
5248          * @return {Roo.MessageBox} This message box
5249          */
5250         show : function(options)
5251         {
5252             
5253             // this causes nightmares if you show one dialog after another
5254             // especially on callbacks..
5255              
5256             if(this.isVisible()){
5257                 
5258                 this.hide();
5259                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5260                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5261                 Roo.log("New Dialog Message:" +  options.msg )
5262                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5263                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5264                 
5265             }
5266             var d = this.getDialog();
5267             opt = options;
5268             d.setTitle(opt.title || "&#160;");
5269             d.closeEl.setDisplayed(opt.closable !== false);
5270             activeTextEl = textboxEl;
5271             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5272             if(opt.prompt){
5273                 if(opt.multiline){
5274                     textboxEl.hide();
5275                     textareaEl.show();
5276                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5277                         opt.multiline : this.defaultTextHeight);
5278                     activeTextEl = textareaEl;
5279                 }else{
5280                     textboxEl.show();
5281                     textareaEl.hide();
5282                 }
5283             }else{
5284                 textboxEl.hide();
5285                 textareaEl.hide();
5286             }
5287             progressEl.setDisplayed(opt.progress === true);
5288             if (opt.progress) {
5289                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290             }
5291             this.updateProgress(0);
5292             activeTextEl.dom.value = opt.value || "";
5293             if(opt.prompt){
5294                 dlg.setDefaultButton(activeTextEl);
5295             }else{
5296                 var bs = opt.buttons;
5297                 var db = null;
5298                 if(bs && bs.ok){
5299                     db = buttons["ok"];
5300                 }else if(bs && bs.yes){
5301                     db = buttons["yes"];
5302                 }
5303                 dlg.setDefaultButton(db);
5304             }
5305             bwidth = updateButtons(opt.buttons);
5306             this.updateText(opt.msg);
5307             if(opt.cls){
5308                 d.el.addClass(opt.cls);
5309             }
5310             d.proxyDrag = opt.proxyDrag === true;
5311             d.modal = opt.modal !== false;
5312             d.mask = opt.modal !== false ? mask : false;
5313             if(!d.isVisible()){
5314                 // force it to the end of the z-index stack so it gets a cursor in FF
5315                 document.body.appendChild(dlg.el.dom);
5316                 d.animateTarget = null;
5317                 d.show(options.animEl);
5318             }
5319             return this;
5320         },
5321
5322         /**
5323          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5324          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5325          * and closing the message box when the process is complete.
5326          * @param {String} title The title bar text
5327          * @param {String} msg The message box body text
5328          * @return {Roo.MessageBox} This message box
5329          */
5330         progress : function(title, msg){
5331             this.show({
5332                 title : title,
5333                 msg : msg,
5334                 buttons: false,
5335                 progress:true,
5336                 closable:false,
5337                 minWidth: this.minProgressWidth,
5338                 modal : true
5339             });
5340             return this;
5341         },
5342
5343         /**
5344          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5345          * If a callback function is passed it will be called after the user clicks the button, and the
5346          * id of the button that was clicked will be passed as the only parameter to the callback
5347          * (could also be the top-right close button).
5348          * @param {String} title The title bar text
5349          * @param {String} msg The message box body text
5350          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5351          * @param {Object} scope (optional) The scope of the callback function
5352          * @return {Roo.MessageBox} This message box
5353          */
5354         alert : function(title, msg, fn, scope)
5355         {
5356             this.show({
5357                 title : title,
5358                 msg : msg,
5359                 buttons: this.OK,
5360                 fn: fn,
5361                 closable : false,
5362                 scope : scope,
5363                 modal : true
5364             });
5365             return this;
5366         },
5367
5368         /**
5369          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5370          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5371          * You are responsible for closing the message box when the process is complete.
5372          * @param {String} msg The message box body text
5373          * @param {String} title (optional) The title bar text
5374          * @return {Roo.MessageBox} This message box
5375          */
5376         wait : function(msg, title){
5377             this.show({
5378                 title : title,
5379                 msg : msg,
5380                 buttons: false,
5381                 closable:false,
5382                 progress:true,
5383                 modal:true,
5384                 width:300,
5385                 wait:true
5386             });
5387             waitTimer = Roo.TaskMgr.start({
5388                 run: function(i){
5389                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5390                 },
5391                 interval: 1000
5392             });
5393             return this;
5394         },
5395
5396         /**
5397          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5398          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5399          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5400          * @param {String} title The title bar text
5401          * @param {String} msg The message box body text
5402          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5403          * @param {Object} scope (optional) The scope of the callback function
5404          * @return {Roo.MessageBox} This message box
5405          */
5406         confirm : function(title, msg, fn, scope){
5407             this.show({
5408                 title : title,
5409                 msg : msg,
5410                 buttons: this.YESNO,
5411                 fn: fn,
5412                 scope : scope,
5413                 modal : true
5414             });
5415             return this;
5416         },
5417
5418         /**
5419          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5420          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5421          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5422          * (could also be the top-right close button) and the text that was entered will be passed as the two
5423          * parameters to the callback.
5424          * @param {String} title The title bar text
5425          * @param {String} msg The message box body text
5426          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5427          * @param {Object} scope (optional) The scope of the callback function
5428          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5429          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5430          * @return {Roo.MessageBox} This message box
5431          */
5432         prompt : function(title, msg, fn, scope, multiline){
5433             this.show({
5434                 title : title,
5435                 msg : msg,
5436                 buttons: this.OKCANCEL,
5437                 fn: fn,
5438                 minWidth:250,
5439                 scope : scope,
5440                 prompt:true,
5441                 multiline: multiline,
5442                 modal : true
5443             });
5444             return this;
5445         },
5446
5447         /**
5448          * Button config that displays a single OK button
5449          * @type Object
5450          */
5451         OK : {ok:true},
5452         /**
5453          * Button config that displays Yes and No buttons
5454          * @type Object
5455          */
5456         YESNO : {yes:true, no:true},
5457         /**
5458          * Button config that displays OK and Cancel buttons
5459          * @type Object
5460          */
5461         OKCANCEL : {ok:true, cancel:true},
5462         /**
5463          * Button config that displays Yes, No and Cancel buttons
5464          * @type Object
5465          */
5466         YESNOCANCEL : {yes:true, no:true, cancel:true},
5467
5468         /**
5469          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5470          * @type Number
5471          */
5472         defaultTextHeight : 75,
5473         /**
5474          * The maximum width in pixels of the message box (defaults to 600)
5475          * @type Number
5476          */
5477         maxWidth : 600,
5478         /**
5479          * The minimum width in pixels of the message box (defaults to 100)
5480          * @type Number
5481          */
5482         minWidth : 100,
5483         /**
5484          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5485          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5486          * @type Number
5487          */
5488         minProgressWidth : 250,
5489         /**
5490          * An object containing the default button text strings that can be overriden for localized language support.
5491          * Supported properties are: ok, cancel, yes and no.
5492          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5493          * @type Object
5494          */
5495         buttonText : {
5496             ok : "OK",
5497             cancel : "Cancel",
5498             yes : "Yes",
5499             no : "No"
5500         }
5501     };
5502 }();
5503
5504 /**
5505  * Shorthand for {@link Roo.MessageBox}
5506  */
5507 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5508 Roo.Msg = Roo.Msg || Roo.MessageBox;
5509 /*
5510  * - LGPL
5511  *
5512  * navbar
5513  * 
5514  */
5515
5516 /**
5517  * @class Roo.bootstrap.Navbar
5518  * @extends Roo.bootstrap.Component
5519  * Bootstrap Navbar class
5520
5521  * @constructor
5522  * Create a new Navbar
5523  * @param {Object} config The config object
5524  */
5525
5526
5527 Roo.bootstrap.Navbar = function(config){
5528     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5529     this.addEvents({
5530         // raw events
5531         /**
5532          * @event beforetoggle
5533          * Fire before toggle the menu
5534          * @param {Roo.EventObject} e
5535          */
5536         "beforetoggle" : true
5537     });
5538 };
5539
5540 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5541     
5542     
5543    
5544     // private
5545     navItems : false,
5546     loadMask : false,
5547     
5548     
5549     getAutoCreate : function(){
5550         
5551         
5552         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5553         
5554     },
5555     
5556     initEvents :function ()
5557     {
5558         //Roo.log(this.el.select('.navbar-toggle',true));
5559         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5560         
5561         var mark = {
5562             tag: "div",
5563             cls:"x-dlg-mask"
5564         };
5565         
5566         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567         
5568         var size = this.el.getSize();
5569         this.maskEl.setSize(size.width, size.height);
5570         this.maskEl.enableDisplayMode("block");
5571         this.maskEl.hide();
5572         
5573         if(this.loadMask){
5574             this.maskEl.show();
5575         }
5576     },
5577     
5578     
5579     getChildContainer : function()
5580     {
5581         if (this.el && this.el.select('.collapse').getCount()) {
5582             return this.el.select('.collapse',true).first();
5583         }
5584         
5585         return this.el;
5586     },
5587     
5588     mask : function()
5589     {
5590         this.maskEl.show();
5591     },
5592     
5593     unmask : function()
5594     {
5595         this.maskEl.hide();
5596     },
5597     onToggle : function()
5598     {
5599         
5600         if(this.fireEvent('beforetoggle', this) === false){
5601             return;
5602         }
5603         var ce = this.el.select('.navbar-collapse',true).first();
5604       
5605         if (!ce.hasClass('show')) {
5606            this.expand();
5607         } else {
5608             this.collapse();
5609         }
5610         
5611         
5612     
5613     },
5614     /**
5615      * Expand the navbar pulldown 
5616      */
5617     expand : function ()
5618     {
5619        
5620         var ce = this.el.select('.navbar-collapse',true).first();
5621         if (ce.hasClass('collapsing')) {
5622             return;
5623         }
5624         ce.dom.style.height = '';
5625                // show it...
5626         ce.addClass('in'); // old...
5627         ce.removeClass('collapse');
5628         ce.addClass('show');
5629         var h = ce.getHeight();
5630         Roo.log(h);
5631         ce.removeClass('show');
5632         // at this point we should be able to see it..
5633         ce.addClass('collapsing');
5634         
5635         ce.setHeight(0); // resize it ...
5636         ce.on('transitionend', function() {
5637             //Roo.log('done transition');
5638             ce.removeClass('collapsing');
5639             ce.addClass('show');
5640             ce.removeClass('collapse');
5641
5642             ce.dom.style.height = '';
5643         }, this, { single: true} );
5644         ce.setHeight(h);
5645         ce.dom.scrollTop = 0;
5646     },
5647     /**
5648      * Collapse the navbar pulldown 
5649      */
5650     collapse : function()
5651     {
5652          var ce = this.el.select('.navbar-collapse',true).first();
5653        
5654         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5655             // it's collapsed or collapsing..
5656             return;
5657         }
5658         ce.removeClass('in'); // old...
5659         ce.setHeight(ce.getHeight());
5660         ce.removeClass('show');
5661         ce.addClass('collapsing');
5662         
5663         ce.on('transitionend', function() {
5664             ce.dom.style.height = '';
5665             ce.removeClass('collapsing');
5666             ce.addClass('collapse');
5667         }, this, { single: true} );
5668         ce.setHeight(0);
5669     }
5670     
5671     
5672     
5673 });
5674
5675
5676
5677  
5678
5679  /*
5680  * - LGPL
5681  *
5682  * navbar
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.NavSimplebar
5688  * @extends Roo.bootstrap.Navbar
5689  * Bootstrap Sidebar class
5690  *
5691  * @cfg {Boolean} inverse is inverted color
5692  * 
5693  * @cfg {String} type (nav | pills | tabs)
5694  * @cfg {Boolean} arrangement stacked | justified
5695  * @cfg {String} align (left | right) alignment
5696  * 
5697  * @cfg {Boolean} main (true|false) main nav bar? default false
5698  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699  * 
5700  * @cfg {String} tag (header|footer|nav|div) default is nav 
5701
5702  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5703  * 
5704  * 
5705  * @constructor
5706  * Create a new Sidebar
5707  * @param {Object} config The config object
5708  */
5709
5710
5711 Roo.bootstrap.NavSimplebar = function(config){
5712     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5713 };
5714
5715 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5716     
5717     inverse: false,
5718     
5719     type: false,
5720     arrangement: '',
5721     align : false,
5722     
5723     weight : 'light',
5724     
5725     main : false,
5726     
5727     
5728     tag : false,
5729     
5730     
5731     getAutoCreate : function(){
5732         
5733         
5734         var cfg = {
5735             tag : this.tag || 'div',
5736             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737         };
5738         if (['light','white'].indexOf(this.weight) > -1) {
5739             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740         }
5741         cfg.cls += ' bg-' + this.weight;
5742         
5743         if (this.inverse) {
5744             cfg.cls += ' navbar-inverse';
5745             
5746         }
5747         
5748         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749         
5750         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5751             return cfg;
5752         }
5753         
5754         
5755     
5756         
5757         cfg.cn = [
5758             {
5759                 cls: 'nav nav-' + this.xtype,
5760                 tag : 'ul'
5761             }
5762         ];
5763         
5764          
5765         this.type = this.type || 'nav';
5766         if (['tabs','pills'].indexOf(this.type) != -1) {
5767             cfg.cn[0].cls += ' nav-' + this.type
5768         
5769         
5770         } else {
5771             if (this.type!=='nav') {
5772                 Roo.log('nav type must be nav/tabs/pills')
5773             }
5774             cfg.cn[0].cls += ' navbar-nav'
5775         }
5776         
5777         
5778         
5779         
5780         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5781             cfg.cn[0].cls += ' nav-' + this.arrangement;
5782         }
5783         
5784         
5785         if (this.align === 'right') {
5786             cfg.cn[0].cls += ' navbar-right';
5787         }
5788         
5789         
5790         
5791         
5792         return cfg;
5793     
5794         
5795     }
5796     
5797     
5798     
5799 });
5800
5801
5802
5803  
5804
5805  
5806        /*
5807  * - LGPL
5808  *
5809  * navbar
5810  * navbar-fixed-top
5811  * navbar-expand-md  fixed-top 
5812  */
5813
5814 /**
5815  * @class Roo.bootstrap.NavHeaderbar
5816  * @extends Roo.bootstrap.NavSimplebar
5817  * Bootstrap Sidebar class
5818  *
5819  * @cfg {String} brand what is brand
5820  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5821  * @cfg {String} brand_href href of the brand
5822  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5823  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5824  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5825  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5826  * 
5827  * @constructor
5828  * Create a new Sidebar
5829  * @param {Object} config The config object
5830  */
5831
5832
5833 Roo.bootstrap.NavHeaderbar = function(config){
5834     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5835       
5836 };
5837
5838 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5839     
5840     position: '',
5841     brand: '',
5842     brand_href: false,
5843     srButton : true,
5844     autohide : false,
5845     desktopCenter : false,
5846    
5847     
5848     getAutoCreate : function(){
5849         
5850         var   cfg = {
5851             tag: this.nav || 'nav',
5852             cls: 'navbar navbar-expand-md',
5853             role: 'navigation',
5854             cn: []
5855         };
5856         
5857         var cn = cfg.cn;
5858         if (this.desktopCenter) {
5859             cn.push({cls : 'container', cn : []});
5860             cn = cn[0].cn;
5861         }
5862         
5863         if(this.srButton){
5864             var btn = {
5865                 tag: 'button',
5866                 type: 'button',
5867                 cls: 'navbar-toggle navbar-toggler',
5868                 'data-toggle': 'collapse',
5869                 cn: [
5870                     {
5871                         tag: 'span',
5872                         cls: 'sr-only',
5873                         html: 'Toggle navigation'
5874                     },
5875                     {
5876                         tag: 'span',
5877                         cls: 'icon-bar navbar-toggler-icon'
5878                     },
5879                     {
5880                         tag: 'span',
5881                         cls: 'icon-bar'
5882                     },
5883                     {
5884                         tag: 'span',
5885                         cls: 'icon-bar'
5886                     }
5887                 ]
5888             };
5889             
5890             cn.push( Roo.bootstrap.version == 4 ? btn : {
5891                 tag: 'div',
5892                 cls: 'navbar-header',
5893                 cn: [
5894                     btn
5895                 ]
5896             });
5897         }
5898         
5899         cn.push({
5900             tag: 'div',
5901             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5902             cn : []
5903         });
5904         
5905         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906         
5907         if (['light','white'].indexOf(this.weight) > -1) {
5908             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909         }
5910         cfg.cls += ' bg-' + this.weight;
5911         
5912         
5913         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5914             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915             
5916             // tag can override this..
5917             
5918             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5919         }
5920         
5921         if (this.brand !== '') {
5922             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5923             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924                 tag: 'a',
5925                 href: this.brand_href ? this.brand_href : '#',
5926                 cls: 'navbar-brand',
5927                 cn: [
5928                 this.brand
5929                 ]
5930             });
5931         }
5932         
5933         if(this.main){
5934             cfg.cls += ' main-nav';
5935         }
5936         
5937         
5938         return cfg;
5939
5940         
5941     },
5942     getHeaderChildContainer : function()
5943     {
5944         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5945             return this.el.select('.navbar-header',true).first();
5946         }
5947         
5948         return this.getChildContainer();
5949     },
5950     
5951     getChildContainer : function()
5952     {
5953          
5954         return this.el.select('.roo-navbar-collapse',true).first();
5955          
5956         
5957     },
5958     
5959     initEvents : function()
5960     {
5961         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962         
5963         if (this.autohide) {
5964             
5965             var prevScroll = 0;
5966             var ft = this.el;
5967             
5968             Roo.get(document).on('scroll',function(e) {
5969                 var ns = Roo.get(document).getScroll().top;
5970                 var os = prevScroll;
5971                 prevScroll = ns;
5972                 
5973                 if(ns > os){
5974                     ft.removeClass('slideDown');
5975                     ft.addClass('slideUp');
5976                     return;
5977                 }
5978                 ft.removeClass('slideUp');
5979                 ft.addClass('slideDown');
5980                  
5981               
5982           },this);
5983         }
5984     }    
5985     
5986 });
5987
5988
5989
5990  
5991
5992  /*
5993  * - LGPL
5994  *
5995  * navbar
5996  * 
5997  */
5998
5999 /**
6000  * @class Roo.bootstrap.NavSidebar
6001  * @extends Roo.bootstrap.Navbar
6002  * Bootstrap Sidebar class
6003  * 
6004  * @constructor
6005  * Create a new Sidebar
6006  * @param {Object} config The config object
6007  */
6008
6009
6010 Roo.bootstrap.NavSidebar = function(config){
6011     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6012 };
6013
6014 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6015     
6016     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017     
6018     getAutoCreate : function(){
6019         
6020         
6021         return  {
6022             tag: 'div',
6023             cls: 'sidebar sidebar-nav'
6024         };
6025     
6026         
6027     }
6028     
6029     
6030     
6031 });
6032
6033
6034
6035  
6036
6037  /*
6038  * - LGPL
6039  *
6040  * nav group
6041  * 
6042  */
6043
6044 /**
6045  * @class Roo.bootstrap.NavGroup
6046  * @extends Roo.bootstrap.Component
6047  * Bootstrap NavGroup class
6048  * @cfg {String} align (left|right)
6049  * @cfg {Boolean} inverse
6050  * @cfg {String} type (nav|pills|tab) default nav
6051  * @cfg {String} navId - reference Id for navbar.
6052  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6053  * 
6054  * @constructor
6055  * Create a new nav group
6056  * @param {Object} config The config object
6057  */
6058
6059 Roo.bootstrap.NavGroup = function(config){
6060     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6061     this.navItems = [];
6062    
6063     Roo.bootstrap.NavGroup.register(this);
6064      this.addEvents({
6065         /**
6066              * @event changed
6067              * Fires when the active item changes
6068              * @param {Roo.bootstrap.NavGroup} this
6069              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6070              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6071          */
6072         'changed': true
6073      });
6074     
6075 };
6076
6077 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6078     
6079     align: '',
6080     inverse: false,
6081     form: false,
6082     type: 'nav',
6083     navId : '',
6084     // private
6085     pilltype : true,
6086     
6087     navItems : false, 
6088     
6089     getAutoCreate : function()
6090     {
6091         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6092         
6093         cfg = {
6094             tag : 'ul',
6095             cls: 'nav' 
6096         };
6097         if (Roo.bootstrap.version == 4) {
6098             if (['tabs','pills'].indexOf(this.type) != -1) {
6099                 cfg.cls += ' nav-' + this.type; 
6100             } else {
6101                 // trying to remove so header bar can right align top?
6102                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6103                     // do not use on header bar... 
6104                     cfg.cls += ' navbar-nav';
6105                 }
6106             }
6107             
6108         } else {
6109             if (['tabs','pills'].indexOf(this.type) != -1) {
6110                 cfg.cls += ' nav-' + this.type
6111             } else {
6112                 if (this.type !== 'nav') {
6113                     Roo.log('nav type must be nav/tabs/pills')
6114                 }
6115                 cfg.cls += ' navbar-nav'
6116             }
6117         }
6118         
6119         if (this.parent() && this.parent().sidebar) {
6120             cfg = {
6121                 tag: 'ul',
6122                 cls: 'dashboard-menu sidebar-menu'
6123             };
6124             
6125             return cfg;
6126         }
6127         
6128         if (this.form === true) {
6129             cfg = {
6130                 tag: 'form',
6131                 cls: 'navbar-form form-inline'
6132             };
6133             //nav navbar-right ml-md-auto
6134             if (this.align === 'right') {
6135                 cfg.cls += ' navbar-right ml-md-auto';
6136             } else {
6137                 cfg.cls += ' navbar-left';
6138             }
6139         }
6140         
6141         if (this.align === 'right') {
6142             cfg.cls += ' navbar-right ml-md-auto';
6143         } else {
6144             cfg.cls += ' mr-auto';
6145         }
6146         
6147         if (this.inverse) {
6148             cfg.cls += ' navbar-inverse';
6149             
6150         }
6151         
6152         
6153         return cfg;
6154     },
6155     /**
6156     * sets the active Navigation item
6157     * @param {Roo.bootstrap.NavItem} the new current navitem
6158     */
6159     setActiveItem : function(item)
6160     {
6161         var prev = false;
6162         Roo.each(this.navItems, function(v){
6163             if (v == item) {
6164                 return ;
6165             }
6166             if (v.isActive()) {
6167                 v.setActive(false, true);
6168                 prev = v;
6169                 
6170             }
6171             
6172         });
6173
6174         item.setActive(true, true);
6175         this.fireEvent('changed', this, item, prev);
6176         
6177         
6178     },
6179     /**
6180     * gets the active Navigation item
6181     * @return {Roo.bootstrap.NavItem} the current navitem
6182     */
6183     getActive : function()
6184     {
6185         
6186         var prev = false;
6187         Roo.each(this.navItems, function(v){
6188             
6189             if (v.isActive()) {
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195         return prev;
6196     },
6197     
6198     indexOfNav : function()
6199     {
6200         
6201         var prev = false;
6202         Roo.each(this.navItems, function(v,i){
6203             
6204             if (v.isActive()) {
6205                 prev = i;
6206                 
6207             }
6208             
6209         });
6210         return prev;
6211     },
6212     /**
6213     * adds a Navigation item
6214     * @param {Roo.bootstrap.NavItem} the navitem to add
6215     */
6216     addItem : function(cfg)
6217     {
6218         if (this.form && Roo.bootstrap.version == 4) {
6219             cfg.tag = 'div';
6220         }
6221         var cn = new Roo.bootstrap.NavItem(cfg);
6222         this.register(cn);
6223         cn.parentId = this.id;
6224         cn.onRender(this.el, null);
6225         return cn;
6226     },
6227     /**
6228     * register a Navigation item
6229     * @param {Roo.bootstrap.NavItem} the navitem to add
6230     */
6231     register : function(item)
6232     {
6233         this.navItems.push( item);
6234         item.navId = this.navId;
6235     
6236     },
6237     
6238     /**
6239     * clear all the Navigation item
6240     */
6241    
6242     clearAll : function()
6243     {
6244         this.navItems = [];
6245         this.el.dom.innerHTML = '';
6246     },
6247     
6248     getNavItem: function(tabId)
6249     {
6250         var ret = false;
6251         Roo.each(this.navItems, function(e) {
6252             if (e.tabId == tabId) {
6253                ret =  e;
6254                return false;
6255             }
6256             return true;
6257             
6258         });
6259         return ret;
6260     },
6261     
6262     setActiveNext : function()
6263     {
6264         var i = this.indexOfNav(this.getActive());
6265         if (i > this.navItems.length) {
6266             return;
6267         }
6268         this.setActiveItem(this.navItems[i+1]);
6269     },
6270     setActivePrev : function()
6271     {
6272         var i = this.indexOfNav(this.getActive());
6273         if (i  < 1) {
6274             return;
6275         }
6276         this.setActiveItem(this.navItems[i-1]);
6277     },
6278     clearWasActive : function(except) {
6279         Roo.each(this.navItems, function(e) {
6280             if (e.tabId != except.tabId && e.was_active) {
6281                e.was_active = false;
6282                return false;
6283             }
6284             return true;
6285             
6286         });
6287     },
6288     getWasActive : function ()
6289     {
6290         var r = false;
6291         Roo.each(this.navItems, function(e) {
6292             if (e.was_active) {
6293                r = e;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299         return r;
6300     }
6301     
6302     
6303 });
6304
6305  
6306 Roo.apply(Roo.bootstrap.NavGroup, {
6307     
6308     groups: {},
6309      /**
6310     * register a Navigation Group
6311     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312     */
6313     register : function(navgrp)
6314     {
6315         this.groups[navgrp.navId] = navgrp;
6316         
6317     },
6318     /**
6319     * fetch a Navigation Group based on the navigation ID
6320     * @param {string} the navgroup to add
6321     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6322     */
6323     get: function(navId) {
6324         if (typeof(this.groups[navId]) == 'undefined') {
6325             return false;
6326             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327         }
6328         return this.groups[navId] ;
6329     }
6330     
6331     
6332     
6333 });
6334
6335  /*
6336  * - LGPL
6337  *
6338  * row
6339  * 
6340  */
6341
6342 /**
6343  * @class Roo.bootstrap.NavItem
6344  * @extends Roo.bootstrap.Component
6345  * Bootstrap Navbar.NavItem class
6346  * @cfg {String} href  link to
6347  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6348  * @cfg {Boolean} button_outline show and outlined button
6349  * @cfg {String} html content of button
6350  * @cfg {String} badge text inside badge
6351  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6352  * @cfg {String} glyphicon DEPRICATED - use fa
6353  * @cfg {String} icon DEPRICATED - use fa
6354  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6355  * @cfg {Boolean} active Is item active
6356  * @cfg {Boolean} disabled Is item disabled
6357  * @cfg {String} linkcls  Link Class
6358  * @cfg {Boolean} preventDefault (true | false) default false
6359  * @cfg {String} tabId the tab that this item activates.
6360  * @cfg {String} tagtype (a|span) render as a href or span?
6361  * @cfg {Boolean} animateRef (true|false) link to element default false  
6362   
6363  * @constructor
6364  * Create a new Navbar Item
6365  * @param {Object} config The config object
6366  */
6367 Roo.bootstrap.NavItem = function(config){
6368     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6369     this.addEvents({
6370         // raw events
6371         /**
6372          * @event click
6373          * The raw click event for the entire grid.
6374          * @param {Roo.EventObject} e
6375          */
6376         "click" : true,
6377          /**
6378             * @event changed
6379             * Fires when the active item active state changes
6380             * @param {Roo.bootstrap.NavItem} this
6381             * @param {boolean} state the new state
6382              
6383          */
6384         'changed': true,
6385         /**
6386             * @event scrollto
6387             * Fires when scroll to element
6388             * @param {Roo.bootstrap.NavItem} this
6389             * @param {Object} options
6390             * @param {Roo.EventObject} e
6391              
6392          */
6393         'scrollto': true
6394     });
6395    
6396 };
6397
6398 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6399     
6400     href: false,
6401     html: '',
6402     badge: '',
6403     icon: false,
6404     fa : false,
6405     glyphicon: false,
6406     active: false,
6407     preventDefault : false,
6408     tabId : false,
6409     tagtype : 'a',
6410     tag: 'li',
6411     disabled : false,
6412     animateRef : false,
6413     was_active : false,
6414     button_weight : '',
6415     button_outline : false,
6416     linkcls : '',
6417     navLink: false,
6418     
6419     getAutoCreate : function(){
6420          
6421         var cfg = {
6422             tag: this.tag,
6423             cls: 'nav-item'
6424         };
6425         
6426         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6427         
6428         if (this.active) {
6429             cfg.cls +=  ' active' ;
6430         }
6431         if (this.disabled) {
6432             cfg.cls += ' disabled';
6433         }
6434         
6435         // BS4 only?
6436         if (this.button_weight.length) {
6437             cfg.tag = this.href ? 'a' : 'button';
6438             cfg.html = this.html || '';
6439             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440             if (this.href) {
6441                 cfg.href = this.href;
6442             }
6443             if (this.fa) {
6444                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445             } else {
6446                 cfg.cls += " nav-html";
6447             }
6448             
6449             // menu .. should add dropdown-menu class - so no need for carat..
6450             
6451             if (this.badge !== '') {
6452                  
6453                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6454             }
6455             return cfg;
6456         }
6457         
6458         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6459             cfg.cn = [
6460                 {
6461                     tag: this.tagtype,
6462                     href : this.href || "#",
6463                     html: this.html || '',
6464                     cls : ''
6465                 }
6466             ];
6467             if (this.tagtype == 'a') {
6468                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6469         
6470             }
6471             if (this.icon) {
6472                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6473             } else  if (this.fa) {
6474                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6475             } else if(this.glyphicon) {
6476                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6477             } else {
6478                 cfg.cn[0].cls += " nav-html";
6479             }
6480             
6481             if (this.menu) {
6482                 cfg.cn[0].html += " <span class='caret'></span>";
6483              
6484             }
6485             
6486             if (this.badge !== '') {
6487                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6488             }
6489         }
6490         
6491         
6492         
6493         return cfg;
6494     },
6495     onRender : function(ct, position)
6496     {
6497        // Roo.log("Call onRender: " + this.xtype);
6498         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6499             this.tag = 'div';
6500         }
6501         
6502         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6503         this.navLink = this.el.select('.nav-link',true).first();
6504         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6505         return ret;
6506     },
6507       
6508     
6509     initEvents: function() 
6510     {
6511         if (typeof (this.menu) != 'undefined') {
6512             this.menu.parentType = this.xtype;
6513             this.menu.triggerEl = this.el;
6514             this.menu = this.addxtype(Roo.apply({}, this.menu));
6515         }
6516         
6517         this.el.on('click', this.onClick, this);
6518         
6519         //if(this.tagtype == 'span'){
6520         //    this.el.select('span',true).on('click', this.onClick, this);
6521         //}
6522        
6523         // at this point parent should be available..
6524         this.parent().register(this);
6525     },
6526     
6527     onClick : function(e)
6528     {
6529         if (e.getTarget('.dropdown-menu-item')) {
6530             // did you click on a menu itemm.... - then don't trigger onclick..
6531             return;
6532         }
6533         
6534         if(
6535                 this.preventDefault || 
6536                 this.href == '#' 
6537         ){
6538             Roo.log("NavItem - prevent Default?");
6539             e.preventDefault();
6540         }
6541         
6542         if (this.disabled) {
6543             return;
6544         }
6545         
6546         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6547         if (tg && tg.transition) {
6548             Roo.log("waiting for the transitionend");
6549             return;
6550         }
6551         
6552         
6553         
6554         //Roo.log("fire event clicked");
6555         if(this.fireEvent('click', this, e) === false){
6556             return;
6557         };
6558         
6559         if(this.tagtype == 'span'){
6560             return;
6561         }
6562         
6563         //Roo.log(this.href);
6564         var ael = this.el.select('a',true).first();
6565         //Roo.log(ael);
6566         
6567         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6568             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6569             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6570                 return; // ignore... - it's a 'hash' to another page.
6571             }
6572             Roo.log("NavItem - prevent Default?");
6573             e.preventDefault();
6574             this.scrollToElement(e);
6575         }
6576         
6577         
6578         var p =  this.parent();
6579    
6580         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6581             if (typeof(p.setActiveItem) !== 'undefined') {
6582                 p.setActiveItem(this);
6583             }
6584         }
6585         
6586         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6587         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6588             // remove the collapsed menu expand...
6589             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6590         }
6591     },
6592     
6593     isActive: function () {
6594         return this.active
6595     },
6596     setActive : function(state, fire, is_was_active)
6597     {
6598         if (this.active && !state && this.navId) {
6599             this.was_active = true;
6600             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601             if (nv) {
6602                 nv.clearWasActive(this);
6603             }
6604             
6605         }
6606         this.active = state;
6607         
6608         if (!state ) {
6609             this.el.removeClass('active');
6610             this.navLink ? this.navLink.removeClass('active') : false;
6611         } else if (!this.el.hasClass('active')) {
6612             
6613             this.el.addClass('active');
6614             if (Roo.bootstrap.version == 4 && this.navLink ) {
6615                 this.navLink.addClass('active');
6616             }
6617             
6618         }
6619         if (fire) {
6620             this.fireEvent('changed', this, state);
6621         }
6622         
6623         // show a panel if it's registered and related..
6624         
6625         if (!this.navId || !this.tabId || !state || is_was_active) {
6626             return;
6627         }
6628         
6629         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6630         if (!tg) {
6631             return;
6632         }
6633         var pan = tg.getPanelByName(this.tabId);
6634         if (!pan) {
6635             return;
6636         }
6637         // if we can not flip to new panel - go back to old nav highlight..
6638         if (false == tg.showPanel(pan)) {
6639             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640             if (nv) {
6641                 var onav = nv.getWasActive();
6642                 if (onav) {
6643                     onav.setActive(true, false, true);
6644                 }
6645             }
6646             
6647         }
6648         
6649         
6650         
6651     },
6652      // this should not be here...
6653     setDisabled : function(state)
6654     {
6655         this.disabled = state;
6656         if (!state ) {
6657             this.el.removeClass('disabled');
6658         } else if (!this.el.hasClass('disabled')) {
6659             this.el.addClass('disabled');
6660         }
6661         
6662     },
6663     
6664     /**
6665      * Fetch the element to display the tooltip on.
6666      * @return {Roo.Element} defaults to this.el
6667      */
6668     tooltipEl : function()
6669     {
6670         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6671     },
6672     
6673     scrollToElement : function(e)
6674     {
6675         var c = document.body;
6676         
6677         /*
6678          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679          */
6680         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6681             c = document.documentElement;
6682         }
6683         
6684         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6685         
6686         if(!target){
6687             return;
6688         }
6689
6690         var o = target.calcOffsetsTo(c);
6691         
6692         var options = {
6693             target : target,
6694             value : o[1]
6695         };
6696         
6697         this.fireEvent('scrollto', this, options, e);
6698         
6699         Roo.get(c).scrollTo('top', options.value, true);
6700         
6701         return;
6702     },
6703     /**
6704      * Set the HTML (text content) of the item
6705      * @param {string} html  content for the nav item
6706      */
6707     setHtml : function(html)
6708     {
6709         this.html = html;
6710         this.htmlEl.dom.innerHTML = html;
6711         
6712     } 
6713 });
6714  
6715
6716  /*
6717  * - LGPL
6718  *
6719  * sidebar item
6720  *
6721  *  li
6722  *    <span> icon </span>
6723  *    <span> text </span>
6724  *    <span>badge </span>
6725  */
6726
6727 /**
6728  * @class Roo.bootstrap.NavSidebarItem
6729  * @extends Roo.bootstrap.NavItem
6730  * Bootstrap Navbar.NavSidebarItem class
6731  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6732  * {Boolean} open is the menu open
6733  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6734  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6735  * {String} buttonSize (sm|md|lg)the extra classes for the button
6736  * {Boolean} showArrow show arrow next to the text (default true)
6737  * @constructor
6738  * Create a new Navbar Button
6739  * @param {Object} config The config object
6740  */
6741 Roo.bootstrap.NavSidebarItem = function(config){
6742     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6743     this.addEvents({
6744         // raw events
6745         /**
6746          * @event click
6747          * The raw click event for the entire grid.
6748          * @param {Roo.EventObject} e
6749          */
6750         "click" : true,
6751          /**
6752             * @event changed
6753             * Fires when the active item active state changes
6754             * @param {Roo.bootstrap.NavSidebarItem} this
6755             * @param {boolean} state the new state
6756              
6757          */
6758         'changed': true
6759     });
6760    
6761 };
6762
6763 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6764     
6765     badgeWeight : 'default',
6766     
6767     open: false,
6768     
6769     buttonView : false,
6770     
6771     buttonWeight : 'default',
6772     
6773     buttonSize : 'md',
6774     
6775     showArrow : true,
6776     
6777     getAutoCreate : function(){
6778         
6779         
6780         var a = {
6781                 tag: 'a',
6782                 href : this.href || '#',
6783                 cls: '',
6784                 html : '',
6785                 cn : []
6786         };
6787         
6788         if(this.buttonView){
6789             a = {
6790                 tag: 'button',
6791                 href : this.href || '#',
6792                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6793                 html : this.html,
6794                 cn : []
6795             };
6796         }
6797         
6798         var cfg = {
6799             tag: 'li',
6800             cls: '',
6801             cn: [ a ]
6802         };
6803         
6804         if (this.active) {
6805             cfg.cls += ' active';
6806         }
6807         
6808         if (this.disabled) {
6809             cfg.cls += ' disabled';
6810         }
6811         if (this.open) {
6812             cfg.cls += ' open x-open';
6813         }
6814         // left icon..
6815         if (this.glyphicon || this.icon) {
6816             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6817             a.cn.push({ tag : 'i', cls : c }) ;
6818         }
6819         
6820         if(!this.buttonView){
6821             var span = {
6822                 tag: 'span',
6823                 html : this.html || ''
6824             };
6825
6826             a.cn.push(span);
6827             
6828         }
6829         
6830         if (this.badge !== '') {
6831             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6832         }
6833         
6834         if (this.menu) {
6835             
6836             if(this.showArrow){
6837                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6838             }
6839             
6840             a.cls += ' dropdown-toggle treeview' ;
6841         }
6842         
6843         return cfg;
6844     },
6845     
6846     initEvents : function()
6847     { 
6848         if (typeof (this.menu) != 'undefined') {
6849             this.menu.parentType = this.xtype;
6850             this.menu.triggerEl = this.el;
6851             this.menu = this.addxtype(Roo.apply({}, this.menu));
6852         }
6853         
6854         this.el.on('click', this.onClick, this);
6855         
6856         if(this.badge !== ''){
6857             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6858         }
6859         
6860     },
6861     
6862     onClick : function(e)
6863     {
6864         if(this.disabled){
6865             e.preventDefault();
6866             return;
6867         }
6868         
6869         if(this.preventDefault){
6870             e.preventDefault();
6871         }
6872         
6873         this.fireEvent('click', this, e);
6874     },
6875     
6876     disable : function()
6877     {
6878         this.setDisabled(true);
6879     },
6880     
6881     enable : function()
6882     {
6883         this.setDisabled(false);
6884     },
6885     
6886     setDisabled : function(state)
6887     {
6888         if(this.disabled == state){
6889             return;
6890         }
6891         
6892         this.disabled = state;
6893         
6894         if (state) {
6895             this.el.addClass('disabled');
6896             return;
6897         }
6898         
6899         this.el.removeClass('disabled');
6900         
6901         return;
6902     },
6903     
6904     setActive : function(state)
6905     {
6906         if(this.active == state){
6907             return;
6908         }
6909         
6910         this.active = state;
6911         
6912         if (state) {
6913             this.el.addClass('active');
6914             return;
6915         }
6916         
6917         this.el.removeClass('active');
6918         
6919         return;
6920     },
6921     
6922     isActive: function () 
6923     {
6924         return this.active;
6925     },
6926     
6927     setBadge : function(str)
6928     {
6929         if(!this.badgeEl){
6930             return;
6931         }
6932         
6933         this.badgeEl.dom.innerHTML = str;
6934     }
6935     
6936    
6937      
6938  
6939 });
6940  
6941
6942  /*
6943  * - LGPL
6944  *
6945  *  Breadcrumb Nav
6946  * 
6947  */
6948 Roo.namespace('Roo.bootstrap.breadcrumb');
6949
6950
6951 /**
6952  * @class Roo.bootstrap.breadcrumb.Nav
6953  * @extends Roo.bootstrap.Component
6954  * Bootstrap Breadcrumb Nav Class
6955  *  
6956  * @children Roo.bootstrap.breadcrumb.Item
6957  * 
6958  * @constructor
6959  * Create a new breadcrumb.Nav
6960  * @param {Object} config The config object
6961  */
6962
6963
6964 Roo.bootstrap.breadcrumb.Nav = function(config){
6965     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6966     
6967     
6968 };
6969
6970 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6971     
6972     getAutoCreate : function()
6973     {
6974
6975         var cfg = {
6976             tag: 'nav',
6977             cn : [
6978                 {
6979                     tag : 'ol',
6980                     cls : 'breadcrumb'
6981                 }
6982             ]
6983             
6984         };
6985           
6986         return cfg;
6987     },
6988     
6989     initEvents: function()
6990     {
6991         this.olEl = this.el.select('ol',true).first();    
6992     },
6993     getChildContainer : function()
6994     {
6995         return this.olEl;  
6996     }
6997     
6998 });
6999
7000  /*
7001  * - LGPL
7002  *
7003  *  Breadcrumb Item
7004  * 
7005  */
7006
7007
7008 /**
7009  * @class Roo.bootstrap.breadcrumb.Nav
7010  * @extends Roo.bootstrap.Component
7011  * Bootstrap Breadcrumb Nav Class
7012  *  
7013  * @children Roo.bootstrap.breadcrumb.Component
7014  * @cfg {String} html the content of the link.
7015  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7016  * @cfg {Boolean} active is it active
7017
7018  * 
7019  * @constructor
7020  * Create a new breadcrumb.Nav
7021  * @param {Object} config The config object
7022  */
7023
7024 Roo.bootstrap.breadcrumb.Item = function(config){
7025     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7026     this.addEvents({
7027         // img events
7028         /**
7029          * @event click
7030          * The img click event for the img.
7031          * @param {Roo.EventObject} e
7032          */
7033         "click" : true
7034     });
7035     
7036 };
7037
7038 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7039     
7040     href: false,
7041     html : '',
7042     
7043     getAutoCreate : function()
7044     {
7045
7046         var cfg = {
7047             tag: 'li',
7048             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049         };
7050         if (this.href !== false) {
7051             cfg.cn = [{
7052                 tag : 'a',
7053                 href : this.href,
7054                 html : this.html
7055             }];
7056         } else {
7057             cfg.html = this.html;
7058         }
7059         
7060         return cfg;
7061     },
7062     
7063     initEvents: function()
7064     {
7065         if (this.href) {
7066             this.el.select('a', true).first().on('click',this.onClick, this)
7067         }
7068         
7069     },
7070     onClick : function(e)
7071     {
7072         e.preventDefault();
7073         this.fireEvent('click',this,  e);
7074     }
7075     
7076 });
7077
7078  /*
7079  * - LGPL
7080  *
7081  * row
7082  * 
7083  */
7084
7085 /**
7086  * @class Roo.bootstrap.Row
7087  * @extends Roo.bootstrap.Component
7088  * Bootstrap Row class (contains columns...)
7089  * 
7090  * @constructor
7091  * Create a new Row
7092  * @param {Object} config The config object
7093  */
7094
7095 Roo.bootstrap.Row = function(config){
7096     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7097 };
7098
7099 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7100     
7101     getAutoCreate : function(){
7102        return {
7103             cls: 'row clearfix'
7104        };
7105     }
7106     
7107     
7108 });
7109
7110  
7111
7112  /*
7113  * - LGPL
7114  *
7115  * pagination
7116  * 
7117  */
7118
7119 /**
7120  * @class Roo.bootstrap.Pagination
7121  * @extends Roo.bootstrap.Component
7122  * Bootstrap Pagination class
7123  * @cfg {String} size xs | sm | md | lg
7124  * @cfg {Boolean} inverse false | true
7125  * 
7126  * @constructor
7127  * Create a new Pagination
7128  * @param {Object} config The config object
7129  */
7130
7131 Roo.bootstrap.Pagination = function(config){
7132     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7133 };
7134
7135 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7136     
7137     cls: false,
7138     size: false,
7139     inverse: false,
7140     
7141     getAutoCreate : function(){
7142         var cfg = {
7143             tag: 'ul',
7144                 cls: 'pagination'
7145         };
7146         if (this.inverse) {
7147             cfg.cls += ' inverse';
7148         }
7149         if (this.html) {
7150             cfg.html=this.html;
7151         }
7152         if (this.cls) {
7153             cfg.cls += " " + this.cls;
7154         }
7155         return cfg;
7156     }
7157    
7158 });
7159
7160  
7161
7162  /*
7163  * - LGPL
7164  *
7165  * Pagination item
7166  * 
7167  */
7168
7169
7170 /**
7171  * @class Roo.bootstrap.PaginationItem
7172  * @extends Roo.bootstrap.Component
7173  * Bootstrap PaginationItem class
7174  * @cfg {String} html text
7175  * @cfg {String} href the link
7176  * @cfg {Boolean} preventDefault (true | false) default true
7177  * @cfg {Boolean} active (true | false) default false
7178  * @cfg {Boolean} disabled default false
7179  * 
7180  * 
7181  * @constructor
7182  * Create a new PaginationItem
7183  * @param {Object} config The config object
7184  */
7185
7186
7187 Roo.bootstrap.PaginationItem = function(config){
7188     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7189     this.addEvents({
7190         // raw events
7191         /**
7192          * @event click
7193          * The raw click event for the entire grid.
7194          * @param {Roo.EventObject} e
7195          */
7196         "click" : true
7197     });
7198 };
7199
7200 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7201     
7202     href : false,
7203     html : false,
7204     preventDefault: true,
7205     active : false,
7206     cls : false,
7207     disabled: false,
7208     
7209     getAutoCreate : function(){
7210         var cfg= {
7211             tag: 'li',
7212             cn: [
7213                 {
7214                     tag : 'a',
7215                     href : this.href ? this.href : '#',
7216                     html : this.html ? this.html : ''
7217                 }
7218             ]
7219         };
7220         
7221         if(this.cls){
7222             cfg.cls = this.cls;
7223         }
7224         
7225         if(this.disabled){
7226             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7227         }
7228         
7229         if(this.active){
7230             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7231         }
7232         
7233         return cfg;
7234     },
7235     
7236     initEvents: function() {
7237         
7238         this.el.on('click', this.onClick, this);
7239         
7240     },
7241     onClick : function(e)
7242     {
7243         Roo.log('PaginationItem on click ');
7244         if(this.preventDefault){
7245             e.preventDefault();
7246         }
7247         
7248         if(this.disabled){
7249             return;
7250         }
7251         
7252         this.fireEvent('click', this, e);
7253     }
7254    
7255 });
7256
7257  
7258
7259  /*
7260  * - LGPL
7261  *
7262  * slider
7263  * 
7264  */
7265
7266
7267 /**
7268  * @class Roo.bootstrap.Slider
7269  * @extends Roo.bootstrap.Component
7270  * Bootstrap Slider class
7271  *    
7272  * @constructor
7273  * Create a new Slider
7274  * @param {Object} config The config object
7275  */
7276
7277 Roo.bootstrap.Slider = function(config){
7278     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7279 };
7280
7281 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7282     
7283     getAutoCreate : function(){
7284         
7285         var cfg = {
7286             tag: 'div',
7287             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7288             cn: [
7289                 {
7290                     tag: 'a',
7291                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7292                 }
7293             ]
7294         };
7295         
7296         return cfg;
7297     }
7298    
7299 });
7300
7301  /*
7302  * Based on:
7303  * Ext JS Library 1.1.1
7304  * Copyright(c) 2006-2007, Ext JS, LLC.
7305  *
7306  * Originally Released Under LGPL - original licence link has changed is not relivant.
7307  *
7308  * Fork - LGPL
7309  * <script type="text/javascript">
7310  */
7311  /**
7312  * @extends Roo.dd.DDProxy
7313  * @class Roo.grid.SplitDragZone
7314  * Support for Column Header resizing
7315  * @constructor
7316  * @param {Object} config
7317  */
7318 // private
7319 // This is a support class used internally by the Grid components
7320 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7321     this.grid = grid;
7322     this.view = grid.getView();
7323     this.proxy = this.view.resizeProxy;
7324     Roo.grid.SplitDragZone.superclass.constructor.call(
7325         this,
7326         hd, // ID
7327         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7328         {  // CONFIG
7329             dragElId : Roo.id(this.proxy.dom),
7330             resizeFrame:false
7331         }
7332     );
7333     
7334     this.setHandleElId(Roo.id(hd));
7335     if (hd2 !== false) {
7336         this.setOuterHandleElId(Roo.id(hd2));
7337     }
7338     
7339     this.scroll = false;
7340 };
7341 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7342     fly: Roo.Element.fly,
7343
7344     b4StartDrag : function(x, y){
7345         this.view.headersDisabled = true;
7346         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7347                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7348         );
7349         this.proxy.setHeight(h);
7350         
7351         // for old system colWidth really stored the actual width?
7352         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7353         // which in reality did not work.. - it worked only for fixed sizes
7354         // for resizable we need to use actual sizes.
7355         var w = this.cm.getColumnWidth(this.cellIndex);
7356         if (!this.view.mainWrap) {
7357             // bootstrap.
7358             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7359         }
7360         
7361         
7362         
7363         // this was w-this.grid.minColumnWidth;
7364         // doesnt really make sense? - w = thie curren width or the rendered one?
7365         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7366         this.resetConstraints();
7367         this.setXConstraint(minw, 1000);
7368         this.setYConstraint(0, 0);
7369         this.minX = x - minw;
7370         this.maxX = x + 1000;
7371         this.startPos = x;
7372         if (!this.view.mainWrap) { // this is Bootstrap code..
7373             this.getDragEl().style.display='block';
7374         }
7375         
7376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7377     },
7378
7379
7380     handleMouseDown : function(e){
7381         ev = Roo.EventObject.setEvent(e);
7382         var t = this.fly(ev.getTarget());
7383         if(t.hasClass("x-grid-split")){
7384             this.cellIndex = this.view.getCellIndex(t.dom);
7385             this.split = t.dom;
7386             this.cm = this.grid.colModel;
7387             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7388                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7389             }
7390         }
7391     },
7392
7393     endDrag : function(e){
7394         this.view.headersDisabled = false;
7395         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7396         var diff = endX - this.startPos;
7397         // 
7398         var w = this.cm.getColumnWidth(this.cellIndex);
7399         if (!this.view.mainWrap) {
7400             w = 0;
7401         }
7402         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7403     },
7404
7405     autoOffset : function(){
7406         this.setDelta(0,0);
7407     }
7408 });/*
7409  * Based on:
7410  * Ext JS Library 1.1.1
7411  * Copyright(c) 2006-2007, Ext JS, LLC.
7412  *
7413  * Originally Released Under LGPL - original licence link has changed is not relivant.
7414  *
7415  * Fork - LGPL
7416  * <script type="text/javascript">
7417  */
7418
7419 /**
7420  * @class Roo.grid.AbstractSelectionModel
7421  * @extends Roo.util.Observable
7422  * @abstract
7423  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7424  * implemented by descendant classes.  This class should not be directly instantiated.
7425  * @constructor
7426  */
7427 Roo.grid.AbstractSelectionModel = function(){
7428     this.locked = false;
7429     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7430 };
7431
7432 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7433     /** @ignore Called by the grid automatically. Do not call directly. */
7434     init : function(grid){
7435         this.grid = grid;
7436         this.initEvents();
7437     },
7438
7439     /**
7440      * Locks the selections.
7441      */
7442     lock : function(){
7443         this.locked = true;
7444     },
7445
7446     /**
7447      * Unlocks the selections.
7448      */
7449     unlock : function(){
7450         this.locked = false;
7451     },
7452
7453     /**
7454      * Returns true if the selections are locked.
7455      * @return {Boolean}
7456      */
7457     isLocked : function(){
7458         return this.locked;
7459     }
7460 });/*
7461  * Based on:
7462  * Ext JS Library 1.1.1
7463  * Copyright(c) 2006-2007, Ext JS, LLC.
7464  *
7465  * Originally Released Under LGPL - original licence link has changed is not relivant.
7466  *
7467  * Fork - LGPL
7468  * <script type="text/javascript">
7469  */
7470 /**
7471  * @extends Roo.grid.AbstractSelectionModel
7472  * @class Roo.grid.RowSelectionModel
7473  * The default SelectionModel used by {@link Roo.grid.Grid}.
7474  * It supports multiple selections and keyboard selection/navigation. 
7475  * @constructor
7476  * @param {Object} config
7477  */
7478 Roo.grid.RowSelectionModel = function(config){
7479     Roo.apply(this, config);
7480     this.selections = new Roo.util.MixedCollection(false, function(o){
7481         return o.id;
7482     });
7483
7484     this.last = false;
7485     this.lastActive = false;
7486
7487     this.addEvents({
7488         /**
7489         * @event selectionchange
7490         * Fires when the selection changes
7491         * @param {SelectionModel} this
7492         */
7493        "selectionchange" : true,
7494        /**
7495         * @event afterselectionchange
7496         * Fires after the selection changes (eg. by key press or clicking)
7497         * @param {SelectionModel} this
7498         */
7499        "afterselectionchange" : true,
7500        /**
7501         * @event beforerowselect
7502         * Fires when a row is selected being selected, return false to cancel.
7503         * @param {SelectionModel} this
7504         * @param {Number} rowIndex The selected index
7505         * @param {Boolean} keepExisting False if other selections will be cleared
7506         */
7507        "beforerowselect" : true,
7508        /**
7509         * @event rowselect
7510         * Fires when a row is selected.
7511         * @param {SelectionModel} this
7512         * @param {Number} rowIndex The selected index
7513         * @param {Roo.data.Record} r The record
7514         */
7515        "rowselect" : true,
7516        /**
7517         * @event rowdeselect
7518         * Fires when a row is deselected.
7519         * @param {SelectionModel} this
7520         * @param {Number} rowIndex The selected index
7521         */
7522         "rowdeselect" : true
7523     });
7524     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7525     this.locked = false;
7526 };
7527
7528 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7529     /**
7530      * @cfg {Boolean} singleSelect
7531      * True to allow selection of only one row at a time (defaults to false)
7532      */
7533     singleSelect : false,
7534
7535     // private
7536     initEvents : function(){
7537
7538         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7539             this.grid.on("mousedown", this.handleMouseDown, this);
7540         }else{ // allow click to work like normal
7541             this.grid.on("rowclick", this.handleDragableRowClick, this);
7542         }
7543         // bootstrap does not have a view..
7544         var view = this.grid.view ? this.grid.view : this.grid;
7545         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7546             "up" : function(e){
7547                 if(!e.shiftKey){
7548                     this.selectPrevious(e.shiftKey);
7549                 }else if(this.last !== false && this.lastActive !== false){
7550                     var last = this.last;
7551                     this.selectRange(this.last,  this.lastActive-1);
7552                     view.focusRow(this.lastActive);
7553                     if(last !== false){
7554                         this.last = last;
7555                     }
7556                 }else{
7557                     this.selectFirstRow();
7558                 }
7559                 this.fireEvent("afterselectionchange", this);
7560             },
7561             "down" : function(e){
7562                 if(!e.shiftKey){
7563                     this.selectNext(e.shiftKey);
7564                 }else if(this.last !== false && this.lastActive !== false){
7565                     var last = this.last;
7566                     this.selectRange(this.last,  this.lastActive+1);
7567                     view.focusRow(this.lastActive);
7568                     if(last !== false){
7569                         this.last = last;
7570                     }
7571                 }else{
7572                     this.selectFirstRow();
7573                 }
7574                 this.fireEvent("afterselectionchange", this);
7575             },
7576             scope: this
7577         });
7578
7579          
7580         view.on("refresh", this.onRefresh, this);
7581         view.on("rowupdated", this.onRowUpdated, this);
7582         view.on("rowremoved", this.onRemove, this);
7583     },
7584
7585     // private
7586     onRefresh : function(){
7587         var ds = this.grid.ds, i, v = this.grid.view;
7588         var s = this.selections;
7589         s.each(function(r){
7590             if((i = ds.indexOfId(r.id)) != -1){
7591                 v.onRowSelect(i);
7592                 s.add(ds.getAt(i)); // updating the selection relate data
7593             }else{
7594                 s.remove(r);
7595             }
7596         });
7597     },
7598
7599     // private
7600     onRemove : function(v, index, r){
7601         this.selections.remove(r);
7602     },
7603
7604     // private
7605     onRowUpdated : function(v, index, r){
7606         if(this.isSelected(r)){
7607             v.onRowSelect(index);
7608         }
7609     },
7610
7611     /**
7612      * Select records.
7613      * @param {Array} records The records to select
7614      * @param {Boolean} keepExisting (optional) True to keep existing selections
7615      */
7616     selectRecords : function(records, keepExisting){
7617         if(!keepExisting){
7618             this.clearSelections();
7619         }
7620         var ds = this.grid.ds;
7621         for(var i = 0, len = records.length; i < len; i++){
7622             this.selectRow(ds.indexOf(records[i]), true);
7623         }
7624     },
7625
7626     /**
7627      * Gets the number of selected rows.
7628      * @return {Number}
7629      */
7630     getCount : function(){
7631         return this.selections.length;
7632     },
7633
7634     /**
7635      * Selects the first row in the grid.
7636      */
7637     selectFirstRow : function(){
7638         this.selectRow(0);
7639     },
7640
7641     /**
7642      * Select the last row.
7643      * @param {Boolean} keepExisting (optional) True to keep existing selections
7644      */
7645     selectLastRow : function(keepExisting){
7646         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7647     },
7648
7649     /**
7650      * Selects the row immediately following the last selected row.
7651      * @param {Boolean} keepExisting (optional) True to keep existing selections
7652      */
7653     selectNext : function(keepExisting){
7654         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7655             this.selectRow(this.last+1, keepExisting);
7656             var view = this.grid.view ? this.grid.view : this.grid;
7657             view.focusRow(this.last);
7658         }
7659     },
7660
7661     /**
7662      * Selects the row that precedes the last selected row.
7663      * @param {Boolean} keepExisting (optional) True to keep existing selections
7664      */
7665     selectPrevious : function(keepExisting){
7666         if(this.last){
7667             this.selectRow(this.last-1, keepExisting);
7668             var view = this.grid.view ? this.grid.view : this.grid;
7669             view.focusRow(this.last);
7670         }
7671     },
7672
7673     /**
7674      * Returns the selected records
7675      * @return {Array} Array of selected records
7676      */
7677     getSelections : function(){
7678         return [].concat(this.selections.items);
7679     },
7680
7681     /**
7682      * Returns the first selected record.
7683      * @return {Record}
7684      */
7685     getSelected : function(){
7686         return this.selections.itemAt(0);
7687     },
7688
7689
7690     /**
7691      * Clears all selections.
7692      */
7693     clearSelections : function(fast){
7694         if(this.locked) {
7695             return;
7696         }
7697         if(fast !== true){
7698             var ds = this.grid.ds;
7699             var s = this.selections;
7700             s.each(function(r){
7701                 this.deselectRow(ds.indexOfId(r.id));
7702             }, this);
7703             s.clear();
7704         }else{
7705             this.selections.clear();
7706         }
7707         this.last = false;
7708     },
7709
7710
7711     /**
7712      * Selects all rows.
7713      */
7714     selectAll : function(){
7715         if(this.locked) {
7716             return;
7717         }
7718         this.selections.clear();
7719         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7720             this.selectRow(i, true);
7721         }
7722     },
7723
7724     /**
7725      * Returns True if there is a selection.
7726      * @return {Boolean}
7727      */
7728     hasSelection : function(){
7729         return this.selections.length > 0;
7730     },
7731
7732     /**
7733      * Returns True if the specified row is selected.
7734      * @param {Number/Record} record The record or index of the record to check
7735      * @return {Boolean}
7736      */
7737     isSelected : function(index){
7738         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7739         return (r && this.selections.key(r.id) ? true : false);
7740     },
7741
7742     /**
7743      * Returns True if the specified record id is selected.
7744      * @param {String} id The id of record to check
7745      * @return {Boolean}
7746      */
7747     isIdSelected : function(id){
7748         return (this.selections.key(id) ? true : false);
7749     },
7750
7751     // private
7752     handleMouseDown : function(e, t)
7753     {
7754         var view = this.grid.view ? this.grid.view : this.grid;
7755         var rowIndex;
7756         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7757             return;
7758         };
7759         if(e.shiftKey && this.last !== false){
7760             var last = this.last;
7761             this.selectRange(last, rowIndex, e.ctrlKey);
7762             this.last = last; // reset the last
7763             view.focusRow(rowIndex);
7764         }else{
7765             var isSelected = this.isSelected(rowIndex);
7766             if(e.button !== 0 && isSelected){
7767                 view.focusRow(rowIndex);
7768             }else if(e.ctrlKey && isSelected){
7769                 this.deselectRow(rowIndex);
7770             }else if(!isSelected){
7771                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7772                 view.focusRow(rowIndex);
7773             }
7774         }
7775         this.fireEvent("afterselectionchange", this);
7776     },
7777     // private
7778     handleDragableRowClick :  function(grid, rowIndex, e) 
7779     {
7780         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7781             this.selectRow(rowIndex, false);
7782             var view = this.grid.view ? this.grid.view : this.grid;
7783             view.focusRow(rowIndex);
7784              this.fireEvent("afterselectionchange", this);
7785         }
7786     },
7787     
7788     /**
7789      * Selects multiple rows.
7790      * @param {Array} rows Array of the indexes of the row to select
7791      * @param {Boolean} keepExisting (optional) True to keep existing selections
7792      */
7793     selectRows : function(rows, keepExisting){
7794         if(!keepExisting){
7795             this.clearSelections();
7796         }
7797         for(var i = 0, len = rows.length; i < len; i++){
7798             this.selectRow(rows[i], true);
7799         }
7800     },
7801
7802     /**
7803      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7804      * @param {Number} startRow The index of the first row in the range
7805      * @param {Number} endRow The index of the last row in the range
7806      * @param {Boolean} keepExisting (optional) True to retain existing selections
7807      */
7808     selectRange : function(startRow, endRow, keepExisting){
7809         if(this.locked) {
7810             return;
7811         }
7812         if(!keepExisting){
7813             this.clearSelections();
7814         }
7815         if(startRow <= endRow){
7816             for(var i = startRow; i <= endRow; i++){
7817                 this.selectRow(i, true);
7818             }
7819         }else{
7820             for(var i = startRow; i >= endRow; i--){
7821                 this.selectRow(i, true);
7822             }
7823         }
7824     },
7825
7826     /**
7827      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7828      * @param {Number} startRow The index of the first row in the range
7829      * @param {Number} endRow The index of the last row in the range
7830      */
7831     deselectRange : function(startRow, endRow, preventViewNotify){
7832         if(this.locked) {
7833             return;
7834         }
7835         for(var i = startRow; i <= endRow; i++){
7836             this.deselectRow(i, preventViewNotify);
7837         }
7838     },
7839
7840     /**
7841      * Selects a row.
7842      * @param {Number} row The index of the row to select
7843      * @param {Boolean} keepExisting (optional) True to keep existing selections
7844      */
7845     selectRow : function(index, keepExisting, preventViewNotify){
7846         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7847             return;
7848         }
7849         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7850             if(!keepExisting || this.singleSelect){
7851                 this.clearSelections();
7852             }
7853             var r = this.grid.ds.getAt(index);
7854             this.selections.add(r);
7855             this.last = this.lastActive = index;
7856             if(!preventViewNotify){
7857                 var view = this.grid.view ? this.grid.view : this.grid;
7858                 view.onRowSelect(index);
7859             }
7860             this.fireEvent("rowselect", this, index, r);
7861             this.fireEvent("selectionchange", this);
7862         }
7863     },
7864
7865     /**
7866      * Deselects a row.
7867      * @param {Number} row The index of the row to deselect
7868      */
7869     deselectRow : function(index, preventViewNotify){
7870         if(this.locked) {
7871             return;
7872         }
7873         if(this.last == index){
7874             this.last = false;
7875         }
7876         if(this.lastActive == index){
7877             this.lastActive = false;
7878         }
7879         var r = this.grid.ds.getAt(index);
7880         this.selections.remove(r);
7881         if(!preventViewNotify){
7882             var view = this.grid.view ? this.grid.view : this.grid;
7883             view.onRowDeselect(index);
7884         }
7885         this.fireEvent("rowdeselect", this, index);
7886         this.fireEvent("selectionchange", this);
7887     },
7888
7889     // private
7890     restoreLast : function(){
7891         if(this._last){
7892             this.last = this._last;
7893         }
7894     },
7895
7896     // private
7897     acceptsNav : function(row, col, cm){
7898         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7899     },
7900
7901     // private
7902     onEditorKey : function(field, e){
7903         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7904         if(k == e.TAB){
7905             e.stopEvent();
7906             ed.completeEdit();
7907             if(e.shiftKey){
7908                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7909             }else{
7910                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7911             }
7912         }else if(k == e.ENTER && !e.ctrlKey){
7913             e.stopEvent();
7914             ed.completeEdit();
7915             if(e.shiftKey){
7916                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7917             }else{
7918                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7919             }
7920         }else if(k == e.ESC){
7921             ed.cancelEdit();
7922         }
7923         if(newCell){
7924             g.startEditing(newCell[0], newCell[1]);
7925         }
7926     }
7927 });/*
7928  * Based on:
7929  * Ext JS Library 1.1.1
7930  * Copyright(c) 2006-2007, Ext JS, LLC.
7931  *
7932  * Originally Released Under LGPL - original licence link has changed is not relivant.
7933  *
7934  * Fork - LGPL
7935  * <script type="text/javascript">
7936  */
7937  
7938
7939 /**
7940  * @class Roo.grid.ColumnModel
7941  * @extends Roo.util.Observable
7942  * This is the default implementation of a ColumnModel used by the Grid. It defines
7943  * the columns in the grid.
7944  * <br>Usage:<br>
7945  <pre><code>
7946  var colModel = new Roo.grid.ColumnModel([
7947         {header: "Ticker", width: 60, sortable: true, locked: true},
7948         {header: "Company Name", width: 150, sortable: true},
7949         {header: "Market Cap.", width: 100, sortable: true},
7950         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7951         {header: "Employees", width: 100, sortable: true, resizable: false}
7952  ]);
7953  </code></pre>
7954  * <p>
7955  
7956  * The config options listed for this class are options which may appear in each
7957  * individual column definition.
7958  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7959  * @constructor
7960  * @param {Object} config An Array of column config objects. See this class's
7961  * config objects for details.
7962 */
7963 Roo.grid.ColumnModel = function(config){
7964         /**
7965      * The config passed into the constructor
7966      */
7967     this.config = []; //config;
7968     this.lookup = {};
7969
7970     // if no id, create one
7971     // if the column does not have a dataIndex mapping,
7972     // map it to the order it is in the config
7973     for(var i = 0, len = config.length; i < len; i++){
7974         this.addColumn(config[i]);
7975         
7976     }
7977
7978     /**
7979      * The width of columns which have no width specified (defaults to 100)
7980      * @type Number
7981      */
7982     this.defaultWidth = 100;
7983
7984     /**
7985      * Default sortable of columns which have no sortable specified (defaults to false)
7986      * @type Boolean
7987      */
7988     this.defaultSortable = false;
7989
7990     this.addEvents({
7991         /**
7992              * @event widthchange
7993              * Fires when the width of a column changes.
7994              * @param {ColumnModel} this
7995              * @param {Number} columnIndex The column index
7996              * @param {Number} newWidth The new width
7997              */
7998             "widthchange": true,
7999         /**
8000              * @event headerchange
8001              * Fires when the text of a header changes.
8002              * @param {ColumnModel} this
8003              * @param {Number} columnIndex The column index
8004              * @param {Number} newText The new header text
8005              */
8006             "headerchange": true,
8007         /**
8008              * @event hiddenchange
8009              * Fires when a column is hidden or "unhidden".
8010              * @param {ColumnModel} this
8011              * @param {Number} columnIndex The column index
8012              * @param {Boolean} hidden true if hidden, false otherwise
8013              */
8014             "hiddenchange": true,
8015             /**
8016          * @event columnmoved
8017          * Fires when a column is moved.
8018          * @param {ColumnModel} this
8019          * @param {Number} oldIndex
8020          * @param {Number} newIndex
8021          */
8022         "columnmoved" : true,
8023         /**
8024          * @event columlockchange
8025          * Fires when a column's locked state is changed
8026          * @param {ColumnModel} this
8027          * @param {Number} colIndex
8028          * @param {Boolean} locked true if locked
8029          */
8030         "columnlockchange" : true
8031     });
8032     Roo.grid.ColumnModel.superclass.constructor.call(this);
8033 };
8034 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8035     /**
8036      * @cfg {String} header The header text to display in the Grid view.
8037      */
8038         /**
8039      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8040      */
8041         /**
8042      * @cfg {String} smHeader Header at Bootsrap Small width
8043      */
8044         /**
8045      * @cfg {String} mdHeader Header at Bootsrap Medium width
8046      */
8047         /**
8048      * @cfg {String} lgHeader Header at Bootsrap Large width
8049      */
8050         /**
8051      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8052      */
8053     /**
8054      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8055      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8056      * specified, the column's index is used as an index into the Record's data Array.
8057      */
8058     /**
8059      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8060      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8061      */
8062     /**
8063      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8064      * Defaults to the value of the {@link #defaultSortable} property.
8065      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8066      */
8067     /**
8068      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8069      */
8070     /**
8071      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8072      */
8073     /**
8074      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8075      */
8076     /**
8077      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8078      */
8079     /**
8080      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8081      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8082      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8083      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8084      */
8085        /**
8086      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8087      */
8088     /**
8089      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8090      */
8091     /**
8092      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8093      */
8094     /**
8095      * @cfg {String} cursor (Optional)
8096      */
8097     /**
8098      * @cfg {String} tooltip (Optional)
8099      */
8100     /**
8101      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8102      */
8103     /**
8104      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8105      */
8106     /**
8107      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8108      */
8109     /**
8110      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8111      */
8112         /**
8113      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8114      */
8115     /**
8116      * Returns the id of the column at the specified index.
8117      * @param {Number} index The column index
8118      * @return {String} the id
8119      */
8120     getColumnId : function(index){
8121         return this.config[index].id;
8122     },
8123
8124     /**
8125      * Returns the column for a specified id.
8126      * @param {String} id The column id
8127      * @return {Object} the column
8128      */
8129     getColumnById : function(id){
8130         return this.lookup[id];
8131     },
8132
8133     
8134     /**
8135      * Returns the column Object for a specified dataIndex.
8136      * @param {String} dataIndex The column dataIndex
8137      * @return {Object|Boolean} the column or false if not found
8138      */
8139     getColumnByDataIndex: function(dataIndex){
8140         var index = this.findColumnIndex(dataIndex);
8141         return index > -1 ? this.config[index] : false;
8142     },
8143     
8144     /**
8145      * Returns the index for a specified column id.
8146      * @param {String} id The column id
8147      * @return {Number} the index, or -1 if not found
8148      */
8149     getIndexById : function(id){
8150         for(var i = 0, len = this.config.length; i < len; i++){
8151             if(this.config[i].id == id){
8152                 return i;
8153             }
8154         }
8155         return -1;
8156     },
8157     
8158     /**
8159      * Returns the index for a specified column dataIndex.
8160      * @param {String} dataIndex The column dataIndex
8161      * @return {Number} the index, or -1 if not found
8162      */
8163     
8164     findColumnIndex : function(dataIndex){
8165         for(var i = 0, len = this.config.length; i < len; i++){
8166             if(this.config[i].dataIndex == dataIndex){
8167                 return i;
8168             }
8169         }
8170         return -1;
8171     },
8172     
8173     
8174     moveColumn : function(oldIndex, newIndex){
8175         var c = this.config[oldIndex];
8176         this.config.splice(oldIndex, 1);
8177         this.config.splice(newIndex, 0, c);
8178         this.dataMap = null;
8179         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8180     },
8181
8182     isLocked : function(colIndex){
8183         return this.config[colIndex].locked === true;
8184     },
8185
8186     setLocked : function(colIndex, value, suppressEvent){
8187         if(this.isLocked(colIndex) == value){
8188             return;
8189         }
8190         this.config[colIndex].locked = value;
8191         if(!suppressEvent){
8192             this.fireEvent("columnlockchange", this, colIndex, value);
8193         }
8194     },
8195
8196     getTotalLockedWidth : function(){
8197         var totalWidth = 0;
8198         for(var i = 0; i < this.config.length; i++){
8199             if(this.isLocked(i) && !this.isHidden(i)){
8200                 this.totalWidth += this.getColumnWidth(i);
8201             }
8202         }
8203         return totalWidth;
8204     },
8205
8206     getLockedCount : function(){
8207         for(var i = 0, len = this.config.length; i < len; i++){
8208             if(!this.isLocked(i)){
8209                 return i;
8210             }
8211         }
8212         
8213         return this.config.length;
8214     },
8215
8216     /**
8217      * Returns the number of columns.
8218      * @return {Number}
8219      */
8220     getColumnCount : function(visibleOnly){
8221         if(visibleOnly === true){
8222             var c = 0;
8223             for(var i = 0, len = this.config.length; i < len; i++){
8224                 if(!this.isHidden(i)){
8225                     c++;
8226                 }
8227             }
8228             return c;
8229         }
8230         return this.config.length;
8231     },
8232
8233     /**
8234      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8235      * @param {Function} fn
8236      * @param {Object} scope (optional)
8237      * @return {Array} result
8238      */
8239     getColumnsBy : function(fn, scope){
8240         var r = [];
8241         for(var i = 0, len = this.config.length; i < len; i++){
8242             var c = this.config[i];
8243             if(fn.call(scope||this, c, i) === true){
8244                 r[r.length] = c;
8245             }
8246         }
8247         return r;
8248     },
8249
8250     /**
8251      * Returns true if the specified column is sortable.
8252      * @param {Number} col The column index
8253      * @return {Boolean}
8254      */
8255     isSortable : function(col){
8256         if(typeof this.config[col].sortable == "undefined"){
8257             return this.defaultSortable;
8258         }
8259         return this.config[col].sortable;
8260     },
8261
8262     /**
8263      * Returns the rendering (formatting) function defined for the column.
8264      * @param {Number} col The column index.
8265      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8266      */
8267     getRenderer : function(col){
8268         if(!this.config[col].renderer){
8269             return Roo.grid.ColumnModel.defaultRenderer;
8270         }
8271         return this.config[col].renderer;
8272     },
8273
8274     /**
8275      * Sets the rendering (formatting) function for a column.
8276      * @param {Number} col The column index
8277      * @param {Function} fn The function to use to process the cell's raw data
8278      * to return HTML markup for the grid view. The render function is called with
8279      * the following parameters:<ul>
8280      * <li>Data value.</li>
8281      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8282      * <li>css A CSS style string to apply to the table cell.</li>
8283      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8284      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8285      * <li>Row index</li>
8286      * <li>Column index</li>
8287      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8288      */
8289     setRenderer : function(col, fn){
8290         this.config[col].renderer = fn;
8291     },
8292
8293     /**
8294      * Returns the width for the specified column.
8295      * @param {Number} col The column index
8296      * @param (optional) {String} gridSize bootstrap width size.
8297      * @return {Number}
8298      */
8299     getColumnWidth : function(col, gridSize)
8300         {
8301                 var cfg = this.config[col];
8302                 
8303                 if (typeof(gridSize) == 'undefined') {
8304                         return cfg.width * 1 || this.defaultWidth;
8305                 }
8306                 if (gridSize === false) { // if we set it..
8307                         return cfg.width || false;
8308                 }
8309                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8310                 
8311                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8312                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8313                                 continue;
8314                         }
8315                         return cfg[ sizes[i] ];
8316                 }
8317                 return 1;
8318                 
8319     },
8320
8321     /**
8322      * Sets the width for a column.
8323      * @param {Number} col The column index
8324      * @param {Number} width The new width
8325      */
8326     setColumnWidth : function(col, width, suppressEvent){
8327         this.config[col].width = width;
8328         this.totalWidth = null;
8329         if(!suppressEvent){
8330              this.fireEvent("widthchange", this, col, width);
8331         }
8332     },
8333
8334     /**
8335      * Returns the total width of all columns.
8336      * @param {Boolean} includeHidden True to include hidden column widths
8337      * @return {Number}
8338      */
8339     getTotalWidth : function(includeHidden){
8340         if(!this.totalWidth){
8341             this.totalWidth = 0;
8342             for(var i = 0, len = this.config.length; i < len; i++){
8343                 if(includeHidden || !this.isHidden(i)){
8344                     this.totalWidth += this.getColumnWidth(i);
8345                 }
8346             }
8347         }
8348         return this.totalWidth;
8349     },
8350
8351     /**
8352      * Returns the header for the specified column.
8353      * @param {Number} col The column index
8354      * @return {String}
8355      */
8356     getColumnHeader : function(col){
8357         return this.config[col].header;
8358     },
8359
8360     /**
8361      * Sets the header for a column.
8362      * @param {Number} col The column index
8363      * @param {String} header The new header
8364      */
8365     setColumnHeader : function(col, header){
8366         this.config[col].header = header;
8367         this.fireEvent("headerchange", this, col, header);
8368     },
8369
8370     /**
8371      * Returns the tooltip for the specified column.
8372      * @param {Number} col The column index
8373      * @return {String}
8374      */
8375     getColumnTooltip : function(col){
8376             return this.config[col].tooltip;
8377     },
8378     /**
8379      * Sets the tooltip for a column.
8380      * @param {Number} col The column index
8381      * @param {String} tooltip The new tooltip
8382      */
8383     setColumnTooltip : function(col, tooltip){
8384             this.config[col].tooltip = tooltip;
8385     },
8386
8387     /**
8388      * Returns the dataIndex for the specified column.
8389      * @param {Number} col The column index
8390      * @return {Number}
8391      */
8392     getDataIndex : function(col){
8393         return this.config[col].dataIndex;
8394     },
8395
8396     /**
8397      * Sets the dataIndex for a column.
8398      * @param {Number} col The column index
8399      * @param {Number} dataIndex The new dataIndex
8400      */
8401     setDataIndex : function(col, dataIndex){
8402         this.config[col].dataIndex = dataIndex;
8403     },
8404
8405     
8406     
8407     /**
8408      * Returns true if the cell is editable.
8409      * @param {Number} colIndex The column index
8410      * @param {Number} rowIndex The row index - this is nto actually used..?
8411      * @return {Boolean}
8412      */
8413     isCellEditable : function(colIndex, rowIndex){
8414         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8415     },
8416
8417     /**
8418      * Returns the editor defined for the cell/column.
8419      * return false or null to disable editing.
8420      * @param {Number} colIndex The column index
8421      * @param {Number} rowIndex The row index
8422      * @return {Object}
8423      */
8424     getCellEditor : function(colIndex, rowIndex){
8425         return this.config[colIndex].editor;
8426     },
8427
8428     /**
8429      * Sets if a column is editable.
8430      * @param {Number} col The column index
8431      * @param {Boolean} editable True if the column is editable
8432      */
8433     setEditable : function(col, editable){
8434         this.config[col].editable = editable;
8435     },
8436
8437
8438     /**
8439      * Returns true if the column is hidden.
8440      * @param {Number} colIndex The column index
8441      * @return {Boolean}
8442      */
8443     isHidden : function(colIndex){
8444         return this.config[colIndex].hidden;
8445     },
8446
8447
8448     /**
8449      * Returns true if the column width cannot be changed
8450      */
8451     isFixed : function(colIndex){
8452         return this.config[colIndex].fixed;
8453     },
8454
8455     /**
8456      * Returns true if the column can be resized
8457      * @return {Boolean}
8458      */
8459     isResizable : function(colIndex){
8460         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8461     },
8462     /**
8463      * Sets if a column is hidden.
8464      * @param {Number} colIndex The column index
8465      * @param {Boolean} hidden True if the column is hidden
8466      */
8467     setHidden : function(colIndex, hidden){
8468         this.config[colIndex].hidden = hidden;
8469         this.totalWidth = null;
8470         this.fireEvent("hiddenchange", this, colIndex, hidden);
8471     },
8472
8473     /**
8474      * Sets the editor for a column.
8475      * @param {Number} col The column index
8476      * @param {Object} editor The editor object
8477      */
8478     setEditor : function(col, editor){
8479         this.config[col].editor = editor;
8480     },
8481     /**
8482      * Add a column (experimental...) - defaults to adding to the end..
8483      * @param {Object} config 
8484     */
8485     addColumn : function(c)
8486     {
8487     
8488         var i = this.config.length;
8489         this.config[i] = c;
8490         
8491         if(typeof c.dataIndex == "undefined"){
8492             c.dataIndex = i;
8493         }
8494         if(typeof c.renderer == "string"){
8495             c.renderer = Roo.util.Format[c.renderer];
8496         }
8497         if(typeof c.id == "undefined"){
8498             c.id = Roo.id();
8499         }
8500         if(c.editor && c.editor.xtype){
8501             c.editor  = Roo.factory(c.editor, Roo.grid);
8502         }
8503         if(c.editor && c.editor.isFormField){
8504             c.editor = new Roo.grid.GridEditor(c.editor);
8505         }
8506         this.lookup[c.id] = c;
8507     }
8508     
8509 });
8510
8511 Roo.grid.ColumnModel.defaultRenderer = function(value)
8512 {
8513     if(typeof value == "object") {
8514         return value;
8515     }
8516         if(typeof value == "string" && value.length < 1){
8517             return "&#160;";
8518         }
8519     
8520         return String.format("{0}", value);
8521 };
8522
8523 // Alias for backwards compatibility
8524 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8525 /*
8526  * Based on:
8527  * Ext JS Library 1.1.1
8528  * Copyright(c) 2006-2007, Ext JS, LLC.
8529  *
8530  * Originally Released Under LGPL - original licence link has changed is not relivant.
8531  *
8532  * Fork - LGPL
8533  * <script type="text/javascript">
8534  */
8535  
8536 /**
8537  * @class Roo.LoadMask
8538  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8539  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8540  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8541  * element's UpdateManager load indicator and will be destroyed after the initial load.
8542  * @constructor
8543  * Create a new LoadMask
8544  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8545  * @param {Object} config The config object
8546  */
8547 Roo.LoadMask = function(el, config){
8548     this.el = Roo.get(el);
8549     Roo.apply(this, config);
8550     if(this.store){
8551         this.store.on('beforeload', this.onBeforeLoad, this);
8552         this.store.on('load', this.onLoad, this);
8553         this.store.on('loadexception', this.onLoadException, this);
8554         this.removeMask = false;
8555     }else{
8556         var um = this.el.getUpdateManager();
8557         um.showLoadIndicator = false; // disable the default indicator
8558         um.on('beforeupdate', this.onBeforeLoad, this);
8559         um.on('update', this.onLoad, this);
8560         um.on('failure', this.onLoad, this);
8561         this.removeMask = true;
8562     }
8563 };
8564
8565 Roo.LoadMask.prototype = {
8566     /**
8567      * @cfg {Boolean} removeMask
8568      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8569      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8570      */
8571     removeMask : false,
8572     /**
8573      * @cfg {String} msg
8574      * The text to display in a centered loading message box (defaults to 'Loading...')
8575      */
8576     msg : 'Loading...',
8577     /**
8578      * @cfg {String} msgCls
8579      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8580      */
8581     msgCls : 'x-mask-loading',
8582
8583     /**
8584      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8585      * @type Boolean
8586      */
8587     disabled: false,
8588
8589     /**
8590      * Disables the mask to prevent it from being displayed
8591      */
8592     disable : function(){
8593        this.disabled = true;
8594     },
8595
8596     /**
8597      * Enables the mask so that it can be displayed
8598      */
8599     enable : function(){
8600         this.disabled = false;
8601     },
8602     
8603     onLoadException : function()
8604     {
8605         Roo.log(arguments);
8606         
8607         if (typeof(arguments[3]) != 'undefined') {
8608             Roo.MessageBox.alert("Error loading",arguments[3]);
8609         } 
8610         /*
8611         try {
8612             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8613                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8614             }   
8615         } catch(e) {
8616             
8617         }
8618         */
8619     
8620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8621     },
8622     // private
8623     onLoad : function()
8624     {
8625         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8626     },
8627
8628     // private
8629     onBeforeLoad : function(){
8630         if(!this.disabled){
8631             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8632         }
8633     },
8634
8635     // private
8636     destroy : function(){
8637         if(this.store){
8638             this.store.un('beforeload', this.onBeforeLoad, this);
8639             this.store.un('load', this.onLoad, this);
8640             this.store.un('loadexception', this.onLoadException, this);
8641         }else{
8642             var um = this.el.getUpdateManager();
8643             um.un('beforeupdate', this.onBeforeLoad, this);
8644             um.un('update', this.onLoad, this);
8645             um.un('failure', this.onLoad, this);
8646         }
8647     }
8648 };/**
8649  * @class Roo.bootstrap.Table
8650  * @licence LGBL
8651  * @extends Roo.bootstrap.Component
8652  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8653  * Similar to Roo.grid.Grid
8654  * <pre><code>
8655  var table = Roo.factory({
8656     xtype : 'Table',
8657     xns : Roo.bootstrap,
8658     autoSizeColumns: true,
8659     
8660     
8661     store : {
8662         xtype : 'Store',
8663         xns : Roo.data,
8664         remoteSort : true,
8665         sortInfo : { direction : 'ASC', field: 'name' },
8666         proxy : {
8667            xtype : 'HttpProxy',
8668            xns : Roo.data,
8669            method : 'GET',
8670            url : 'https://example.com/some.data.url.json'
8671         },
8672         reader : {
8673            xtype : 'JsonReader',
8674            xns : Roo.data,
8675            fields : [ 'id', 'name', whatever' ],
8676            id : 'id',
8677            root : 'data'
8678         }
8679     },
8680     cm : [
8681         {
8682             xtype : 'ColumnModel',
8683             xns : Roo.grid,
8684             align : 'center',
8685             cursor : 'pointer',
8686             dataIndex : 'is_in_group',
8687             header : "Name",
8688             sortable : true,
8689             renderer : function(v, x , r) {  
8690             
8691                 return String.format("{0}", v)
8692             }
8693             width : 3
8694         } // more columns..
8695     ],
8696     selModel : {
8697         xtype : 'RowSelectionModel',
8698         xns : Roo.bootstrap.Table
8699         // you can add listeners to catch selection change here....
8700     }
8701      
8702
8703  });
8704  // set any options
8705  grid.render(Roo.get("some-div"));
8706 </code></pre>
8707
8708 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8709
8710
8711
8712  *
8713  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8714  * @cfg {Roo.data.Store} store The data store to use
8715  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8716  * 
8717  * @cfg {String} cls table class
8718  *
8719  * 
8720  * @cfg {boolean} striped Should the rows be alternative striped
8721  * @cfg {boolean} bordered Add borders to the table
8722  * @cfg {boolean} hover Add hover highlighting
8723  * @cfg {boolean} condensed Format condensed
8724  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
8725  *                also adds table-responsive (see bootstrap docs for details)
8726  * @cfg {Boolean} loadMask (true|false) default false
8727  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8728  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8729  * @cfg {Boolean} rowSelection (true|false) default false
8730  * @cfg {Boolean} cellSelection (true|false) default false
8731  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8732  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8733  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8734  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8735  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8736  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8737  * 
8738  * @constructor
8739  * Create a new Table
8740  * @param {Object} config The config object
8741  */
8742
8743 Roo.bootstrap.Table = function(config)
8744 {
8745     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8746      
8747     // BC...
8748     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8749     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8750     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8751     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8752     
8753     this.view = this; // compat with grid.
8754     
8755     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8756     if (this.sm) {
8757         this.sm.grid = this;
8758         this.selModel = Roo.factory(this.sm, Roo.grid);
8759         this.sm = this.selModel;
8760         this.sm.xmodule = this.xmodule || false;
8761     }
8762     
8763     if (this.cm && typeof(this.cm.config) == 'undefined') {
8764         this.colModel = new Roo.grid.ColumnModel(this.cm);
8765         this.cm = this.colModel;
8766         this.cm.xmodule = this.xmodule || false;
8767     }
8768     if (this.store) {
8769         this.store= Roo.factory(this.store, Roo.data);
8770         this.ds = this.store;
8771         this.ds.xmodule = this.xmodule || false;
8772          
8773     }
8774     if (this.footer && this.store) {
8775         this.footer.dataSource = this.ds;
8776         this.footer = Roo.factory(this.footer);
8777     }
8778     
8779     /** @private */
8780     this.addEvents({
8781         /**
8782          * @event cellclick
8783          * Fires when a cell is clicked
8784          * @param {Roo.bootstrap.Table} this
8785          * @param {Roo.Element} el
8786          * @param {Number} rowIndex
8787          * @param {Number} columnIndex
8788          * @param {Roo.EventObject} e
8789          */
8790         "cellclick" : true,
8791         /**
8792          * @event celldblclick
8793          * Fires when a cell is double clicked
8794          * @param {Roo.bootstrap.Table} this
8795          * @param {Roo.Element} el
8796          * @param {Number} rowIndex
8797          * @param {Number} columnIndex
8798          * @param {Roo.EventObject} e
8799          */
8800         "celldblclick" : true,
8801         /**
8802          * @event rowclick
8803          * Fires when a row is clicked
8804          * @param {Roo.bootstrap.Table} this
8805          * @param {Roo.Element} el
8806          * @param {Number} rowIndex
8807          * @param {Roo.EventObject} e
8808          */
8809         "rowclick" : true,
8810         /**
8811          * @event rowdblclick
8812          * Fires when a row is double clicked
8813          * @param {Roo.bootstrap.Table} this
8814          * @param {Roo.Element} el
8815          * @param {Number} rowIndex
8816          * @param {Roo.EventObject} e
8817          */
8818         "rowdblclick" : true,
8819         /**
8820          * @event mouseover
8821          * Fires when a mouseover occur
8822          * @param {Roo.bootstrap.Table} this
8823          * @param {Roo.Element} el
8824          * @param {Number} rowIndex
8825          * @param {Number} columnIndex
8826          * @param {Roo.EventObject} e
8827          */
8828         "mouseover" : true,
8829         /**
8830          * @event mouseout
8831          * Fires when a mouseout occur
8832          * @param {Roo.bootstrap.Table} this
8833          * @param {Roo.Element} el
8834          * @param {Number} rowIndex
8835          * @param {Number} columnIndex
8836          * @param {Roo.EventObject} e
8837          */
8838         "mouseout" : true,
8839         /**
8840          * @event rowclass
8841          * Fires when a row is rendered, so you can change add a style to it.
8842          * @param {Roo.bootstrap.Table} this
8843          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8844          */
8845         'rowclass' : true,
8846           /**
8847          * @event rowsrendered
8848          * Fires when all the  rows have been rendered
8849          * @param {Roo.bootstrap.Table} this
8850          */
8851         'rowsrendered' : true,
8852         /**
8853          * @event contextmenu
8854          * The raw contextmenu event for the entire grid.
8855          * @param {Roo.EventObject} e
8856          */
8857         "contextmenu" : true,
8858         /**
8859          * @event rowcontextmenu
8860          * Fires when a row is right clicked
8861          * @param {Roo.bootstrap.Table} this
8862          * @param {Number} rowIndex
8863          * @param {Roo.EventObject} e
8864          */
8865         "rowcontextmenu" : true,
8866         /**
8867          * @event cellcontextmenu
8868          * Fires when a cell is right clicked
8869          * @param {Roo.bootstrap.Table} this
8870          * @param {Number} rowIndex
8871          * @param {Number} cellIndex
8872          * @param {Roo.EventObject} e
8873          */
8874          "cellcontextmenu" : true,
8875          /**
8876          * @event headercontextmenu
8877          * Fires when a header is right clicked
8878          * @param {Roo.bootstrap.Table} this
8879          * @param {Number} columnIndex
8880          * @param {Roo.EventObject} e
8881          */
8882         "headercontextmenu" : true,
8883         /**
8884          * @event mousedown
8885          * The raw mousedown event for the entire grid.
8886          * @param {Roo.EventObject} e
8887          */
8888         "mousedown" : true
8889         
8890     });
8891 };
8892
8893 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8894     
8895     cls: false,
8896     
8897     striped : false,
8898     scrollBody : false,
8899     bordered: false,
8900     hover:  false,
8901     condensed : false,
8902     responsive : false,
8903     sm : false,
8904     cm : false,
8905     store : false,
8906     loadMask : false,
8907     footerShow : true,
8908     headerShow : true,
8909     enableColumnResize: true,
8910   
8911     rowSelection : false,
8912     cellSelection : false,
8913     layout : false,
8914
8915     minColumnWidth : 50,
8916     
8917     // Roo.Element - the tbody
8918     bodyEl: false,  // <tbody> Roo.Element - thead element    
8919     headEl: false,  // <thead> Roo.Element - thead element
8920     resizeProxy : false, // proxy element for dragging?
8921
8922
8923     
8924     container: false, // used by gridpanel...
8925     
8926     lazyLoad : false,
8927     
8928     CSS : Roo.util.CSS,
8929     
8930     auto_hide_footer : false,
8931     
8932     view: false, // actually points to this..
8933     
8934     getAutoCreate : function()
8935     {
8936         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8937         
8938         cfg = {
8939             tag: 'table',
8940             cls : 'table', 
8941             cn : []
8942         };
8943         // this get's auto added by panel.Grid
8944         if (this.scrollBody) {
8945             cfg.cls += ' table-body-fixed';
8946         }    
8947         if (this.striped) {
8948             cfg.cls += ' table-striped';
8949         }
8950         
8951         if (this.hover) {
8952             cfg.cls += ' table-hover';
8953         }
8954         if (this.bordered) {
8955             cfg.cls += ' table-bordered';
8956         }
8957         if (this.condensed) {
8958             cfg.cls += ' table-condensed';
8959         }
8960         
8961         if (this.responsive) {
8962             cfg.cls += ' table-responsive';
8963         }
8964         
8965         if (this.cls) {
8966             cfg.cls+=  ' ' +this.cls;
8967         }
8968         
8969         
8970         
8971         if (this.layout) {
8972             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8973         }
8974         
8975         if(this.store || this.cm){
8976             if(this.headerShow){
8977                 cfg.cn.push(this.renderHeader());
8978             }
8979             
8980             cfg.cn.push(this.renderBody());
8981             
8982             if(this.footerShow){
8983                 cfg.cn.push(this.renderFooter());
8984             }
8985             // where does this come from?
8986             //cfg.cls+=  ' TableGrid';
8987         }
8988         
8989         return { cn : [ cfg ] };
8990     },
8991     
8992     initEvents : function()
8993     {   
8994         if(!this.store || !this.cm){
8995             return;
8996         }
8997         if (this.selModel) {
8998             this.selModel.initEvents();
8999         }
9000         
9001         
9002         //Roo.log('initEvents with ds!!!!');
9003         
9004         this.bodyEl = this.el.select('tbody', true).first();
9005         this.headEl = this.el.select('thead', true).first();
9006         this.mainFoot = this.el.select('tfoot', true).first();
9007         
9008         
9009         
9010         
9011         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9012             e.on('click', this.sort, this);
9013         }, this);
9014         
9015         
9016         // why is this done????? = it breaks dialogs??
9017         //this.parent().el.setStyle('position', 'relative');
9018         
9019         
9020         if (this.footer) {
9021             this.footer.parentId = this.id;
9022             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9023             
9024             if(this.lazyLoad){
9025                 this.el.select('tfoot tr td').first().addClass('hide');
9026             }
9027         } 
9028         
9029         if(this.loadMask) {
9030             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9031         }
9032         
9033         this.store.on('load', this.onLoad, this);
9034         this.store.on('beforeload', this.onBeforeLoad, this);
9035         this.store.on('update', this.onUpdate, this);
9036         this.store.on('add', this.onAdd, this);
9037         this.store.on("clear", this.clear, this);
9038         
9039         this.el.on("contextmenu", this.onContextMenu, this);
9040         
9041         
9042         this.cm.on("headerchange", this.onHeaderChange, this);
9043         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9044
9045  //?? does bodyEl get replaced on render?
9046         this.bodyEl.on("click", this.onClick, this);
9047         this.bodyEl.on("dblclick", this.onDblClick, this);        
9048         this.bodyEl.on('scroll', this.onBodyScroll, this);
9049
9050         // guessing mainbody will work - this relays usually caught by selmodel at present.
9051         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9052   
9053   
9054         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9055         
9056   
9057         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9058             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9059         }
9060         
9061         this.initCSS();
9062     },
9063     // Compatibility with grid - we implement all the view features at present.
9064     getView : function()
9065     {
9066         return this;
9067     },
9068     
9069     initCSS : function()
9070     {
9071         
9072         
9073         var cm = this.cm, styles = [];
9074         this.CSS.removeStyleSheet(this.id + '-cssrules');
9075         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9076         // we can honour xs/sm/md/xl  as widths...
9077         // we first have to decide what widht we are currently at...
9078         var sz = Roo.getGridSize();
9079         
9080         var total = 0;
9081         var last = -1;
9082         var cols = []; // visable cols.
9083         var total_abs = 0;
9084         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9085             var w = cm.getColumnWidth(i, false);
9086             if(cm.isHidden(i)){
9087                 cols.push( { rel : false, abs : 0 });
9088                 continue;
9089             }
9090             if (w !== false) {
9091                 cols.push( { rel : false, abs : w });
9092                 total_abs += w;
9093                 last = i; // not really..
9094                 continue;
9095             }
9096             var w = cm.getColumnWidth(i, sz);
9097             if (w > 0) {
9098                 last = i
9099             }
9100             total += w;
9101             cols.push( { rel : w, abs : false });
9102         }
9103         
9104         var avail = this.bodyEl.dom.clientWidth - total_abs;
9105         
9106         var unitWidth = Math.floor(avail / total);
9107         var rem = avail - (unitWidth * total);
9108         
9109         var hidden, width, pos = 0 , splithide , left;
9110         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9111             
9112             hidden = 'display:none;';
9113             left = '';
9114             width  = 'width:0px;';
9115             splithide = '';
9116             if(!cm.isHidden(i)){
9117                 hidden = '';
9118                 
9119                 
9120                 // we can honour xs/sm/md/xl ?
9121                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9122                 if (w===0) {
9123                     hidden = 'display:none;';
9124                 }
9125                 // width should return a small number...
9126                 if (i == last) {
9127                     w+=rem; // add the remaining with..
9128                 }
9129                 pos += w;
9130                 left = "left:" + (pos -4) + "px;";
9131                 width = "width:" + w+ "px;";
9132                 
9133             }
9134             if (this.responsive) {
9135                 width = '';
9136                 left = '';
9137                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9138                 splithide = 'display: none;';
9139             }
9140             
9141             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9142             if (this.headEl) {
9143                 if (i == last) {
9144                     splithide = 'display:none;';
9145                 }
9146                 
9147                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9148                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9149                 );
9150             }
9151             
9152         }
9153         //Roo.log(styles.join(''));
9154         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9155         
9156     },
9157     
9158     
9159     
9160     onContextMenu : function(e, t)
9161     {
9162         this.processEvent("contextmenu", e);
9163     },
9164     
9165     processEvent : function(name, e)
9166     {
9167         if (name != 'touchstart' ) {
9168             this.fireEvent(name, e);    
9169         }
9170         
9171         var t = e.getTarget();
9172         
9173         var cell = Roo.get(t);
9174         
9175         if(!cell){
9176             return;
9177         }
9178         
9179         if(cell.findParent('tfoot', false, true)){
9180             return;
9181         }
9182         
9183         if(cell.findParent('thead', false, true)){
9184             
9185             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9186                 cell = Roo.get(t).findParent('th', false, true);
9187                 if (!cell) {
9188                     Roo.log("failed to find th in thead?");
9189                     Roo.log(e.getTarget());
9190                     return;
9191                 }
9192             }
9193             
9194             var cellIndex = cell.dom.cellIndex;
9195             
9196             var ename = name == 'touchstart' ? 'click' : name;
9197             this.fireEvent("header" + ename, this, cellIndex, e);
9198             
9199             return;
9200         }
9201         
9202         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9203             cell = Roo.get(t).findParent('td', false, true);
9204             if (!cell) {
9205                 Roo.log("failed to find th in tbody?");
9206                 Roo.log(e.getTarget());
9207                 return;
9208             }
9209         }
9210         
9211         var row = cell.findParent('tr', false, true);
9212         var cellIndex = cell.dom.cellIndex;
9213         var rowIndex = row.dom.rowIndex - 1;
9214         
9215         if(row !== false){
9216             
9217             this.fireEvent("row" + name, this, rowIndex, e);
9218             
9219             if(cell !== false){
9220             
9221                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9222             }
9223         }
9224         
9225     },
9226     
9227     onMouseover : function(e, el)
9228     {
9229         var cell = Roo.get(el);
9230         
9231         if(!cell){
9232             return;
9233         }
9234         
9235         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9236             cell = cell.findParent('td', false, true);
9237         }
9238         
9239         var row = cell.findParent('tr', false, true);
9240         var cellIndex = cell.dom.cellIndex;
9241         var rowIndex = row.dom.rowIndex - 1; // start from 0
9242         
9243         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9244         
9245     },
9246     
9247     onMouseout : function(e, el)
9248     {
9249         var cell = Roo.get(el);
9250         
9251         if(!cell){
9252             return;
9253         }
9254         
9255         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9256             cell = cell.findParent('td', false, true);
9257         }
9258         
9259         var row = cell.findParent('tr', false, true);
9260         var cellIndex = cell.dom.cellIndex;
9261         var rowIndex = row.dom.rowIndex - 1; // start from 0
9262         
9263         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9264         
9265     },
9266     
9267     onClick : function(e, el)
9268     {
9269         var cell = Roo.get(el);
9270         
9271         if(!cell || (!this.cellSelection && !this.rowSelection)){
9272             return;
9273         }
9274         
9275         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9276             cell = cell.findParent('td', false, true);
9277         }
9278         
9279         if(!cell || typeof(cell) == 'undefined'){
9280             return;
9281         }
9282         
9283         var row = cell.findParent('tr', false, true);
9284         
9285         if(!row || typeof(row) == 'undefined'){
9286             return;
9287         }
9288         
9289         var cellIndex = cell.dom.cellIndex;
9290         var rowIndex = this.getRowIndex(row);
9291         
9292         // why??? - should these not be based on SelectionModel?
9293         //if(this.cellSelection){
9294             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9295         //}
9296         
9297         //if(this.rowSelection){
9298             this.fireEvent('rowclick', this, row, rowIndex, e);
9299         //}
9300          
9301     },
9302         
9303     onDblClick : function(e,el)
9304     {
9305         var cell = Roo.get(el);
9306         
9307         if(!cell || (!this.cellSelection && !this.rowSelection)){
9308             return;
9309         }
9310         
9311         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9312             cell = cell.findParent('td', false, true);
9313         }
9314         
9315         if(!cell || typeof(cell) == 'undefined'){
9316             return;
9317         }
9318         
9319         var row = cell.findParent('tr', false, true);
9320         
9321         if(!row || typeof(row) == 'undefined'){
9322             return;
9323         }
9324         
9325         var cellIndex = cell.dom.cellIndex;
9326         var rowIndex = this.getRowIndex(row);
9327         
9328         if(this.cellSelection){
9329             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9330         }
9331         
9332         if(this.rowSelection){
9333             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9334         }
9335     },
9336     findRowIndex : function(el)
9337     {
9338         var cell = Roo.get(el);
9339         if(!cell) {
9340             return false;
9341         }
9342         var row = cell.findParent('tr', false, true);
9343         
9344         if(!row || typeof(row) == 'undefined'){
9345             return false;
9346         }
9347         return this.getRowIndex(row);
9348     },
9349     sort : function(e,el)
9350     {
9351         var col = Roo.get(el);
9352         
9353         if(!col.hasClass('sortable')){
9354             return;
9355         }
9356         
9357         var sort = col.attr('sort');
9358         var dir = 'ASC';
9359         
9360         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9361             dir = 'DESC';
9362         }
9363         
9364         this.store.sortInfo = {field : sort, direction : dir};
9365         
9366         if (this.footer) {
9367             Roo.log("calling footer first");
9368             this.footer.onClick('first');
9369         } else {
9370         
9371             this.store.load({ params : { start : 0 } });
9372         }
9373     },
9374     
9375     renderHeader : function()
9376     {
9377         var header = {
9378             tag: 'thead',
9379             cn : []
9380         };
9381         
9382         var cm = this.cm;
9383         this.totalWidth = 0;
9384         
9385         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9386             
9387             var config = cm.config[i];
9388             
9389             var c = {
9390                 tag: 'th',
9391                 cls : 'x-hcol-' + i,
9392                 style : '',
9393                 
9394                 html: cm.getColumnHeader(i)
9395             };
9396             
9397             var tooltip = cm.getColumnTooltip(i);
9398             if (tooltip) {
9399                 c.tooltip = tooltip;
9400             }
9401             
9402             
9403             var hh = '';
9404             
9405             if(typeof(config.sortable) != 'undefined' && config.sortable){
9406                 c.cls += ' sortable';
9407                 c.html = '<i class="fa"></i>' + c.html;
9408             }
9409             
9410             // could use BS4 hidden-..-down 
9411             
9412             if(typeof(config.lgHeader) != 'undefined'){
9413                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9414             }
9415             
9416             if(typeof(config.mdHeader) != 'undefined'){
9417                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9418             }
9419             
9420             if(typeof(config.smHeader) != 'undefined'){
9421                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9422             }
9423             
9424             if(typeof(config.xsHeader) != 'undefined'){
9425                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9426             }
9427             
9428             if(hh.length){
9429                 c.html = hh;
9430             }
9431             
9432             if(typeof(config.tooltip) != 'undefined'){
9433                 c.tooltip = config.tooltip;
9434             }
9435             
9436             if(typeof(config.colspan) != 'undefined'){
9437                 c.colspan = config.colspan;
9438             }
9439             
9440             // hidden is handled by CSS now
9441             
9442             if(typeof(config.dataIndex) != 'undefined'){
9443                 c.sort = config.dataIndex;
9444             }
9445             
9446            
9447             
9448             if(typeof(config.align) != 'undefined' && config.align.length){
9449                 c.style += ' text-align:' + config.align + ';';
9450             }
9451             
9452             /* width is done in CSS
9453              *if(typeof(config.width) != 'undefined'){
9454                 c.style += ' width:' + config.width + 'px;';
9455                 this.totalWidth += config.width;
9456             } else {
9457                 this.totalWidth += 100; // assume minimum of 100 per column?
9458             }
9459             */
9460             
9461             if(typeof(config.cls) != 'undefined'){
9462                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9463             }
9464             // this is the bit that doesnt reall work at all...
9465             
9466             if (this.responsive) {
9467                  
9468             
9469                 ['xs','sm','md','lg'].map(function(size){
9470                     
9471                     if(typeof(config[size]) == 'undefined'){
9472                         return;
9473                     }
9474                      
9475                     if (!config[size]) { // 0 = hidden
9476                         // BS 4 '0' is treated as hide that column and below.
9477                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9478                         return;
9479                     }
9480                     
9481                     c.cls += ' col-' + size + '-' + config[size] + (
9482                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9483                     );
9484                     
9485                     
9486                 });
9487             }
9488             // at the end?
9489             
9490             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9491             
9492             
9493             
9494             
9495             header.cn.push(c)
9496         }
9497         
9498         return header;
9499     },
9500     
9501     renderBody : function()
9502     {
9503         var body = {
9504             tag: 'tbody',
9505             cn : [
9506                 {
9507                     tag: 'tr',
9508                     cn : [
9509                         {
9510                             tag : 'td',
9511                             colspan :  this.cm.getColumnCount()
9512                         }
9513                     ]
9514                 }
9515             ]
9516         };
9517         
9518         return body;
9519     },
9520     
9521     renderFooter : function()
9522     {
9523         var footer = {
9524             tag: 'tfoot',
9525             cn : [
9526                 {
9527                     tag: 'tr',
9528                     cn : [
9529                         {
9530                             tag : 'td',
9531                             colspan :  this.cm.getColumnCount()
9532                         }
9533                     ]
9534                 }
9535             ]
9536         };
9537         
9538         return footer;
9539     },
9540     
9541     
9542     
9543     onLoad : function()
9544     {
9545 //        Roo.log('ds onload');
9546         this.clear();
9547         
9548         var _this = this;
9549         var cm = this.cm;
9550         var ds = this.store;
9551         
9552         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9553             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9554             if (_this.store.sortInfo) {
9555                     
9556                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9557                     e.select('i', true).addClass(['fa-arrow-up']);
9558                 }
9559                 
9560                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9561                     e.select('i', true).addClass(['fa-arrow-down']);
9562                 }
9563             }
9564         });
9565         
9566         var tbody =  this.bodyEl;
9567               
9568         if(ds.getCount() > 0){
9569             ds.data.each(function(d,rowIndex){
9570                 var row =  this.renderRow(cm, ds, rowIndex);
9571                 
9572                 tbody.createChild(row);
9573                 
9574                 var _this = this;
9575                 
9576                 if(row.cellObjects.length){
9577                     Roo.each(row.cellObjects, function(r){
9578                         _this.renderCellObject(r);
9579                     })
9580                 }
9581                 
9582             }, this);
9583         }
9584         
9585         var tfoot = this.el.select('tfoot', true).first();
9586         
9587         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9588             
9589             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9590             
9591             var total = this.ds.getTotalCount();
9592             
9593             if(this.footer.pageSize < total){
9594                 this.mainFoot.show();
9595             }
9596         }
9597         
9598         Roo.each(this.el.select('tbody td', true).elements, function(e){
9599             e.on('mouseover', _this.onMouseover, _this);
9600         });
9601         
9602         Roo.each(this.el.select('tbody td', true).elements, function(e){
9603             e.on('mouseout', _this.onMouseout, _this);
9604         });
9605         this.fireEvent('rowsrendered', this);
9606         
9607         this.autoSize();
9608         
9609         this.initCSS(); /// resize cols
9610
9611         
9612     },
9613     
9614     
9615     onUpdate : function(ds,record)
9616     {
9617         this.refreshRow(record);
9618         this.autoSize();
9619     },
9620     
9621     onRemove : function(ds, record, index, isUpdate){
9622         if(isUpdate !== true){
9623             this.fireEvent("beforerowremoved", this, index, record);
9624         }
9625         var bt = this.bodyEl.dom;
9626         
9627         var rows = this.el.select('tbody > tr', true).elements;
9628         
9629         if(typeof(rows[index]) != 'undefined'){
9630             bt.removeChild(rows[index].dom);
9631         }
9632         
9633 //        if(bt.rows[index]){
9634 //            bt.removeChild(bt.rows[index]);
9635 //        }
9636         
9637         if(isUpdate !== true){
9638             //this.stripeRows(index);
9639             //this.syncRowHeights(index, index);
9640             //this.layout();
9641             this.fireEvent("rowremoved", this, index, record);
9642         }
9643     },
9644     
9645     onAdd : function(ds, records, rowIndex)
9646     {
9647         //Roo.log('on Add called');
9648         // - note this does not handle multiple adding very well..
9649         var bt = this.bodyEl.dom;
9650         for (var i =0 ; i < records.length;i++) {
9651             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9652             //Roo.log(records[i]);
9653             //Roo.log(this.store.getAt(rowIndex+i));
9654             this.insertRow(this.store, rowIndex + i, false);
9655             return;
9656         }
9657         
9658     },
9659     
9660     
9661     refreshRow : function(record){
9662         var ds = this.store, index;
9663         if(typeof record == 'number'){
9664             index = record;
9665             record = ds.getAt(index);
9666         }else{
9667             index = ds.indexOf(record);
9668             if (index < 0) {
9669                 return; // should not happen - but seems to 
9670             }
9671         }
9672         this.insertRow(ds, index, true);
9673         this.autoSize();
9674         this.onRemove(ds, record, index+1, true);
9675         this.autoSize();
9676         //this.syncRowHeights(index, index);
9677         //this.layout();
9678         this.fireEvent("rowupdated", this, index, record);
9679     },
9680     // private - called by RowSelection
9681     onRowSelect : function(rowIndex){
9682         var row = this.getRowDom(rowIndex);
9683         row.addClass(['bg-info','info']);
9684     },
9685     // private - called by RowSelection
9686     onRowDeselect : function(rowIndex)
9687     {
9688         if (rowIndex < 0) {
9689             return;
9690         }
9691         var row = this.getRowDom(rowIndex);
9692         row.removeClass(['bg-info','info']);
9693     },
9694       /**
9695      * Focuses the specified row.
9696      * @param {Number} row The row index
9697      */
9698     focusRow : function(row)
9699     {
9700         //Roo.log('GridView.focusRow');
9701         var x = this.bodyEl.dom.scrollLeft;
9702         this.focusCell(row, 0, false);
9703         this.bodyEl.dom.scrollLeft = x;
9704
9705     },
9706      /**
9707      * Focuses the specified cell.
9708      * @param {Number} row The row index
9709      * @param {Number} col The column index
9710      * @param {Boolean} hscroll false to disable horizontal scrolling
9711      */
9712     focusCell : function(row, col, hscroll)
9713     {
9714         //Roo.log('GridView.focusCell');
9715         var el = this.ensureVisible(row, col, hscroll);
9716         // not sure what focusEL achives = it's a <a> pos relative 
9717         //this.focusEl.alignTo(el, "tl-tl");
9718         //if(Roo.isGecko){
9719         //    this.focusEl.focus();
9720         //}else{
9721         //    this.focusEl.focus.defer(1, this.focusEl);
9722         //}
9723     },
9724     
9725      /**
9726      * Scrolls the specified cell into view
9727      * @param {Number} row The row index
9728      * @param {Number} col The column index
9729      * @param {Boolean} hscroll false to disable horizontal scrolling
9730      */
9731     ensureVisible : function(row, col, hscroll)
9732     {
9733         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9734         //return null; //disable for testing.
9735         if(typeof row != "number"){
9736             row = row.rowIndex;
9737         }
9738         if(row < 0 && row >= this.ds.getCount()){
9739             return  null;
9740         }
9741         col = (col !== undefined ? col : 0);
9742         var cm = this.cm;
9743         while(cm.isHidden(col)){
9744             col++;
9745         }
9746
9747         var el = this.getCellDom(row, col);
9748         if(!el){
9749             return null;
9750         }
9751         var c = this.bodyEl.dom;
9752
9753         var ctop = parseInt(el.offsetTop, 10);
9754         var cleft = parseInt(el.offsetLeft, 10);
9755         var cbot = ctop + el.offsetHeight;
9756         var cright = cleft + el.offsetWidth;
9757
9758         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9759         var ch = 0; //?? header is not withing the area?
9760         var stop = parseInt(c.scrollTop, 10);
9761         var sleft = parseInt(c.scrollLeft, 10);
9762         var sbot = stop + ch;
9763         var sright = sleft + c.clientWidth;
9764         /*
9765         Roo.log('GridView.ensureVisible:' +
9766                 ' ctop:' + ctop +
9767                 ' c.clientHeight:' + c.clientHeight +
9768                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9769                 ' stop:' + stop +
9770                 ' cbot:' + cbot +
9771                 ' sbot:' + sbot +
9772                 ' ch:' + ch  
9773                 );
9774         */
9775         if(ctop < stop){
9776             c.scrollTop = ctop;
9777             //Roo.log("set scrolltop to ctop DISABLE?");
9778         }else if(cbot > sbot){
9779             //Roo.log("set scrolltop to cbot-ch");
9780             c.scrollTop = cbot-ch;
9781         }
9782
9783         if(hscroll !== false){
9784             if(cleft < sleft){
9785                 c.scrollLeft = cleft;
9786             }else if(cright > sright){
9787                 c.scrollLeft = cright-c.clientWidth;
9788             }
9789         }
9790
9791         return el;
9792     },
9793     
9794     
9795     insertRow : function(dm, rowIndex, isUpdate){
9796         
9797         if(!isUpdate){
9798             this.fireEvent("beforerowsinserted", this, rowIndex);
9799         }
9800             //var s = this.getScrollState();
9801         var row = this.renderRow(this.cm, this.store, rowIndex);
9802         // insert before rowIndex..
9803         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9804         
9805         var _this = this;
9806                 
9807         if(row.cellObjects.length){
9808             Roo.each(row.cellObjects, function(r){
9809                 _this.renderCellObject(r);
9810             })
9811         }
9812             
9813         if(!isUpdate){
9814             this.fireEvent("rowsinserted", this, rowIndex);
9815             //this.syncRowHeights(firstRow, lastRow);
9816             //this.stripeRows(firstRow);
9817             //this.layout();
9818         }
9819         
9820     },
9821     
9822     
9823     getRowDom : function(rowIndex)
9824     {
9825         var rows = this.el.select('tbody > tr', true).elements;
9826         
9827         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9828         
9829     },
9830     getCellDom : function(rowIndex, colIndex)
9831     {
9832         var row = this.getRowDom(rowIndex);
9833         if (row === false) {
9834             return false;
9835         }
9836         var cols = row.select('td', true).elements;
9837         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9838         
9839     },
9840     
9841     // returns the object tree for a tr..
9842   
9843     
9844     renderRow : function(cm, ds, rowIndex) 
9845     {
9846         var d = ds.getAt(rowIndex);
9847         
9848         var row = {
9849             tag : 'tr',
9850             cls : 'x-row-' + rowIndex,
9851             cn : []
9852         };
9853             
9854         var cellObjects = [];
9855         
9856         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9857             var config = cm.config[i];
9858             
9859             var renderer = cm.getRenderer(i);
9860             var value = '';
9861             var id = false;
9862             
9863             if(typeof(renderer) !== 'undefined'){
9864                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9865             }
9866             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9867             // and are rendered into the cells after the row is rendered - using the id for the element.
9868             
9869             if(typeof(value) === 'object'){
9870                 id = Roo.id();
9871                 cellObjects.push({
9872                     container : id,
9873                     cfg : value 
9874                 })
9875             }
9876             
9877             var rowcfg = {
9878                 record: d,
9879                 rowIndex : rowIndex,
9880                 colIndex : i,
9881                 rowClass : ''
9882             };
9883
9884             this.fireEvent('rowclass', this, rowcfg);
9885             
9886             var td = {
9887                 tag: 'td',
9888                 // this might end up displaying HTML?
9889                 // this is too messy... - better to only do it on columsn you know are going to be too long
9890                 //tooltip : (typeof(value) === 'object') ? '' : value,
9891                 cls : rowcfg.rowClass + ' x-col-' + i,
9892                 style: '',
9893                 html: (typeof(value) === 'object') ? '' : value
9894             };
9895             
9896             if (id) {
9897                 td.id = id;
9898             }
9899             
9900             if(typeof(config.colspan) != 'undefined'){
9901                 td.colspan = config.colspan;
9902             }
9903             
9904             
9905             
9906             if(typeof(config.align) != 'undefined' && config.align.length){
9907                 td.style += ' text-align:' + config.align + ';';
9908             }
9909             if(typeof(config.valign) != 'undefined' && config.valign.length){
9910                 td.style += ' vertical-align:' + config.valign + ';';
9911             }
9912             /*
9913             if(typeof(config.width) != 'undefined'){
9914                 td.style += ' width:' +  config.width + 'px;';
9915             }
9916             */
9917             
9918             if(typeof(config.cursor) != 'undefined'){
9919                 td.style += ' cursor:' +  config.cursor + ';';
9920             }
9921             
9922             if(typeof(config.cls) != 'undefined'){
9923                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9924             }
9925             if (this.responsive) {
9926                 ['xs','sm','md','lg'].map(function(size){
9927                     
9928                     if(typeof(config[size]) == 'undefined'){
9929                         return;
9930                     }
9931                     
9932                     
9933                       
9934                     if (!config[size]) { // 0 = hidden
9935                         // BS 4 '0' is treated as hide that column and below.
9936                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9937                         return;
9938                     }
9939                     
9940                     td.cls += ' col-' + size + '-' + config[size] + (
9941                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9942                     );
9943                      
9944     
9945                 });
9946             }
9947             row.cn.push(td);
9948            
9949         }
9950         
9951         row.cellObjects = cellObjects;
9952         
9953         return row;
9954           
9955     },
9956     
9957     
9958     
9959     onBeforeLoad : function()
9960     {
9961         
9962     },
9963      /**
9964      * Remove all rows
9965      */
9966     clear : function()
9967     {
9968         this.el.select('tbody', true).first().dom.innerHTML = '';
9969     },
9970     /**
9971      * Show or hide a row.
9972      * @param {Number} rowIndex to show or hide
9973      * @param {Boolean} state hide
9974      */
9975     setRowVisibility : function(rowIndex, state)
9976     {
9977         var bt = this.bodyEl.dom;
9978         
9979         var rows = this.el.select('tbody > tr', true).elements;
9980         
9981         if(typeof(rows[rowIndex]) == 'undefined'){
9982             return;
9983         }
9984         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9985         
9986     },
9987     
9988     
9989     getSelectionModel : function(){
9990         if(!this.selModel){
9991             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9992         }
9993         return this.selModel;
9994     },
9995     /*
9996      * Render the Roo.bootstrap object from renderder
9997      */
9998     renderCellObject : function(r)
9999     {
10000         var _this = this;
10001         
10002         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10003         
10004         var t = r.cfg.render(r.container);
10005         
10006         if(r.cfg.cn){
10007             Roo.each(r.cfg.cn, function(c){
10008                 var child = {
10009                     container: t.getChildContainer(),
10010                     cfg: c
10011                 };
10012                 _this.renderCellObject(child);
10013             })
10014         }
10015     },
10016     /**
10017      * get the Row Index from a dom element.
10018      * @param {Roo.Element} row The row to look for
10019      * @returns {Number} the row
10020      */
10021     getRowIndex : function(row)
10022     {
10023         var rowIndex = -1;
10024         
10025         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10026             if(el != row){
10027                 return;
10028             }
10029             
10030             rowIndex = index;
10031         });
10032         
10033         return rowIndex;
10034     },
10035     /**
10036      * get the header TH element for columnIndex
10037      * @param {Number} columnIndex
10038      * @returns {Roo.Element}
10039      */
10040     getHeaderIndex: function(colIndex)
10041     {
10042         var cols = this.headEl.select('th', true).elements;
10043         return cols[colIndex]; 
10044     },
10045     /**
10046      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10047      * @param {domElement} cell to look for
10048      * @returns {Number} the column
10049      */
10050     getCellIndex : function(cell)
10051     {
10052         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10053         if(id){
10054             return parseInt(id[1], 10);
10055         }
10056         return 0;
10057     },
10058      /**
10059      * Returns the grid's underlying element = used by panel.Grid
10060      * @return {Element} The element
10061      */
10062     getGridEl : function(){
10063         return this.el;
10064     },
10065      /**
10066      * Forces a resize - used by panel.Grid
10067      * @return {Element} The element
10068      */
10069     autoSize : function()
10070     {
10071         //var ctr = Roo.get(this.container.dom.parentElement);
10072         var ctr = Roo.get(this.el.dom);
10073         
10074         var thd = this.getGridEl().select('thead',true).first();
10075         var tbd = this.getGridEl().select('tbody', true).first();
10076         var tfd = this.getGridEl().select('tfoot', true).first();
10077         
10078         var cw = ctr.getWidth();
10079         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10080         
10081         if (tbd) {
10082             
10083             tbd.setWidth(ctr.getWidth());
10084             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10085             // this needs fixing for various usage - currently only hydra job advers I think..
10086             //tdb.setHeight(
10087             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10088             //); 
10089             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10090             cw -= barsize;
10091         }
10092         cw = Math.max(cw, this.totalWidth);
10093         this.getGridEl().select('tbody tr',true).setWidth(cw);
10094         this.initCSS();
10095         
10096         // resize 'expandable coloumn?
10097         
10098         return; // we doe not have a view in this design..
10099         
10100     },
10101     onBodyScroll: function()
10102     {
10103         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10104         if(this.headEl){
10105             this.headEl.setStyle({
10106                 'position' : 'relative',
10107                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10108             });
10109         }
10110         
10111         if(this.lazyLoad){
10112             
10113             var scrollHeight = this.bodyEl.dom.scrollHeight;
10114             
10115             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10116             
10117             var height = this.bodyEl.getHeight();
10118             
10119             if(scrollHeight - height == scrollTop) {
10120                 
10121                 var total = this.ds.getTotalCount();
10122                 
10123                 if(this.footer.cursor + this.footer.pageSize < total){
10124                     
10125                     this.footer.ds.load({
10126                         params : {
10127                             start : this.footer.cursor + this.footer.pageSize,
10128                             limit : this.footer.pageSize
10129                         },
10130                         add : true
10131                     });
10132                 }
10133             }
10134             
10135         }
10136     },
10137     onColumnSplitterMoved : function(i, diff)
10138     {
10139         this.userResized = true;
10140         
10141         var cm = this.colModel;
10142         
10143         var w = this.getHeaderIndex(i).getWidth() + diff;
10144         
10145         
10146         cm.setColumnWidth(i, w, true);
10147         this.initCSS();
10148         //var cid = cm.getColumnId(i); << not used in this version?
10149        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10150         
10151         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10152         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10153         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10154 */
10155         //this.updateSplitters();
10156         //this.layout(); << ??
10157         this.fireEvent("columnresize", i, w);
10158     },
10159     onHeaderChange : function()
10160     {
10161         var header = this.renderHeader();
10162         var table = this.el.select('table', true).first();
10163         
10164         this.headEl.remove();
10165         this.headEl = table.createChild(header, this.bodyEl, false);
10166         
10167         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10168             e.on('click', this.sort, this);
10169         }, this);
10170         
10171         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10172             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10173         }
10174         
10175     },
10176     
10177     onHiddenChange : function(colModel, colIndex, hidden)
10178     {
10179         /*
10180         this.cm.setHidden()
10181         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10182         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10183         
10184         this.CSS.updateRule(thSelector, "display", "");
10185         this.CSS.updateRule(tdSelector, "display", "");
10186         
10187         if(hidden){
10188             this.CSS.updateRule(thSelector, "display", "none");
10189             this.CSS.updateRule(tdSelector, "display", "none");
10190         }
10191         */
10192         // onload calls initCSS()
10193         this.onHeaderChange();
10194         this.onLoad();
10195     },
10196     
10197     setColumnWidth: function(col_index, width)
10198     {
10199         // width = "md-2 xs-2..."
10200         if(!this.colModel.config[col_index]) {
10201             return;
10202         }
10203         
10204         var w = width.split(" ");
10205         
10206         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10207         
10208         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10209         
10210         
10211         for(var j = 0; j < w.length; j++) {
10212             
10213             if(!w[j]) {
10214                 continue;
10215             }
10216             
10217             var size_cls = w[j].split("-");
10218             
10219             if(!Number.isInteger(size_cls[1] * 1)) {
10220                 continue;
10221             }
10222             
10223             if(!this.colModel.config[col_index][size_cls[0]]) {
10224                 continue;
10225             }
10226             
10227             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10228                 continue;
10229             }
10230             
10231             h_row[0].classList.replace(
10232                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10233                 "col-"+size_cls[0]+"-"+size_cls[1]
10234             );
10235             
10236             for(var i = 0; i < rows.length; i++) {
10237                 
10238                 var size_cls = w[j].split("-");
10239                 
10240                 if(!Number.isInteger(size_cls[1] * 1)) {
10241                     continue;
10242                 }
10243                 
10244                 if(!this.colModel.config[col_index][size_cls[0]]) {
10245                     continue;
10246                 }
10247                 
10248                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10249                     continue;
10250                 }
10251                 
10252                 rows[i].classList.replace(
10253                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10254                     "col-"+size_cls[0]+"-"+size_cls[1]
10255                 );
10256             }
10257             
10258             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10259         }
10260     }
10261 });
10262
10263 // currently only used to find the split on drag.. 
10264 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10265
10266 /**
10267  * @depricated
10268 */
10269 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10270 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10271 /*
10272  * - LGPL
10273  *
10274  * table cell
10275  * 
10276  */
10277
10278 /**
10279  * @class Roo.bootstrap.TableCell
10280  * @extends Roo.bootstrap.Component
10281  * Bootstrap TableCell class
10282  * @cfg {String} html cell contain text
10283  * @cfg {String} cls cell class
10284  * @cfg {String} tag cell tag (td|th) default td
10285  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10286  * @cfg {String} align Aligns the content in a cell
10287  * @cfg {String} axis Categorizes cells
10288  * @cfg {String} bgcolor Specifies the background color of a cell
10289  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10290  * @cfg {Number} colspan Specifies the number of columns a cell should span
10291  * @cfg {String} headers Specifies one or more header cells a cell is related to
10292  * @cfg {Number} height Sets the height of a cell
10293  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10294  * @cfg {Number} rowspan Sets the number of rows a cell should span
10295  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10296  * @cfg {String} valign Vertical aligns the content in a cell
10297  * @cfg {Number} width Specifies the width of a cell
10298  * 
10299  * @constructor
10300  * Create a new TableCell
10301  * @param {Object} config The config object
10302  */
10303
10304 Roo.bootstrap.TableCell = function(config){
10305     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10306 };
10307
10308 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10309     
10310     html: false,
10311     cls: false,
10312     tag: false,
10313     abbr: false,
10314     align: false,
10315     axis: false,
10316     bgcolor: false,
10317     charoff: false,
10318     colspan: false,
10319     headers: false,
10320     height: false,
10321     nowrap: false,
10322     rowspan: false,
10323     scope: false,
10324     valign: false,
10325     width: false,
10326     
10327     
10328     getAutoCreate : function(){
10329         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10330         
10331         cfg = {
10332             tag: 'td'
10333         };
10334         
10335         if(this.tag){
10336             cfg.tag = this.tag;
10337         }
10338         
10339         if (this.html) {
10340             cfg.html=this.html
10341         }
10342         if (this.cls) {
10343             cfg.cls=this.cls
10344         }
10345         if (this.abbr) {
10346             cfg.abbr=this.abbr
10347         }
10348         if (this.align) {
10349             cfg.align=this.align
10350         }
10351         if (this.axis) {
10352             cfg.axis=this.axis
10353         }
10354         if (this.bgcolor) {
10355             cfg.bgcolor=this.bgcolor
10356         }
10357         if (this.charoff) {
10358             cfg.charoff=this.charoff
10359         }
10360         if (this.colspan) {
10361             cfg.colspan=this.colspan
10362         }
10363         if (this.headers) {
10364             cfg.headers=this.headers
10365         }
10366         if (this.height) {
10367             cfg.height=this.height
10368         }
10369         if (this.nowrap) {
10370             cfg.nowrap=this.nowrap
10371         }
10372         if (this.rowspan) {
10373             cfg.rowspan=this.rowspan
10374         }
10375         if (this.scope) {
10376             cfg.scope=this.scope
10377         }
10378         if (this.valign) {
10379             cfg.valign=this.valign
10380         }
10381         if (this.width) {
10382             cfg.width=this.width
10383         }
10384         
10385         
10386         return cfg;
10387     }
10388    
10389 });
10390
10391  
10392
10393  /*
10394  * - LGPL
10395  *
10396  * table row
10397  * 
10398  */
10399
10400 /**
10401  * @class Roo.bootstrap.TableRow
10402  * @extends Roo.bootstrap.Component
10403  * Bootstrap TableRow class
10404  * @cfg {String} cls row class
10405  * @cfg {String} align Aligns the content in a table row
10406  * @cfg {String} bgcolor Specifies a background color for a table row
10407  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10408  * @cfg {String} valign Vertical aligns the content in a table row
10409  * 
10410  * @constructor
10411  * Create a new TableRow
10412  * @param {Object} config The config object
10413  */
10414
10415 Roo.bootstrap.TableRow = function(config){
10416     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10417 };
10418
10419 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10420     
10421     cls: false,
10422     align: false,
10423     bgcolor: false,
10424     charoff: false,
10425     valign: false,
10426     
10427     getAutoCreate : function(){
10428         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10429         
10430         cfg = {
10431             tag: 'tr'
10432         };
10433             
10434         if(this.cls){
10435             cfg.cls = this.cls;
10436         }
10437         if(this.align){
10438             cfg.align = this.align;
10439         }
10440         if(this.bgcolor){
10441             cfg.bgcolor = this.bgcolor;
10442         }
10443         if(this.charoff){
10444             cfg.charoff = this.charoff;
10445         }
10446         if(this.valign){
10447             cfg.valign = this.valign;
10448         }
10449         
10450         return cfg;
10451     }
10452    
10453 });
10454
10455  
10456
10457  /*
10458  * - LGPL
10459  *
10460  * table body
10461  * 
10462  */
10463
10464 /**
10465  * @class Roo.bootstrap.TableBody
10466  * @extends Roo.bootstrap.Component
10467  * Bootstrap TableBody class
10468  * @cfg {String} cls element class
10469  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10470  * @cfg {String} align Aligns the content inside the element
10471  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10472  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10473  * 
10474  * @constructor
10475  * Create a new TableBody
10476  * @param {Object} config The config object
10477  */
10478
10479 Roo.bootstrap.TableBody = function(config){
10480     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10481 };
10482
10483 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10484     
10485     cls: false,
10486     tag: false,
10487     align: false,
10488     charoff: false,
10489     valign: false,
10490     
10491     getAutoCreate : function(){
10492         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10493         
10494         cfg = {
10495             tag: 'tbody'
10496         };
10497             
10498         if (this.cls) {
10499             cfg.cls=this.cls
10500         }
10501         if(this.tag){
10502             cfg.tag = this.tag;
10503         }
10504         
10505         if(this.align){
10506             cfg.align = this.align;
10507         }
10508         if(this.charoff){
10509             cfg.charoff = this.charoff;
10510         }
10511         if(this.valign){
10512             cfg.valign = this.valign;
10513         }
10514         
10515         return cfg;
10516     }
10517     
10518     
10519 //    initEvents : function()
10520 //    {
10521 //        
10522 //        if(!this.store){
10523 //            return;
10524 //        }
10525 //        
10526 //        this.store = Roo.factory(this.store, Roo.data);
10527 //        this.store.on('load', this.onLoad, this);
10528 //        
10529 //        this.store.load();
10530 //        
10531 //    },
10532 //    
10533 //    onLoad: function () 
10534 //    {   
10535 //        this.fireEvent('load', this);
10536 //    }
10537 //    
10538 //   
10539 });
10540
10541  
10542
10543  /*
10544  * Based on:
10545  * Ext JS Library 1.1.1
10546  * Copyright(c) 2006-2007, Ext JS, LLC.
10547  *
10548  * Originally Released Under LGPL - original licence link has changed is not relivant.
10549  *
10550  * Fork - LGPL
10551  * <script type="text/javascript">
10552  */
10553
10554 // as we use this in bootstrap.
10555 Roo.namespace('Roo.form');
10556  /**
10557  * @class Roo.form.Action
10558  * Internal Class used to handle form actions
10559  * @constructor
10560  * @param {Roo.form.BasicForm} el The form element or its id
10561  * @param {Object} config Configuration options
10562  */
10563
10564  
10565  
10566 // define the action interface
10567 Roo.form.Action = function(form, options){
10568     this.form = form;
10569     this.options = options || {};
10570 };
10571 /**
10572  * Client Validation Failed
10573  * @const 
10574  */
10575 Roo.form.Action.CLIENT_INVALID = 'client';
10576 /**
10577  * Server Validation Failed
10578  * @const 
10579  */
10580 Roo.form.Action.SERVER_INVALID = 'server';
10581  /**
10582  * Connect to Server Failed
10583  * @const 
10584  */
10585 Roo.form.Action.CONNECT_FAILURE = 'connect';
10586 /**
10587  * Reading Data from Server Failed
10588  * @const 
10589  */
10590 Roo.form.Action.LOAD_FAILURE = 'load';
10591
10592 Roo.form.Action.prototype = {
10593     type : 'default',
10594     failureType : undefined,
10595     response : undefined,
10596     result : undefined,
10597
10598     // interface method
10599     run : function(options){
10600
10601     },
10602
10603     // interface method
10604     success : function(response){
10605
10606     },
10607
10608     // interface method
10609     handleResponse : function(response){
10610
10611     },
10612
10613     // default connection failure
10614     failure : function(response){
10615         
10616         this.response = response;
10617         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10618         this.form.afterAction(this, false);
10619     },
10620
10621     processResponse : function(response){
10622         this.response = response;
10623         if(!response.responseText){
10624             return true;
10625         }
10626         this.result = this.handleResponse(response);
10627         return this.result;
10628     },
10629
10630     // utility functions used internally
10631     getUrl : function(appendParams){
10632         var url = this.options.url || this.form.url || this.form.el.dom.action;
10633         if(appendParams){
10634             var p = this.getParams();
10635             if(p){
10636                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10637             }
10638         }
10639         return url;
10640     },
10641
10642     getMethod : function(){
10643         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10644     },
10645
10646     getParams : function(){
10647         var bp = this.form.baseParams;
10648         var p = this.options.params;
10649         if(p){
10650             if(typeof p == "object"){
10651                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10652             }else if(typeof p == 'string' && bp){
10653                 p += '&' + Roo.urlEncode(bp);
10654             }
10655         }else if(bp){
10656             p = Roo.urlEncode(bp);
10657         }
10658         return p;
10659     },
10660
10661     createCallback : function(){
10662         return {
10663             success: this.success,
10664             failure: this.failure,
10665             scope: this,
10666             timeout: (this.form.timeout*1000),
10667             upload: this.form.fileUpload ? this.success : undefined
10668         };
10669     }
10670 };
10671
10672 Roo.form.Action.Submit = function(form, options){
10673     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10674 };
10675
10676 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10677     type : 'submit',
10678
10679     haveProgress : false,
10680     uploadComplete : false,
10681     
10682     // uploadProgress indicator.
10683     uploadProgress : function()
10684     {
10685         if (!this.form.progressUrl) {
10686             return;
10687         }
10688         
10689         if (!this.haveProgress) {
10690             Roo.MessageBox.progress("Uploading", "Uploading");
10691         }
10692         if (this.uploadComplete) {
10693            Roo.MessageBox.hide();
10694            return;
10695         }
10696         
10697         this.haveProgress = true;
10698    
10699         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10700         
10701         var c = new Roo.data.Connection();
10702         c.request({
10703             url : this.form.progressUrl,
10704             params: {
10705                 id : uid
10706             },
10707             method: 'GET',
10708             success : function(req){
10709                //console.log(data);
10710                 var rdata = false;
10711                 var edata;
10712                 try  {
10713                    rdata = Roo.decode(req.responseText)
10714                 } catch (e) {
10715                     Roo.log("Invalid data from server..");
10716                     Roo.log(edata);
10717                     return;
10718                 }
10719                 if (!rdata || !rdata.success) {
10720                     Roo.log(rdata);
10721                     Roo.MessageBox.alert(Roo.encode(rdata));
10722                     return;
10723                 }
10724                 var data = rdata.data;
10725                 
10726                 if (this.uploadComplete) {
10727                    Roo.MessageBox.hide();
10728                    return;
10729                 }
10730                    
10731                 if (data){
10732                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10733                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10734                     );
10735                 }
10736                 this.uploadProgress.defer(2000,this);
10737             },
10738        
10739             failure: function(data) {
10740                 Roo.log('progress url failed ');
10741                 Roo.log(data);
10742             },
10743             scope : this
10744         });
10745            
10746     },
10747     
10748     
10749     run : function()
10750     {
10751         // run get Values on the form, so it syncs any secondary forms.
10752         this.form.getValues();
10753         
10754         var o = this.options;
10755         var method = this.getMethod();
10756         var isPost = method == 'POST';
10757         if(o.clientValidation === false || this.form.isValid()){
10758             
10759             if (this.form.progressUrl) {
10760                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10761                     (new Date() * 1) + '' + Math.random());
10762                     
10763             } 
10764             
10765             
10766             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10767                 form:this.form.el.dom,
10768                 url:this.getUrl(!isPost),
10769                 method: method,
10770                 params:isPost ? this.getParams() : null,
10771                 isUpload: this.form.fileUpload,
10772                 formData : this.form.formData
10773             }));
10774             
10775             this.uploadProgress();
10776
10777         }else if (o.clientValidation !== false){ // client validation failed
10778             this.failureType = Roo.form.Action.CLIENT_INVALID;
10779             this.form.afterAction(this, false);
10780         }
10781     },
10782
10783     success : function(response)
10784     {
10785         this.uploadComplete= true;
10786         if (this.haveProgress) {
10787             Roo.MessageBox.hide();
10788         }
10789         
10790         
10791         var result = this.processResponse(response);
10792         if(result === true || result.success){
10793             this.form.afterAction(this, true);
10794             return;
10795         }
10796         if(result.errors){
10797             this.form.markInvalid(result.errors);
10798             this.failureType = Roo.form.Action.SERVER_INVALID;
10799         }
10800         this.form.afterAction(this, false);
10801     },
10802     failure : function(response)
10803     {
10804         this.uploadComplete= true;
10805         if (this.haveProgress) {
10806             Roo.MessageBox.hide();
10807         }
10808         
10809         this.response = response;
10810         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10811         this.form.afterAction(this, false);
10812     },
10813     
10814     handleResponse : function(response){
10815         if(this.form.errorReader){
10816             var rs = this.form.errorReader.read(response);
10817             var errors = [];
10818             if(rs.records){
10819                 for(var i = 0, len = rs.records.length; i < len; i++) {
10820                     var r = rs.records[i];
10821                     errors[i] = r.data;
10822                 }
10823             }
10824             if(errors.length < 1){
10825                 errors = null;
10826             }
10827             return {
10828                 success : rs.success,
10829                 errors : errors
10830             };
10831         }
10832         var ret = false;
10833         try {
10834             ret = Roo.decode(response.responseText);
10835         } catch (e) {
10836             ret = {
10837                 success: false,
10838                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10839                 errors : []
10840             };
10841         }
10842         return ret;
10843         
10844     }
10845 });
10846
10847
10848 Roo.form.Action.Load = function(form, options){
10849     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10850     this.reader = this.form.reader;
10851 };
10852
10853 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10854     type : 'load',
10855
10856     run : function(){
10857         
10858         Roo.Ajax.request(Roo.apply(
10859                 this.createCallback(), {
10860                     method:this.getMethod(),
10861                     url:this.getUrl(false),
10862                     params:this.getParams()
10863         }));
10864     },
10865
10866     success : function(response){
10867         
10868         var result = this.processResponse(response);
10869         if(result === true || !result.success || !result.data){
10870             this.failureType = Roo.form.Action.LOAD_FAILURE;
10871             this.form.afterAction(this, false);
10872             return;
10873         }
10874         this.form.clearInvalid();
10875         this.form.setValues(result.data);
10876         this.form.afterAction(this, true);
10877     },
10878
10879     handleResponse : function(response){
10880         if(this.form.reader){
10881             var rs = this.form.reader.read(response);
10882             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10883             return {
10884                 success : rs.success,
10885                 data : data
10886             };
10887         }
10888         return Roo.decode(response.responseText);
10889     }
10890 });
10891
10892 Roo.form.Action.ACTION_TYPES = {
10893     'load' : Roo.form.Action.Load,
10894     'submit' : Roo.form.Action.Submit
10895 };/*
10896  * - LGPL
10897  *
10898  * form
10899  *
10900  */
10901
10902 /**
10903  * @class Roo.bootstrap.Form
10904  * @extends Roo.bootstrap.Component
10905  * Bootstrap Form class
10906  * @cfg {String} method  GET | POST (default POST)
10907  * @cfg {String} labelAlign top | left (default top)
10908  * @cfg {String} align left  | right - for navbars
10909  * @cfg {Boolean} loadMask load mask when submit (default true)
10910
10911  *
10912  * @constructor
10913  * Create a new Form
10914  * @param {Object} config The config object
10915  */
10916
10917
10918 Roo.bootstrap.Form = function(config){
10919     
10920     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10921     
10922     Roo.bootstrap.Form.popover.apply();
10923     
10924     this.addEvents({
10925         /**
10926          * @event clientvalidation
10927          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10928          * @param {Form} this
10929          * @param {Boolean} valid true if the form has passed client-side validation
10930          */
10931         clientvalidation: true,
10932         /**
10933          * @event beforeaction
10934          * Fires before any action is performed. Return false to cancel the action.
10935          * @param {Form} this
10936          * @param {Action} action The action to be performed
10937          */
10938         beforeaction: true,
10939         /**
10940          * @event actionfailed
10941          * Fires when an action fails.
10942          * @param {Form} this
10943          * @param {Action} action The action that failed
10944          */
10945         actionfailed : true,
10946         /**
10947          * @event actioncomplete
10948          * Fires when an action is completed.
10949          * @param {Form} this
10950          * @param {Action} action The action that completed
10951          */
10952         actioncomplete : true
10953     });
10954 };
10955
10956 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10957
10958      /**
10959      * @cfg {String} method
10960      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10961      */
10962     method : 'POST',
10963     /**
10964      * @cfg {String} url
10965      * The URL to use for form actions if one isn't supplied in the action options.
10966      */
10967     /**
10968      * @cfg {Boolean} fileUpload
10969      * Set to true if this form is a file upload.
10970      */
10971
10972     /**
10973      * @cfg {Object} baseParams
10974      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10975      */
10976
10977     /**
10978      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10979      */
10980     timeout: 30,
10981     /**
10982      * @cfg {Sting} align (left|right) for navbar forms
10983      */
10984     align : 'left',
10985
10986     // private
10987     activeAction : null,
10988
10989     /**
10990      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10991      * element by passing it or its id or mask the form itself by passing in true.
10992      * @type Mixed
10993      */
10994     waitMsgTarget : false,
10995
10996     loadMask : true,
10997     
10998     /**
10999      * @cfg {Boolean} errorMask (true|false) default false
11000      */
11001     errorMask : false,
11002     
11003     /**
11004      * @cfg {Number} maskOffset Default 100
11005      */
11006     maskOffset : 100,
11007     
11008     /**
11009      * @cfg {Boolean} maskBody
11010      */
11011     maskBody : false,
11012
11013     getAutoCreate : function(){
11014
11015         var cfg = {
11016             tag: 'form',
11017             method : this.method || 'POST',
11018             id : this.id || Roo.id(),
11019             cls : ''
11020         };
11021         if (this.parent().xtype.match(/^Nav/)) {
11022             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11023
11024         }
11025
11026         if (this.labelAlign == 'left' ) {
11027             cfg.cls += ' form-horizontal';
11028         }
11029
11030
11031         return cfg;
11032     },
11033     initEvents : function()
11034     {
11035         this.el.on('submit', this.onSubmit, this);
11036         // this was added as random key presses on the form where triggering form submit.
11037         this.el.on('keypress', function(e) {
11038             if (e.getCharCode() != 13) {
11039                 return true;
11040             }
11041             // we might need to allow it for textareas.. and some other items.
11042             // check e.getTarget().
11043
11044             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11045                 return true;
11046             }
11047
11048             Roo.log("keypress blocked");
11049
11050             e.preventDefault();
11051             return false;
11052         });
11053         
11054     },
11055     // private
11056     onSubmit : function(e){
11057         e.stopEvent();
11058     },
11059
11060      /**
11061      * Returns true if client-side validation on the form is successful.
11062      * @return Boolean
11063      */
11064     isValid : function(){
11065         var items = this.getItems();
11066         var valid = true;
11067         var target = false;
11068         
11069         items.each(function(f){
11070             
11071             if(f.validate()){
11072                 return;
11073             }
11074             
11075             Roo.log('invalid field: ' + f.name);
11076             
11077             valid = false;
11078
11079             if(!target && f.el.isVisible(true)){
11080                 target = f;
11081             }
11082            
11083         });
11084         
11085         if(this.errorMask && !valid){
11086             Roo.bootstrap.Form.popover.mask(this, target);
11087         }
11088         
11089         return valid;
11090     },
11091     
11092     /**
11093      * Returns true if any fields in this form have changed since their original load.
11094      * @return Boolean
11095      */
11096     isDirty : function(){
11097         var dirty = false;
11098         var items = this.getItems();
11099         items.each(function(f){
11100            if(f.isDirty()){
11101                dirty = true;
11102                return false;
11103            }
11104            return true;
11105         });
11106         return dirty;
11107     },
11108      /**
11109      * Performs a predefined action (submit or load) or custom actions you define on this form.
11110      * @param {String} actionName The name of the action type
11111      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11112      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11113      * accept other config options):
11114      * <pre>
11115 Property          Type             Description
11116 ----------------  ---------------  ----------------------------------------------------------------------------------
11117 url               String           The url for the action (defaults to the form's url)
11118 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11119 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11120 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11121                                    validate the form on the client (defaults to false)
11122      * </pre>
11123      * @return {BasicForm} this
11124      */
11125     doAction : function(action, options){
11126         if(typeof action == 'string'){
11127             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11128         }
11129         if(this.fireEvent('beforeaction', this, action) !== false){
11130             this.beforeAction(action);
11131             action.run.defer(100, action);
11132         }
11133         return this;
11134     },
11135
11136     // private
11137     beforeAction : function(action){
11138         var o = action.options;
11139         
11140         if(this.loadMask){
11141             
11142             if(this.maskBody){
11143                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11144             } else {
11145                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11146             }
11147         }
11148         // not really supported yet.. ??
11149
11150         //if(this.waitMsgTarget === true){
11151         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11152         //}else if(this.waitMsgTarget){
11153         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11154         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11155         //}else {
11156         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11157        // }
11158
11159     },
11160
11161     // private
11162     afterAction : function(action, success){
11163         this.activeAction = null;
11164         var o = action.options;
11165
11166         if(this.loadMask){
11167             
11168             if(this.maskBody){
11169                 Roo.get(document.body).unmask();
11170             } else {
11171                 this.el.unmask();
11172             }
11173         }
11174         
11175         //if(this.waitMsgTarget === true){
11176 //            this.el.unmask();
11177         //}else if(this.waitMsgTarget){
11178         //    this.waitMsgTarget.unmask();
11179         //}else{
11180         //    Roo.MessageBox.updateProgress(1);
11181         //    Roo.MessageBox.hide();
11182        // }
11183         //
11184         if(success){
11185             if(o.reset){
11186                 this.reset();
11187             }
11188             Roo.callback(o.success, o.scope, [this, action]);
11189             this.fireEvent('actioncomplete', this, action);
11190
11191         }else{
11192
11193             // failure condition..
11194             // we have a scenario where updates need confirming.
11195             // eg. if a locking scenario exists..
11196             // we look for { errors : { needs_confirm : true }} in the response.
11197             if (
11198                 (typeof(action.result) != 'undefined')  &&
11199                 (typeof(action.result.errors) != 'undefined')  &&
11200                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11201            ){
11202                 var _t = this;
11203                 Roo.log("not supported yet");
11204                  /*
11205
11206                 Roo.MessageBox.confirm(
11207                     "Change requires confirmation",
11208                     action.result.errorMsg,
11209                     function(r) {
11210                         if (r != 'yes') {
11211                             return;
11212                         }
11213                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11214                     }
11215
11216                 );
11217                 */
11218
11219
11220                 return;
11221             }
11222
11223             Roo.callback(o.failure, o.scope, [this, action]);
11224             // show an error message if no failed handler is set..
11225             if (!this.hasListener('actionfailed')) {
11226                 Roo.log("need to add dialog support");
11227                 /*
11228                 Roo.MessageBox.alert("Error",
11229                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11230                         action.result.errorMsg :
11231                         "Saving Failed, please check your entries or try again"
11232                 );
11233                 */
11234             }
11235
11236             this.fireEvent('actionfailed', this, action);
11237         }
11238
11239     },
11240     /**
11241      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11242      * @param {String} id The value to search for
11243      * @return Field
11244      */
11245     findField : function(id){
11246         var items = this.getItems();
11247         var field = items.get(id);
11248         if(!field){
11249              items.each(function(f){
11250                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11251                     field = f;
11252                     return false;
11253                 }
11254                 return true;
11255             });
11256         }
11257         return field || null;
11258     },
11259      /**
11260      * Mark fields in this form invalid in bulk.
11261      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11262      * @return {BasicForm} this
11263      */
11264     markInvalid : function(errors){
11265         if(errors instanceof Array){
11266             for(var i = 0, len = errors.length; i < len; i++){
11267                 var fieldError = errors[i];
11268                 var f = this.findField(fieldError.id);
11269                 if(f){
11270                     f.markInvalid(fieldError.msg);
11271                 }
11272             }
11273         }else{
11274             var field, id;
11275             for(id in errors){
11276                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11277                     field.markInvalid(errors[id]);
11278                 }
11279             }
11280         }
11281         //Roo.each(this.childForms || [], function (f) {
11282         //    f.markInvalid(errors);
11283         //});
11284
11285         return this;
11286     },
11287
11288     /**
11289      * Set values for fields in this form in bulk.
11290      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11291      * @return {BasicForm} this
11292      */
11293     setValues : function(values){
11294         if(values instanceof Array){ // array of objects
11295             for(var i = 0, len = values.length; i < len; i++){
11296                 var v = values[i];
11297                 var f = this.findField(v.id);
11298                 if(f){
11299                     f.setValue(v.value);
11300                     if(this.trackResetOnLoad){
11301                         f.originalValue = f.getValue();
11302                     }
11303                 }
11304             }
11305         }else{ // object hash
11306             var field, id;
11307             for(id in values){
11308                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11309
11310                     if (field.setFromData &&
11311                         field.valueField &&
11312                         field.displayField &&
11313                         // combos' with local stores can
11314                         // be queried via setValue()
11315                         // to set their value..
11316                         (field.store && !field.store.isLocal)
11317                         ) {
11318                         // it's a combo
11319                         var sd = { };
11320                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11321                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11322                         field.setFromData(sd);
11323
11324                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11325                         
11326                         field.setFromData(values);
11327                         
11328                     } else {
11329                         field.setValue(values[id]);
11330                     }
11331
11332
11333                     if(this.trackResetOnLoad){
11334                         field.originalValue = field.getValue();
11335                     }
11336                 }
11337             }
11338         }
11339
11340         //Roo.each(this.childForms || [], function (f) {
11341         //    f.setValues(values);
11342         //});
11343
11344         return this;
11345     },
11346
11347     /**
11348      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11349      * they are returned as an array.
11350      * @param {Boolean} asString
11351      * @return {Object}
11352      */
11353     getValues : function(asString){
11354         //if (this.childForms) {
11355             // copy values from the child forms
11356         //    Roo.each(this.childForms, function (f) {
11357         //        this.setValues(f.getValues());
11358         //    }, this);
11359         //}
11360
11361
11362
11363         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11364         if(asString === true){
11365             return fs;
11366         }
11367         return Roo.urlDecode(fs);
11368     },
11369
11370     /**
11371      * Returns the fields in this form as an object with key/value pairs.
11372      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11373      * @return {Object}
11374      */
11375     getFieldValues : function(with_hidden)
11376     {
11377         var items = this.getItems();
11378         var ret = {};
11379         items.each(function(f){
11380             
11381             if (!f.getName()) {
11382                 return;
11383             }
11384             
11385             var v = f.getValue();
11386             
11387             if (f.inputType =='radio') {
11388                 if (typeof(ret[f.getName()]) == 'undefined') {
11389                     ret[f.getName()] = ''; // empty..
11390                 }
11391
11392                 if (!f.el.dom.checked) {
11393                     return;
11394
11395                 }
11396                 v = f.el.dom.value;
11397
11398             }
11399             
11400             if(f.xtype == 'MoneyField'){
11401                 ret[f.currencyName] = f.getCurrency();
11402             }
11403
11404             // not sure if this supported any more..
11405             if ((typeof(v) == 'object') && f.getRawValue) {
11406                 v = f.getRawValue() ; // dates..
11407             }
11408             // combo boxes where name != hiddenName...
11409             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11410                 ret[f.name] = f.getRawValue();
11411             }
11412             ret[f.getName()] = v;
11413         });
11414
11415         return ret;
11416     },
11417
11418     /**
11419      * Clears all invalid messages in this form.
11420      * @return {BasicForm} this
11421      */
11422     clearInvalid : function(){
11423         var items = this.getItems();
11424
11425         items.each(function(f){
11426            f.clearInvalid();
11427         });
11428
11429         return this;
11430     },
11431
11432     /**
11433      * Resets this form.
11434      * @return {BasicForm} this
11435      */
11436     reset : function(){
11437         var items = this.getItems();
11438         items.each(function(f){
11439             f.reset();
11440         });
11441
11442         Roo.each(this.childForms || [], function (f) {
11443             f.reset();
11444         });
11445
11446
11447         return this;
11448     },
11449     
11450     getItems : function()
11451     {
11452         var r=new Roo.util.MixedCollection(false, function(o){
11453             return o.id || (o.id = Roo.id());
11454         });
11455         var iter = function(el) {
11456             if (el.inputEl) {
11457                 r.add(el);
11458             }
11459             if (!el.items) {
11460                 return;
11461             }
11462             Roo.each(el.items,function(e) {
11463                 iter(e);
11464             });
11465         };
11466
11467         iter(this);
11468         return r;
11469     },
11470     
11471     hideFields : function(items)
11472     {
11473         Roo.each(items, function(i){
11474             
11475             var f = this.findField(i);
11476             
11477             if(!f){
11478                 return;
11479             }
11480             
11481             f.hide();
11482             
11483         }, this);
11484     },
11485     
11486     showFields : function(items)
11487     {
11488         Roo.each(items, function(i){
11489             
11490             var f = this.findField(i);
11491             
11492             if(!f){
11493                 return;
11494             }
11495             
11496             f.show();
11497             
11498         }, this);
11499     }
11500
11501 });
11502
11503 Roo.apply(Roo.bootstrap.Form, {
11504     
11505     popover : {
11506         
11507         padding : 5,
11508         
11509         isApplied : false,
11510         
11511         isMasked : false,
11512         
11513         form : false,
11514         
11515         target : false,
11516         
11517         toolTip : false,
11518         
11519         intervalID : false,
11520         
11521         maskEl : false,
11522         
11523         apply : function()
11524         {
11525             if(this.isApplied){
11526                 return;
11527             }
11528             
11529             this.maskEl = {
11530                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11531                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11532                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11533                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11534             };
11535             
11536             this.maskEl.top.enableDisplayMode("block");
11537             this.maskEl.left.enableDisplayMode("block");
11538             this.maskEl.bottom.enableDisplayMode("block");
11539             this.maskEl.right.enableDisplayMode("block");
11540             
11541             this.toolTip = new Roo.bootstrap.Tooltip({
11542                 cls : 'roo-form-error-popover',
11543                 alignment : {
11544                     'left' : ['r-l', [-2,0], 'right'],
11545                     'right' : ['l-r', [2,0], 'left'],
11546                     'bottom' : ['tl-bl', [0,2], 'top'],
11547                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11548                 }
11549             });
11550             
11551             this.toolTip.render(Roo.get(document.body));
11552
11553             this.toolTip.el.enableDisplayMode("block");
11554             
11555             Roo.get(document.body).on('click', function(){
11556                 this.unmask();
11557             }, this);
11558             
11559             Roo.get(document.body).on('touchstart', function(){
11560                 this.unmask();
11561             }, this);
11562             
11563             this.isApplied = true
11564         },
11565         
11566         mask : function(form, target)
11567         {
11568             this.form = form;
11569             
11570             this.target = target;
11571             
11572             if(!this.form.errorMask || !target.el){
11573                 return;
11574             }
11575             
11576             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11577             
11578             Roo.log(scrollable);
11579             
11580             var ot = this.target.el.calcOffsetsTo(scrollable);
11581             
11582             var scrollTo = ot[1] - this.form.maskOffset;
11583             
11584             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11585             
11586             scrollable.scrollTo('top', scrollTo);
11587             
11588             var box = this.target.el.getBox();
11589             Roo.log(box);
11590             var zIndex = Roo.bootstrap.Modal.zIndex++;
11591
11592             
11593             this.maskEl.top.setStyle('position', 'absolute');
11594             this.maskEl.top.setStyle('z-index', zIndex);
11595             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11596             this.maskEl.top.setLeft(0);
11597             this.maskEl.top.setTop(0);
11598             this.maskEl.top.show();
11599             
11600             this.maskEl.left.setStyle('position', 'absolute');
11601             this.maskEl.left.setStyle('z-index', zIndex);
11602             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11603             this.maskEl.left.setLeft(0);
11604             this.maskEl.left.setTop(box.y - this.padding);
11605             this.maskEl.left.show();
11606
11607             this.maskEl.bottom.setStyle('position', 'absolute');
11608             this.maskEl.bottom.setStyle('z-index', zIndex);
11609             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11610             this.maskEl.bottom.setLeft(0);
11611             this.maskEl.bottom.setTop(box.bottom + this.padding);
11612             this.maskEl.bottom.show();
11613
11614             this.maskEl.right.setStyle('position', 'absolute');
11615             this.maskEl.right.setStyle('z-index', zIndex);
11616             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11617             this.maskEl.right.setLeft(box.right + this.padding);
11618             this.maskEl.right.setTop(box.y - this.padding);
11619             this.maskEl.right.show();
11620
11621             this.toolTip.bindEl = this.target.el;
11622
11623             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11624
11625             var tip = this.target.blankText;
11626
11627             if(this.target.getValue() !== '' ) {
11628                 
11629                 if (this.target.invalidText.length) {
11630                     tip = this.target.invalidText;
11631                 } else if (this.target.regexText.length){
11632                     tip = this.target.regexText;
11633                 }
11634             }
11635
11636             this.toolTip.show(tip);
11637
11638             this.intervalID = window.setInterval(function() {
11639                 Roo.bootstrap.Form.popover.unmask();
11640             }, 10000);
11641
11642             window.onwheel = function(){ return false;};
11643             
11644             (function(){ this.isMasked = true; }).defer(500, this);
11645             
11646         },
11647         
11648         unmask : function()
11649         {
11650             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11651                 return;
11652             }
11653             
11654             this.maskEl.top.setStyle('position', 'absolute');
11655             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11656             this.maskEl.top.hide();
11657
11658             this.maskEl.left.setStyle('position', 'absolute');
11659             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11660             this.maskEl.left.hide();
11661
11662             this.maskEl.bottom.setStyle('position', 'absolute');
11663             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11664             this.maskEl.bottom.hide();
11665
11666             this.maskEl.right.setStyle('position', 'absolute');
11667             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11668             this.maskEl.right.hide();
11669             
11670             this.toolTip.hide();
11671             
11672             this.toolTip.el.hide();
11673             
11674             window.onwheel = function(){ return true;};
11675             
11676             if(this.intervalID){
11677                 window.clearInterval(this.intervalID);
11678                 this.intervalID = false;
11679             }
11680             
11681             this.isMasked = false;
11682             
11683         }
11684         
11685     }
11686     
11687 });
11688
11689 /*
11690  * Based on:
11691  * Ext JS Library 1.1.1
11692  * Copyright(c) 2006-2007, Ext JS, LLC.
11693  *
11694  * Originally Released Under LGPL - original licence link has changed is not relivant.
11695  *
11696  * Fork - LGPL
11697  * <script type="text/javascript">
11698  */
11699 /**
11700  * @class Roo.form.VTypes
11701  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11702  * @singleton
11703  */
11704 Roo.form.VTypes = function(){
11705     // closure these in so they are only created once.
11706     var alpha = /^[a-zA-Z_]+$/;
11707     var alphanum = /^[a-zA-Z0-9_]+$/;
11708     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11709     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11710
11711     // All these messages and functions are configurable
11712     return {
11713         /**
11714          * The function used to validate email addresses
11715          * @param {String} value The email address
11716          */
11717         'email' : function(v){
11718             return email.test(v);
11719         },
11720         /**
11721          * The error text to display when the email validation function returns false
11722          * @type String
11723          */
11724         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11725         /**
11726          * The keystroke filter mask to be applied on email input
11727          * @type RegExp
11728          */
11729         'emailMask' : /[a-z0-9_\.\-@]/i,
11730
11731         /**
11732          * The function used to validate URLs
11733          * @param {String} value The URL
11734          */
11735         'url' : function(v){
11736             return url.test(v);
11737         },
11738         /**
11739          * The error text to display when the url validation function returns false
11740          * @type String
11741          */
11742         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11743         
11744         /**
11745          * The function used to validate alpha values
11746          * @param {String} value The value
11747          */
11748         'alpha' : function(v){
11749             return alpha.test(v);
11750         },
11751         /**
11752          * The error text to display when the alpha validation function returns false
11753          * @type String
11754          */
11755         'alphaText' : 'This field should only contain letters and _',
11756         /**
11757          * The keystroke filter mask to be applied on alpha input
11758          * @type RegExp
11759          */
11760         'alphaMask' : /[a-z_]/i,
11761
11762         /**
11763          * The function used to validate alphanumeric values
11764          * @param {String} value The value
11765          */
11766         'alphanum' : function(v){
11767             return alphanum.test(v);
11768         },
11769         /**
11770          * The error text to display when the alphanumeric validation function returns false
11771          * @type String
11772          */
11773         'alphanumText' : 'This field should only contain letters, numbers and _',
11774         /**
11775          * The keystroke filter mask to be applied on alphanumeric input
11776          * @type RegExp
11777          */
11778         'alphanumMask' : /[a-z0-9_]/i
11779     };
11780 }();/*
11781  * - LGPL
11782  *
11783  * Input
11784  * 
11785  */
11786
11787 /**
11788  * @class Roo.bootstrap.Input
11789  * @extends Roo.bootstrap.Component
11790  * Bootstrap Input class
11791  * @cfg {Boolean} disabled is it disabled
11792  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11793  * @cfg {String} name name of the input
11794  * @cfg {string} fieldLabel - the label associated
11795  * @cfg {string} placeholder - placeholder to put in text.
11796  * @cfg {string}  before - input group add on before
11797  * @cfg {string} after - input group add on after
11798  * @cfg {string} size - (lg|sm) or leave empty..
11799  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11800  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11801  * @cfg {Number} md colspan out of 12 for computer-sized screens
11802  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11803  * @cfg {string} value default value of the input
11804  * @cfg {Number} labelWidth set the width of label 
11805  * @cfg {Number} labellg set the width of label (1-12)
11806  * @cfg {Number} labelmd set the width of label (1-12)
11807  * @cfg {Number} labelsm set the width of label (1-12)
11808  * @cfg {Number} labelxs set the width of label (1-12)
11809  * @cfg {String} labelAlign (top|left)
11810  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11811  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11812  * @cfg {String} indicatorpos (left|right) default left
11813  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11814  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11815  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11816
11817  * @cfg {String} align (left|center|right) Default left
11818  * @cfg {Boolean} forceFeedback (true|false) Default false
11819  * 
11820  * @constructor
11821  * Create a new Input
11822  * @param {Object} config The config object
11823  */
11824
11825 Roo.bootstrap.Input = function(config){
11826     
11827     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11828     
11829     this.addEvents({
11830         /**
11831          * @event focus
11832          * Fires when this field receives input focus.
11833          * @param {Roo.form.Field} this
11834          */
11835         focus : true,
11836         /**
11837          * @event blur
11838          * Fires when this field loses input focus.
11839          * @param {Roo.form.Field} this
11840          */
11841         blur : true,
11842         /**
11843          * @event specialkey
11844          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11845          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11846          * @param {Roo.form.Field} this
11847          * @param {Roo.EventObject} e The event object
11848          */
11849         specialkey : true,
11850         /**
11851          * @event change
11852          * Fires just before the field blurs if the field value has changed.
11853          * @param {Roo.form.Field} this
11854          * @param {Mixed} newValue The new value
11855          * @param {Mixed} oldValue The original value
11856          */
11857         change : true,
11858         /**
11859          * @event invalid
11860          * Fires after the field has been marked as invalid.
11861          * @param {Roo.form.Field} this
11862          * @param {String} msg The validation message
11863          */
11864         invalid : true,
11865         /**
11866          * @event valid
11867          * Fires after the field has been validated with no errors.
11868          * @param {Roo.form.Field} this
11869          */
11870         valid : true,
11871          /**
11872          * @event keyup
11873          * Fires after the key up
11874          * @param {Roo.form.Field} this
11875          * @param {Roo.EventObject}  e The event Object
11876          */
11877         keyup : true,
11878         /**
11879          * @event paste
11880          * Fires after the user pastes into input
11881          * @param {Roo.form.Field} this
11882          * @param {Roo.EventObject}  e The event Object
11883          */
11884         paste : true
11885     });
11886 };
11887
11888 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11889      /**
11890      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11891       automatic validation (defaults to "keyup").
11892      */
11893     validationEvent : "keyup",
11894      /**
11895      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11896      */
11897     validateOnBlur : true,
11898     /**
11899      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11900      */
11901     validationDelay : 250,
11902      /**
11903      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11904      */
11905     focusClass : "x-form-focus",  // not needed???
11906     
11907        
11908     /**
11909      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11910      */
11911     invalidClass : "has-warning",
11912     
11913     /**
11914      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11915      */
11916     validClass : "has-success",
11917     
11918     /**
11919      * @cfg {Boolean} hasFeedback (true|false) default true
11920      */
11921     hasFeedback : true,
11922     
11923     /**
11924      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11925      */
11926     invalidFeedbackClass : "glyphicon-warning-sign",
11927     
11928     /**
11929      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11930      */
11931     validFeedbackClass : "glyphicon-ok",
11932     
11933     /**
11934      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11935      */
11936     selectOnFocus : false,
11937     
11938      /**
11939      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11940      */
11941     maskRe : null,
11942        /**
11943      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11944      */
11945     vtype : null,
11946     
11947       /**
11948      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11949      */
11950     disableKeyFilter : false,
11951     
11952        /**
11953      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11954      */
11955     disabled : false,
11956      /**
11957      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11958      */
11959     allowBlank : true,
11960     /**
11961      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11962      */
11963     blankText : "Please complete this mandatory field",
11964     
11965      /**
11966      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11967      */
11968     minLength : 0,
11969     /**
11970      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11971      */
11972     maxLength : Number.MAX_VALUE,
11973     /**
11974      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11975      */
11976     minLengthText : "The minimum length for this field is {0}",
11977     /**
11978      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11979      */
11980     maxLengthText : "The maximum length for this field is {0}",
11981   
11982     
11983     /**
11984      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11985      * If available, this function will be called only after the basic validators all return true, and will be passed the
11986      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11987      */
11988     validator : null,
11989     /**
11990      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11991      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11992      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11993      */
11994     regex : null,
11995     /**
11996      * @cfg {String} regexText -- Depricated - use Invalid Text
11997      */
11998     regexText : "",
11999     
12000     /**
12001      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12002      */
12003     invalidText : "",
12004     
12005     
12006     
12007     autocomplete: false,
12008     
12009     
12010     fieldLabel : '',
12011     inputType : 'text',
12012     
12013     name : false,
12014     placeholder: false,
12015     before : false,
12016     after : false,
12017     size : false,
12018     hasFocus : false,
12019     preventMark: false,
12020     isFormField : true,
12021     value : '',
12022     labelWidth : 2,
12023     labelAlign : false,
12024     readOnly : false,
12025     align : false,
12026     formatedValue : false,
12027     forceFeedback : false,
12028     
12029     indicatorpos : 'left',
12030     
12031     labellg : 0,
12032     labelmd : 0,
12033     labelsm : 0,
12034     labelxs : 0,
12035     
12036     capture : '',
12037     accept : '',
12038     
12039     parentLabelAlign : function()
12040     {
12041         var parent = this;
12042         while (parent.parent()) {
12043             parent = parent.parent();
12044             if (typeof(parent.labelAlign) !='undefined') {
12045                 return parent.labelAlign;
12046             }
12047         }
12048         return 'left';
12049         
12050     },
12051     
12052     getAutoCreate : function()
12053     {
12054         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12055         
12056         var id = Roo.id();
12057         
12058         var cfg = {};
12059         
12060         if(this.inputType != 'hidden'){
12061             cfg.cls = 'form-group' //input-group
12062         }
12063         
12064         var input =  {
12065             tag: 'input',
12066             id : id,
12067             type : this.inputType,
12068             value : this.value,
12069             cls : 'form-control',
12070             placeholder : this.placeholder || '',
12071             autocomplete : this.autocomplete || 'new-password'
12072         };
12073         if (this.inputType == 'file') {
12074             input.style = 'overflow:hidden'; // why not in CSS?
12075         }
12076         
12077         if(this.capture.length){
12078             input.capture = this.capture;
12079         }
12080         
12081         if(this.accept.length){
12082             input.accept = this.accept + "/*";
12083         }
12084         
12085         if(this.align){
12086             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12087         }
12088         
12089         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12090             input.maxLength = this.maxLength;
12091         }
12092         
12093         if (this.disabled) {
12094             input.disabled=true;
12095         }
12096         
12097         if (this.readOnly) {
12098             input.readonly=true;
12099         }
12100         
12101         if (this.name) {
12102             input.name = this.name;
12103         }
12104         
12105         if (this.size) {
12106             input.cls += ' input-' + this.size;
12107         }
12108         
12109         var settings=this;
12110         ['xs','sm','md','lg'].map(function(size){
12111             if (settings[size]) {
12112                 cfg.cls += ' col-' + size + '-' + settings[size];
12113             }
12114         });
12115         
12116         var inputblock = input;
12117         
12118         var feedback = {
12119             tag: 'span',
12120             cls: 'glyphicon form-control-feedback'
12121         };
12122             
12123         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12124             
12125             inputblock = {
12126                 cls : 'has-feedback',
12127                 cn :  [
12128                     input,
12129                     feedback
12130                 ] 
12131             };  
12132         }
12133         
12134         if (this.before || this.after) {
12135             
12136             inputblock = {
12137                 cls : 'input-group',
12138                 cn :  [] 
12139             };
12140             
12141             if (this.before && typeof(this.before) == 'string') {
12142                 
12143                 inputblock.cn.push({
12144                     tag :'span',
12145                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12146                     html : this.before
12147                 });
12148             }
12149             if (this.before && typeof(this.before) == 'object') {
12150                 this.before = Roo.factory(this.before);
12151                 
12152                 inputblock.cn.push({
12153                     tag :'span',
12154                     cls : 'roo-input-before input-group-prepend   input-group-' +
12155                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12156                 });
12157             }
12158             
12159             inputblock.cn.push(input);
12160             
12161             if (this.after && typeof(this.after) == 'string') {
12162                 inputblock.cn.push({
12163                     tag :'span',
12164                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12165                     html : this.after
12166                 });
12167             }
12168             if (this.after && typeof(this.after) == 'object') {
12169                 this.after = Roo.factory(this.after);
12170                 
12171                 inputblock.cn.push({
12172                     tag :'span',
12173                     cls : 'roo-input-after input-group-append  input-group-' +
12174                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12175                 });
12176             }
12177             
12178             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12179                 inputblock.cls += ' has-feedback';
12180                 inputblock.cn.push(feedback);
12181             }
12182         };
12183         var indicator = {
12184             tag : 'i',
12185             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12186             tooltip : 'This field is required'
12187         };
12188         if (this.allowBlank ) {
12189             indicator.style = this.allowBlank ? ' display:none' : '';
12190         }
12191         if (align ==='left' && this.fieldLabel.length) {
12192             
12193             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12194             
12195             cfg.cn = [
12196                 indicator,
12197                 {
12198                     tag: 'label',
12199                     'for' :  id,
12200                     cls : 'control-label col-form-label',
12201                     html : this.fieldLabel
12202
12203                 },
12204                 {
12205                     cls : "", 
12206                     cn: [
12207                         inputblock
12208                     ]
12209                 }
12210             ];
12211             
12212             var labelCfg = cfg.cn[1];
12213             var contentCfg = cfg.cn[2];
12214             
12215             if(this.indicatorpos == 'right'){
12216                 cfg.cn = [
12217                     {
12218                         tag: 'label',
12219                         'for' :  id,
12220                         cls : 'control-label col-form-label',
12221                         cn : [
12222                             {
12223                                 tag : 'span',
12224                                 html : this.fieldLabel
12225                             },
12226                             indicator
12227                         ]
12228                     },
12229                     {
12230                         cls : "",
12231                         cn: [
12232                             inputblock
12233                         ]
12234                     }
12235
12236                 ];
12237                 
12238                 labelCfg = cfg.cn[0];
12239                 contentCfg = cfg.cn[1];
12240             
12241             }
12242             
12243             if(this.labelWidth > 12){
12244                 labelCfg.style = "width: " + this.labelWidth + 'px';
12245             }
12246             
12247             if(this.labelWidth < 13 && this.labelmd == 0){
12248                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12249             }
12250             
12251             if(this.labellg > 0){
12252                 labelCfg.cls += ' col-lg-' + this.labellg;
12253                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12254             }
12255             
12256             if(this.labelmd > 0){
12257                 labelCfg.cls += ' col-md-' + this.labelmd;
12258                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12259             }
12260             
12261             if(this.labelsm > 0){
12262                 labelCfg.cls += ' col-sm-' + this.labelsm;
12263                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12264             }
12265             
12266             if(this.labelxs > 0){
12267                 labelCfg.cls += ' col-xs-' + this.labelxs;
12268                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12269             }
12270             
12271             
12272         } else if ( this.fieldLabel.length) {
12273                 
12274             
12275             
12276             cfg.cn = [
12277                 {
12278                     tag : 'i',
12279                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12280                     tooltip : 'This field is required',
12281                     style : this.allowBlank ? ' display:none' : '' 
12282                 },
12283                 {
12284                     tag: 'label',
12285                    //cls : 'input-group-addon',
12286                     html : this.fieldLabel
12287
12288                 },
12289
12290                inputblock
12291
12292            ];
12293            
12294            if(this.indicatorpos == 'right'){
12295        
12296                 cfg.cn = [
12297                     {
12298                         tag: 'label',
12299                        //cls : 'input-group-addon',
12300                         html : this.fieldLabel
12301
12302                     },
12303                     {
12304                         tag : 'i',
12305                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12306                         tooltip : 'This field is required',
12307                         style : this.allowBlank ? ' display:none' : '' 
12308                     },
12309
12310                    inputblock
12311
12312                ];
12313
12314             }
12315
12316         } else {
12317             
12318             cfg.cn = [
12319
12320                     inputblock
12321
12322             ];
12323                 
12324                 
12325         };
12326         
12327         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12328            cfg.cls += ' navbar-form';
12329         }
12330         
12331         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12332             // on BS4 we do this only if not form 
12333             cfg.cls += ' navbar-form';
12334             cfg.tag = 'li';
12335         }
12336         
12337         return cfg;
12338         
12339     },
12340     /**
12341      * return the real input element.
12342      */
12343     inputEl: function ()
12344     {
12345         return this.el.select('input.form-control',true).first();
12346     },
12347     
12348     tooltipEl : function()
12349     {
12350         return this.inputEl();
12351     },
12352     
12353     indicatorEl : function()
12354     {
12355         if (Roo.bootstrap.version == 4) {
12356             return false; // not enabled in v4 yet.
12357         }
12358         
12359         var indicator = this.el.select('i.roo-required-indicator',true).first();
12360         
12361         if(!indicator){
12362             return false;
12363         }
12364         
12365         return indicator;
12366         
12367     },
12368     
12369     setDisabled : function(v)
12370     {
12371         var i  = this.inputEl().dom;
12372         if (!v) {
12373             i.removeAttribute('disabled');
12374             return;
12375             
12376         }
12377         i.setAttribute('disabled','true');
12378     },
12379     initEvents : function()
12380     {
12381           
12382         this.inputEl().on("keydown" , this.fireKey,  this);
12383         this.inputEl().on("focus", this.onFocus,  this);
12384         this.inputEl().on("blur", this.onBlur,  this);
12385         
12386         this.inputEl().relayEvent('keyup', this);
12387         this.inputEl().relayEvent('paste', this);
12388         
12389         this.indicator = this.indicatorEl();
12390         
12391         if(this.indicator){
12392             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12393         }
12394  
12395         // reference to original value for reset
12396         this.originalValue = this.getValue();
12397         //Roo.form.TextField.superclass.initEvents.call(this);
12398         if(this.validationEvent == 'keyup'){
12399             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12400             this.inputEl().on('keyup', this.filterValidation, this);
12401         }
12402         else if(this.validationEvent !== false){
12403             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12404         }
12405         
12406         if(this.selectOnFocus){
12407             this.on("focus", this.preFocus, this);
12408             
12409         }
12410         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12411             this.inputEl().on("keypress", this.filterKeys, this);
12412         } else {
12413             this.inputEl().relayEvent('keypress', this);
12414         }
12415        /* if(this.grow){
12416             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12417             this.el.on("click", this.autoSize,  this);
12418         }
12419         */
12420         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12421             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12422         }
12423         
12424         if (typeof(this.before) == 'object') {
12425             this.before.render(this.el.select('.roo-input-before',true).first());
12426         }
12427         if (typeof(this.after) == 'object') {
12428             this.after.render(this.el.select('.roo-input-after',true).first());
12429         }
12430         
12431         this.inputEl().on('change', this.onChange, this);
12432         
12433     },
12434     filterValidation : function(e){
12435         if(!e.isNavKeyPress()){
12436             this.validationTask.delay(this.validationDelay);
12437         }
12438     },
12439      /**
12440      * Validates the field value
12441      * @return {Boolean} True if the value is valid, else false
12442      */
12443     validate : function(){
12444         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12445         if(this.disabled || this.validateValue(this.getRawValue())){
12446             this.markValid();
12447             return true;
12448         }
12449         
12450         this.markInvalid();
12451         return false;
12452     },
12453     
12454     
12455     /**
12456      * Validates a value according to the field's validation rules and marks the field as invalid
12457      * if the validation fails
12458      * @param {Mixed} value The value to validate
12459      * @return {Boolean} True if the value is valid, else false
12460      */
12461     validateValue : function(value)
12462     {
12463         if(this.getVisibilityEl().hasClass('hidden')){
12464             return true;
12465         }
12466         
12467         if(value.length < 1)  { // if it's blank
12468             if(this.allowBlank){
12469                 return true;
12470             }
12471             return false;
12472         }
12473         
12474         if(value.length < this.minLength){
12475             return false;
12476         }
12477         if(value.length > this.maxLength){
12478             return false;
12479         }
12480         if(this.vtype){
12481             var vt = Roo.form.VTypes;
12482             if(!vt[this.vtype](value, this)){
12483                 return false;
12484             }
12485         }
12486         if(typeof this.validator == "function"){
12487             var msg = this.validator(value);
12488             if(msg !== true){
12489                 return false;
12490             }
12491             if (typeof(msg) == 'string') {
12492                 this.invalidText = msg;
12493             }
12494         }
12495         
12496         if(this.regex && !this.regex.test(value)){
12497             return false;
12498         }
12499         
12500         return true;
12501     },
12502     
12503      // private
12504     fireKey : function(e){
12505         //Roo.log('field ' + e.getKey());
12506         if(e.isNavKeyPress()){
12507             this.fireEvent("specialkey", this, e);
12508         }
12509     },
12510     focus : function (selectText){
12511         if(this.rendered){
12512             this.inputEl().focus();
12513             if(selectText === true){
12514                 this.inputEl().dom.select();
12515             }
12516         }
12517         return this;
12518     } ,
12519     
12520     onFocus : function(){
12521         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12522            // this.el.addClass(this.focusClass);
12523         }
12524         if(!this.hasFocus){
12525             this.hasFocus = true;
12526             this.startValue = this.getValue();
12527             this.fireEvent("focus", this);
12528         }
12529     },
12530     
12531     beforeBlur : Roo.emptyFn,
12532
12533     
12534     // private
12535     onBlur : function(){
12536         this.beforeBlur();
12537         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12538             //this.el.removeClass(this.focusClass);
12539         }
12540         this.hasFocus = false;
12541         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12542             this.validate();
12543         }
12544         var v = this.getValue();
12545         if(String(v) !== String(this.startValue)){
12546             this.fireEvent('change', this, v, this.startValue);
12547         }
12548         this.fireEvent("blur", this);
12549     },
12550     
12551     onChange : function(e)
12552     {
12553         var v = this.getValue();
12554         if(String(v) !== String(this.startValue)){
12555             this.fireEvent('change', this, v, this.startValue);
12556         }
12557         
12558     },
12559     
12560     /**
12561      * Resets the current field value to the originally loaded value and clears any validation messages
12562      */
12563     reset : function(){
12564         this.setValue(this.originalValue);
12565         this.validate();
12566     },
12567      /**
12568      * Returns the name of the field
12569      * @return {Mixed} name The name field
12570      */
12571     getName: function(){
12572         return this.name;
12573     },
12574      /**
12575      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12576      * @return {Mixed} value The field value
12577      */
12578     getValue : function(){
12579         
12580         var v = this.inputEl().getValue();
12581         
12582         return v;
12583     },
12584     /**
12585      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12586      * @return {Mixed} value The field value
12587      */
12588     getRawValue : function(){
12589         var v = this.inputEl().getValue();
12590         
12591         return v;
12592     },
12593     
12594     /**
12595      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12596      * @param {Mixed} value The value to set
12597      */
12598     setRawValue : function(v){
12599         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12600     },
12601     
12602     selectText : function(start, end){
12603         var v = this.getRawValue();
12604         if(v.length > 0){
12605             start = start === undefined ? 0 : start;
12606             end = end === undefined ? v.length : end;
12607             var d = this.inputEl().dom;
12608             if(d.setSelectionRange){
12609                 d.setSelectionRange(start, end);
12610             }else if(d.createTextRange){
12611                 var range = d.createTextRange();
12612                 range.moveStart("character", start);
12613                 range.moveEnd("character", v.length-end);
12614                 range.select();
12615             }
12616         }
12617     },
12618     
12619     /**
12620      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12621      * @param {Mixed} value The value to set
12622      */
12623     setValue : function(v){
12624         this.value = v;
12625         if(this.rendered){
12626             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12627             this.validate();
12628         }
12629     },
12630     
12631     /*
12632     processValue : function(value){
12633         if(this.stripCharsRe){
12634             var newValue = value.replace(this.stripCharsRe, '');
12635             if(newValue !== value){
12636                 this.setRawValue(newValue);
12637                 return newValue;
12638             }
12639         }
12640         return value;
12641     },
12642   */
12643     preFocus : function(){
12644         
12645         if(this.selectOnFocus){
12646             this.inputEl().dom.select();
12647         }
12648     },
12649     filterKeys : function(e){
12650         var k = e.getKey();
12651         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12652             return;
12653         }
12654         var c = e.getCharCode(), cc = String.fromCharCode(c);
12655         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12656             return;
12657         }
12658         if(!this.maskRe.test(cc)){
12659             e.stopEvent();
12660         }
12661     },
12662      /**
12663      * Clear any invalid styles/messages for this field
12664      */
12665     clearInvalid : function(){
12666         
12667         if(!this.el || this.preventMark){ // not rendered
12668             return;
12669         }
12670         
12671         
12672         this.el.removeClass([this.invalidClass, 'is-invalid']);
12673         
12674         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12675             
12676             var feedback = this.el.select('.form-control-feedback', true).first();
12677             
12678             if(feedback){
12679                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12680             }
12681             
12682         }
12683         
12684         if(this.indicator){
12685             this.indicator.removeClass('visible');
12686             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12687         }
12688         
12689         this.fireEvent('valid', this);
12690     },
12691     
12692      /**
12693      * Mark this field as valid
12694      */
12695     markValid : function()
12696     {
12697         if(!this.el  || this.preventMark){ // not rendered...
12698             return;
12699         }
12700         
12701         this.el.removeClass([this.invalidClass, this.validClass]);
12702         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12703
12704         var feedback = this.el.select('.form-control-feedback', true).first();
12705             
12706         if(feedback){
12707             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12708         }
12709         
12710         if(this.indicator){
12711             this.indicator.removeClass('visible');
12712             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12713         }
12714         
12715         if(this.disabled){
12716             return;
12717         }
12718         
12719            
12720         if(this.allowBlank && !this.getRawValue().length){
12721             return;
12722         }
12723         if (Roo.bootstrap.version == 3) {
12724             this.el.addClass(this.validClass);
12725         } else {
12726             this.inputEl().addClass('is-valid');
12727         }
12728
12729         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12730             
12731             var feedback = this.el.select('.form-control-feedback', true).first();
12732             
12733             if(feedback){
12734                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12735                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12736             }
12737             
12738         }
12739         
12740         this.fireEvent('valid', this);
12741     },
12742     
12743      /**
12744      * Mark this field as invalid
12745      * @param {String} msg The validation message
12746      */
12747     markInvalid : function(msg)
12748     {
12749         if(!this.el  || this.preventMark){ // not rendered
12750             return;
12751         }
12752         
12753         this.el.removeClass([this.invalidClass, this.validClass]);
12754         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12755         
12756         var feedback = this.el.select('.form-control-feedback', true).first();
12757             
12758         if(feedback){
12759             this.el.select('.form-control-feedback', true).first().removeClass(
12760                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12761         }
12762
12763         if(this.disabled){
12764             return;
12765         }
12766         
12767         if(this.allowBlank && !this.getRawValue().length){
12768             return;
12769         }
12770         
12771         if(this.indicator){
12772             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12773             this.indicator.addClass('visible');
12774         }
12775         if (Roo.bootstrap.version == 3) {
12776             this.el.addClass(this.invalidClass);
12777         } else {
12778             this.inputEl().addClass('is-invalid');
12779         }
12780         
12781         
12782         
12783         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12784             
12785             var feedback = this.el.select('.form-control-feedback', true).first();
12786             
12787             if(feedback){
12788                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12789                 
12790                 if(this.getValue().length || this.forceFeedback){
12791                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12792                 }
12793                 
12794             }
12795             
12796         }
12797         
12798         this.fireEvent('invalid', this, msg);
12799     },
12800     // private
12801     SafariOnKeyDown : function(event)
12802     {
12803         // this is a workaround for a password hang bug on chrome/ webkit.
12804         if (this.inputEl().dom.type != 'password') {
12805             return;
12806         }
12807         
12808         var isSelectAll = false;
12809         
12810         if(this.inputEl().dom.selectionEnd > 0){
12811             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12812         }
12813         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12814             event.preventDefault();
12815             this.setValue('');
12816             return;
12817         }
12818         
12819         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12820             
12821             event.preventDefault();
12822             // this is very hacky as keydown always get's upper case.
12823             //
12824             var cc = String.fromCharCode(event.getCharCode());
12825             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12826             
12827         }
12828     },
12829     adjustWidth : function(tag, w){
12830         tag = tag.toLowerCase();
12831         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12832             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12833                 if(tag == 'input'){
12834                     return w + 2;
12835                 }
12836                 if(tag == 'textarea'){
12837                     return w-2;
12838                 }
12839             }else if(Roo.isOpera){
12840                 if(tag == 'input'){
12841                     return w + 2;
12842                 }
12843                 if(tag == 'textarea'){
12844                     return w-2;
12845                 }
12846             }
12847         }
12848         return w;
12849     },
12850     
12851     setFieldLabel : function(v)
12852     {
12853         if(!this.rendered){
12854             return;
12855         }
12856         
12857         if(this.indicatorEl()){
12858             var ar = this.el.select('label > span',true);
12859             
12860             if (ar.elements.length) {
12861                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12862                 this.fieldLabel = v;
12863                 return;
12864             }
12865             
12866             var br = this.el.select('label',true);
12867             
12868             if(br.elements.length) {
12869                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12870                 this.fieldLabel = v;
12871                 return;
12872             }
12873             
12874             Roo.log('Cannot Found any of label > span || label in input');
12875             return;
12876         }
12877         
12878         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12879         this.fieldLabel = v;
12880         
12881         
12882     }
12883 });
12884
12885  
12886 /*
12887  * - LGPL
12888  *
12889  * Input
12890  * 
12891  */
12892
12893 /**
12894  * @class Roo.bootstrap.TextArea
12895  * @extends Roo.bootstrap.Input
12896  * Bootstrap TextArea class
12897  * @cfg {Number} cols Specifies the visible width of a text area
12898  * @cfg {Number} rows Specifies the visible number of lines in a text area
12899  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12900  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12901  * @cfg {string} html text
12902  * 
12903  * @constructor
12904  * Create a new TextArea
12905  * @param {Object} config The config object
12906  */
12907
12908 Roo.bootstrap.TextArea = function(config){
12909     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12910    
12911 };
12912
12913 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12914      
12915     cols : false,
12916     rows : 5,
12917     readOnly : false,
12918     warp : 'soft',
12919     resize : false,
12920     value: false,
12921     html: false,
12922     
12923     getAutoCreate : function(){
12924         
12925         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12926         
12927         var id = Roo.id();
12928         
12929         var cfg = {};
12930         
12931         if(this.inputType != 'hidden'){
12932             cfg.cls = 'form-group' //input-group
12933         }
12934         
12935         var input =  {
12936             tag: 'textarea',
12937             id : id,
12938             warp : this.warp,
12939             rows : this.rows,
12940             value : this.value || '',
12941             html: this.html || '',
12942             cls : 'form-control',
12943             placeholder : this.placeholder || '' 
12944             
12945         };
12946         
12947         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12948             input.maxLength = this.maxLength;
12949         }
12950         
12951         if(this.resize){
12952             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12953         }
12954         
12955         if(this.cols){
12956             input.cols = this.cols;
12957         }
12958         
12959         if (this.readOnly) {
12960             input.readonly = true;
12961         }
12962         
12963         if (this.name) {
12964             input.name = this.name;
12965         }
12966         
12967         if (this.size) {
12968             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12969         }
12970         
12971         var settings=this;
12972         ['xs','sm','md','lg'].map(function(size){
12973             if (settings[size]) {
12974                 cfg.cls += ' col-' + size + '-' + settings[size];
12975             }
12976         });
12977         
12978         var inputblock = input;
12979         
12980         if(this.hasFeedback && !this.allowBlank){
12981             
12982             var feedback = {
12983                 tag: 'span',
12984                 cls: 'glyphicon form-control-feedback'
12985             };
12986
12987             inputblock = {
12988                 cls : 'has-feedback',
12989                 cn :  [
12990                     input,
12991                     feedback
12992                 ] 
12993             };  
12994         }
12995         
12996         
12997         if (this.before || this.after) {
12998             
12999             inputblock = {
13000                 cls : 'input-group',
13001                 cn :  [] 
13002             };
13003             if (this.before) {
13004                 inputblock.cn.push({
13005                     tag :'span',
13006                     cls : 'input-group-addon',
13007                     html : this.before
13008                 });
13009             }
13010             
13011             inputblock.cn.push(input);
13012             
13013             if(this.hasFeedback && !this.allowBlank){
13014                 inputblock.cls += ' has-feedback';
13015                 inputblock.cn.push(feedback);
13016             }
13017             
13018             if (this.after) {
13019                 inputblock.cn.push({
13020                     tag :'span',
13021                     cls : 'input-group-addon',
13022                     html : this.after
13023                 });
13024             }
13025             
13026         }
13027         
13028         if (align ==='left' && this.fieldLabel.length) {
13029             cfg.cn = [
13030                 {
13031                     tag: 'label',
13032                     'for' :  id,
13033                     cls : 'control-label',
13034                     html : this.fieldLabel
13035                 },
13036                 {
13037                     cls : "",
13038                     cn: [
13039                         inputblock
13040                     ]
13041                 }
13042
13043             ];
13044             
13045             if(this.labelWidth > 12){
13046                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13047             }
13048
13049             if(this.labelWidth < 13 && this.labelmd == 0){
13050                 this.labelmd = this.labelWidth;
13051             }
13052
13053             if(this.labellg > 0){
13054                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13055                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13056             }
13057
13058             if(this.labelmd > 0){
13059                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13060                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13061             }
13062
13063             if(this.labelsm > 0){
13064                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13065                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13066             }
13067
13068             if(this.labelxs > 0){
13069                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13070                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13071             }
13072             
13073         } else if ( this.fieldLabel.length) {
13074             cfg.cn = [
13075
13076                {
13077                    tag: 'label',
13078                    //cls : 'input-group-addon',
13079                    html : this.fieldLabel
13080
13081                },
13082
13083                inputblock
13084
13085            ];
13086
13087         } else {
13088
13089             cfg.cn = [
13090
13091                 inputblock
13092
13093             ];
13094                 
13095         }
13096         
13097         if (this.disabled) {
13098             input.disabled=true;
13099         }
13100         
13101         return cfg;
13102         
13103     },
13104     /**
13105      * return the real textarea element.
13106      */
13107     inputEl: function ()
13108     {
13109         return this.el.select('textarea.form-control',true).first();
13110     },
13111     
13112     /**
13113      * Clear any invalid styles/messages for this field
13114      */
13115     clearInvalid : function()
13116     {
13117         
13118         if(!this.el || this.preventMark){ // not rendered
13119             return;
13120         }
13121         
13122         var label = this.el.select('label', true).first();
13123         var icon = this.el.select('i.fa-star', true).first();
13124         
13125         if(label && icon){
13126             icon.remove();
13127         }
13128         this.el.removeClass( this.validClass);
13129         this.inputEl().removeClass('is-invalid');
13130          
13131         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13132             
13133             var feedback = this.el.select('.form-control-feedback', true).first();
13134             
13135             if(feedback){
13136                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13137             }
13138             
13139         }
13140         
13141         this.fireEvent('valid', this);
13142     },
13143     
13144      /**
13145      * Mark this field as valid
13146      */
13147     markValid : function()
13148     {
13149         if(!this.el  || this.preventMark){ // not rendered
13150             return;
13151         }
13152         
13153         this.el.removeClass([this.invalidClass, this.validClass]);
13154         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13155         
13156         var feedback = this.el.select('.form-control-feedback', true).first();
13157             
13158         if(feedback){
13159             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13160         }
13161
13162         if(this.disabled || this.allowBlank){
13163             return;
13164         }
13165         
13166         var label = this.el.select('label', true).first();
13167         var icon = this.el.select('i.fa-star', true).first();
13168         
13169         if(label && icon){
13170             icon.remove();
13171         }
13172         if (Roo.bootstrap.version == 3) {
13173             this.el.addClass(this.validClass);
13174         } else {
13175             this.inputEl().addClass('is-valid');
13176         }
13177         
13178         
13179         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13180             
13181             var feedback = this.el.select('.form-control-feedback', true).first();
13182             
13183             if(feedback){
13184                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13185                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13186             }
13187             
13188         }
13189         
13190         this.fireEvent('valid', this);
13191     },
13192     
13193      /**
13194      * Mark this field as invalid
13195      * @param {String} msg The validation message
13196      */
13197     markInvalid : function(msg)
13198     {
13199         if(!this.el  || this.preventMark){ // not rendered
13200             return;
13201         }
13202         
13203         this.el.removeClass([this.invalidClass, this.validClass]);
13204         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13205         
13206         var feedback = this.el.select('.form-control-feedback', true).first();
13207             
13208         if(feedback){
13209             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13210         }
13211
13212         if(this.disabled || this.allowBlank){
13213             return;
13214         }
13215         
13216         var label = this.el.select('label', true).first();
13217         var icon = this.el.select('i.fa-star', true).first();
13218         
13219         if(!this.getValue().length && label && !icon){
13220             this.el.createChild({
13221                 tag : 'i',
13222                 cls : 'text-danger fa fa-lg fa-star',
13223                 tooltip : 'This field is required',
13224                 style : 'margin-right:5px;'
13225             }, label, true);
13226         }
13227         
13228         if (Roo.bootstrap.version == 3) {
13229             this.el.addClass(this.invalidClass);
13230         } else {
13231             this.inputEl().addClass('is-invalid');
13232         }
13233         
13234         // fixme ... this may be depricated need to test..
13235         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13236             
13237             var feedback = this.el.select('.form-control-feedback', true).first();
13238             
13239             if(feedback){
13240                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13241                 
13242                 if(this.getValue().length || this.forceFeedback){
13243                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13244                 }
13245                 
13246             }
13247             
13248         }
13249         
13250         this.fireEvent('invalid', this, msg);
13251     }
13252 });
13253
13254  
13255 /*
13256  * - LGPL
13257  *
13258  * trigger field - base class for combo..
13259  * 
13260  */
13261  
13262 /**
13263  * @class Roo.bootstrap.TriggerField
13264  * @extends Roo.bootstrap.Input
13265  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13266  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13267  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13268  * for which you can provide a custom implementation.  For example:
13269  * <pre><code>
13270 var trigger = new Roo.bootstrap.TriggerField();
13271 trigger.onTriggerClick = myTriggerFn;
13272 trigger.applyTo('my-field');
13273 </code></pre>
13274  *
13275  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13276  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13277  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13278  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13279  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13280
13281  * @constructor
13282  * Create a new TriggerField.
13283  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13284  * to the base TextField)
13285  */
13286 Roo.bootstrap.TriggerField = function(config){
13287     this.mimicing = false;
13288     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13289 };
13290
13291 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13292     /**
13293      * @cfg {String} triggerClass A CSS class to apply to the trigger
13294      */
13295      /**
13296      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13297      */
13298     hideTrigger:false,
13299
13300     /**
13301      * @cfg {Boolean} removable (true|false) special filter default false
13302      */
13303     removable : false,
13304     
13305     /** @cfg {Boolean} grow @hide */
13306     /** @cfg {Number} growMin @hide */
13307     /** @cfg {Number} growMax @hide */
13308
13309     /**
13310      * @hide 
13311      * @method
13312      */
13313     autoSize: Roo.emptyFn,
13314     // private
13315     monitorTab : true,
13316     // private
13317     deferHeight : true,
13318
13319     
13320     actionMode : 'wrap',
13321     
13322     caret : false,
13323     
13324     
13325     getAutoCreate : function(){
13326        
13327         var align = this.labelAlign || this.parentLabelAlign();
13328         
13329         var id = Roo.id();
13330         
13331         var cfg = {
13332             cls: 'form-group' //input-group
13333         };
13334         
13335         
13336         var input =  {
13337             tag: 'input',
13338             id : id,
13339             type : this.inputType,
13340             cls : 'form-control',
13341             autocomplete: 'new-password',
13342             placeholder : this.placeholder || '' 
13343             
13344         };
13345         if (this.name) {
13346             input.name = this.name;
13347         }
13348         if (this.size) {
13349             input.cls += ' input-' + this.size;
13350         }
13351         
13352         if (this.disabled) {
13353             input.disabled=true;
13354         }
13355         
13356         var inputblock = input;
13357         
13358         if(this.hasFeedback && !this.allowBlank){
13359             
13360             var feedback = {
13361                 tag: 'span',
13362                 cls: 'glyphicon form-control-feedback'
13363             };
13364             
13365             if(this.removable && !this.editable  ){
13366                 inputblock = {
13367                     cls : 'has-feedback',
13368                     cn :  [
13369                         inputblock,
13370                         {
13371                             tag: 'button',
13372                             html : 'x',
13373                             cls : 'roo-combo-removable-btn close'
13374                         },
13375                         feedback
13376                     ] 
13377                 };
13378             } else {
13379                 inputblock = {
13380                     cls : 'has-feedback',
13381                     cn :  [
13382                         inputblock,
13383                         feedback
13384                     ] 
13385                 };
13386             }
13387
13388         } else {
13389             if(this.removable && !this.editable ){
13390                 inputblock = {
13391                     cls : 'roo-removable',
13392                     cn :  [
13393                         inputblock,
13394                         {
13395                             tag: 'button',
13396                             html : 'x',
13397                             cls : 'roo-combo-removable-btn close'
13398                         }
13399                     ] 
13400                 };
13401             }
13402         }
13403         
13404         if (this.before || this.after) {
13405             
13406             inputblock = {
13407                 cls : 'input-group',
13408                 cn :  [] 
13409             };
13410             if (this.before) {
13411                 inputblock.cn.push({
13412                     tag :'span',
13413                     cls : 'input-group-addon input-group-prepend input-group-text',
13414                     html : this.before
13415                 });
13416             }
13417             
13418             inputblock.cn.push(input);
13419             
13420             if(this.hasFeedback && !this.allowBlank){
13421                 inputblock.cls += ' has-feedback';
13422                 inputblock.cn.push(feedback);
13423             }
13424             
13425             if (this.after) {
13426                 inputblock.cn.push({
13427                     tag :'span',
13428                     cls : 'input-group-addon input-group-append input-group-text',
13429                     html : this.after
13430                 });
13431             }
13432             
13433         };
13434         
13435       
13436         
13437         var ibwrap = inputblock;
13438         
13439         if(this.multiple){
13440             ibwrap = {
13441                 tag: 'ul',
13442                 cls: 'roo-select2-choices',
13443                 cn:[
13444                     {
13445                         tag: 'li',
13446                         cls: 'roo-select2-search-field',
13447                         cn: [
13448
13449                             inputblock
13450                         ]
13451                     }
13452                 ]
13453             };
13454                 
13455         }
13456         
13457         var combobox = {
13458             cls: 'roo-select2-container input-group',
13459             cn: [
13460                  {
13461                     tag: 'input',
13462                     type : 'hidden',
13463                     cls: 'form-hidden-field'
13464                 },
13465                 ibwrap
13466             ]
13467         };
13468         
13469         if(!this.multiple && this.showToggleBtn){
13470             
13471             var caret = {
13472                         tag: 'span',
13473                         cls: 'caret'
13474              };
13475             if (this.caret != false) {
13476                 caret = {
13477                      tag: 'i',
13478                      cls: 'fa fa-' + this.caret
13479                 };
13480                 
13481             }
13482             
13483             combobox.cn.push({
13484                 tag :'span',
13485                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13486                 cn : [
13487                     Roo.bootstrap.version == 3 ? caret : '',
13488                     {
13489                         tag: 'span',
13490                         cls: 'combobox-clear',
13491                         cn  : [
13492                             {
13493                                 tag : 'i',
13494                                 cls: 'icon-remove'
13495                             }
13496                         ]
13497                     }
13498                 ]
13499
13500             })
13501         }
13502         
13503         if(this.multiple){
13504             combobox.cls += ' roo-select2-container-multi';
13505         }
13506          var indicator = {
13507             tag : 'i',
13508             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13509             tooltip : 'This field is required'
13510         };
13511         if (Roo.bootstrap.version == 4) {
13512             indicator = {
13513                 tag : 'i',
13514                 style : 'display:none'
13515             };
13516         }
13517         
13518         
13519         if (align ==='left' && this.fieldLabel.length) {
13520             
13521             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13522
13523             cfg.cn = [
13524                 indicator,
13525                 {
13526                     tag: 'label',
13527                     'for' :  id,
13528                     cls : 'control-label',
13529                     html : this.fieldLabel
13530
13531                 },
13532                 {
13533                     cls : "", 
13534                     cn: [
13535                         combobox
13536                     ]
13537                 }
13538
13539             ];
13540             
13541             var labelCfg = cfg.cn[1];
13542             var contentCfg = cfg.cn[2];
13543             
13544             if(this.indicatorpos == 'right'){
13545                 cfg.cn = [
13546                     {
13547                         tag: 'label',
13548                         'for' :  id,
13549                         cls : 'control-label',
13550                         cn : [
13551                             {
13552                                 tag : 'span',
13553                                 html : this.fieldLabel
13554                             },
13555                             indicator
13556                         ]
13557                     },
13558                     {
13559                         cls : "", 
13560                         cn: [
13561                             combobox
13562                         ]
13563                     }
13564
13565                 ];
13566                 
13567                 labelCfg = cfg.cn[0];
13568                 contentCfg = cfg.cn[1];
13569             }
13570             
13571             if(this.labelWidth > 12){
13572                 labelCfg.style = "width: " + this.labelWidth + 'px';
13573             }
13574             
13575             if(this.labelWidth < 13 && this.labelmd == 0){
13576                 this.labelmd = this.labelWidth;
13577             }
13578             
13579             if(this.labellg > 0){
13580                 labelCfg.cls += ' col-lg-' + this.labellg;
13581                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13582             }
13583             
13584             if(this.labelmd > 0){
13585                 labelCfg.cls += ' col-md-' + this.labelmd;
13586                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13587             }
13588             
13589             if(this.labelsm > 0){
13590                 labelCfg.cls += ' col-sm-' + this.labelsm;
13591                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13592             }
13593             
13594             if(this.labelxs > 0){
13595                 labelCfg.cls += ' col-xs-' + this.labelxs;
13596                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13597             }
13598             
13599         } else if ( this.fieldLabel.length) {
13600 //                Roo.log(" label");
13601             cfg.cn = [
13602                 indicator,
13603                {
13604                    tag: 'label',
13605                    //cls : 'input-group-addon',
13606                    html : this.fieldLabel
13607
13608                },
13609
13610                combobox
13611
13612             ];
13613             
13614             if(this.indicatorpos == 'right'){
13615                 
13616                 cfg.cn = [
13617                     {
13618                        tag: 'label',
13619                        cn : [
13620                            {
13621                                tag : 'span',
13622                                html : this.fieldLabel
13623                            },
13624                            indicator
13625                        ]
13626
13627                     },
13628                     combobox
13629
13630                 ];
13631
13632             }
13633
13634         } else {
13635             
13636 //                Roo.log(" no label && no align");
13637                 cfg = combobox
13638                      
13639                 
13640         }
13641         
13642         var settings=this;
13643         ['xs','sm','md','lg'].map(function(size){
13644             if (settings[size]) {
13645                 cfg.cls += ' col-' + size + '-' + settings[size];
13646             }
13647         });
13648         
13649         return cfg;
13650         
13651     },
13652     
13653     
13654     
13655     // private
13656     onResize : function(w, h){
13657 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13658 //        if(typeof w == 'number'){
13659 //            var x = w - this.trigger.getWidth();
13660 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13661 //            this.trigger.setStyle('left', x+'px');
13662 //        }
13663     },
13664
13665     // private
13666     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13667
13668     // private
13669     getResizeEl : function(){
13670         return this.inputEl();
13671     },
13672
13673     // private
13674     getPositionEl : function(){
13675         return this.inputEl();
13676     },
13677
13678     // private
13679     alignErrorIcon : function(){
13680         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13681     },
13682
13683     // private
13684     initEvents : function(){
13685         
13686         this.createList();
13687         
13688         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13689         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13690         if(!this.multiple && this.showToggleBtn){
13691             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13692             if(this.hideTrigger){
13693                 this.trigger.setDisplayed(false);
13694             }
13695             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13696         }
13697         
13698         if(this.multiple){
13699             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13700         }
13701         
13702         if(this.removable && !this.editable && !this.tickable){
13703             var close = this.closeTriggerEl();
13704             
13705             if(close){
13706                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13707                 close.on('click', this.removeBtnClick, this, close);
13708             }
13709         }
13710         
13711         //this.trigger.addClassOnOver('x-form-trigger-over');
13712         //this.trigger.addClassOnClick('x-form-trigger-click');
13713         
13714         //if(!this.width){
13715         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13716         //}
13717     },
13718     
13719     closeTriggerEl : function()
13720     {
13721         var close = this.el.select('.roo-combo-removable-btn', true).first();
13722         return close ? close : false;
13723     },
13724     
13725     removeBtnClick : function(e, h, el)
13726     {
13727         e.preventDefault();
13728         
13729         if(this.fireEvent("remove", this) !== false){
13730             this.reset();
13731             this.fireEvent("afterremove", this)
13732         }
13733     },
13734     
13735     createList : function()
13736     {
13737         this.list = Roo.get(document.body).createChild({
13738             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13739             cls: 'typeahead typeahead-long dropdown-menu shadow',
13740             style: 'display:none'
13741         });
13742         
13743         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13744         
13745     },
13746
13747     // private
13748     initTrigger : function(){
13749        
13750     },
13751
13752     // private
13753     onDestroy : function(){
13754         if(this.trigger){
13755             this.trigger.removeAllListeners();
13756           //  this.trigger.remove();
13757         }
13758         //if(this.wrap){
13759         //    this.wrap.remove();
13760         //}
13761         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13762     },
13763
13764     // private
13765     onFocus : function(){
13766         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13767         /*
13768         if(!this.mimicing){
13769             this.wrap.addClass('x-trigger-wrap-focus');
13770             this.mimicing = true;
13771             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13772             if(this.monitorTab){
13773                 this.el.on("keydown", this.checkTab, this);
13774             }
13775         }
13776         */
13777     },
13778
13779     // private
13780     checkTab : function(e){
13781         if(e.getKey() == e.TAB){
13782             this.triggerBlur();
13783         }
13784     },
13785
13786     // private
13787     onBlur : function(){
13788         // do nothing
13789     },
13790
13791     // private
13792     mimicBlur : function(e, t){
13793         /*
13794         if(!this.wrap.contains(t) && this.validateBlur()){
13795             this.triggerBlur();
13796         }
13797         */
13798     },
13799
13800     // private
13801     triggerBlur : function(){
13802         this.mimicing = false;
13803         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13804         if(this.monitorTab){
13805             this.el.un("keydown", this.checkTab, this);
13806         }
13807         //this.wrap.removeClass('x-trigger-wrap-focus');
13808         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13809     },
13810
13811     // private
13812     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13813     validateBlur : function(e, t){
13814         return true;
13815     },
13816
13817     // private
13818     onDisable : function(){
13819         this.inputEl().dom.disabled = true;
13820         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13821         //if(this.wrap){
13822         //    this.wrap.addClass('x-item-disabled');
13823         //}
13824     },
13825
13826     // private
13827     onEnable : function(){
13828         this.inputEl().dom.disabled = false;
13829         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13830         //if(this.wrap){
13831         //    this.el.removeClass('x-item-disabled');
13832         //}
13833     },
13834
13835     // private
13836     onShow : function(){
13837         var ae = this.getActionEl();
13838         
13839         if(ae){
13840             ae.dom.style.display = '';
13841             ae.dom.style.visibility = 'visible';
13842         }
13843     },
13844
13845     // private
13846     
13847     onHide : function(){
13848         var ae = this.getActionEl();
13849         ae.dom.style.display = 'none';
13850     },
13851
13852     /**
13853      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13854      * by an implementing function.
13855      * @method
13856      * @param {EventObject} e
13857      */
13858     onTriggerClick : Roo.emptyFn
13859 });
13860  
13861 /*
13862 * Licence: LGPL
13863 */
13864
13865 /**
13866  * @class Roo.bootstrap.CardUploader
13867  * @extends Roo.bootstrap.Button
13868  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13869  * @cfg {Number} errorTimeout default 3000
13870  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13871  * @cfg {Array}  html The button text.
13872
13873  *
13874  * @constructor
13875  * Create a new CardUploader
13876  * @param {Object} config The config object
13877  */
13878
13879 Roo.bootstrap.CardUploader = function(config){
13880     
13881  
13882     
13883     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13884     
13885     
13886     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13887         return r.data.id
13888      });
13889     
13890      this.addEvents({
13891          // raw events
13892         /**
13893          * @event preview
13894          * When a image is clicked on - and needs to display a slideshow or similar..
13895          * @param {Roo.bootstrap.Card} this
13896          * @param {Object} The image information data 
13897          *
13898          */
13899         'preview' : true,
13900          /**
13901          * @event download
13902          * When a the download link is clicked
13903          * @param {Roo.bootstrap.Card} this
13904          * @param {Object} The image information data  contains 
13905          */
13906         'download' : true
13907         
13908     });
13909 };
13910  
13911 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13912     
13913      
13914     errorTimeout : 3000,
13915      
13916     images : false,
13917    
13918     fileCollection : false,
13919     allowBlank : true,
13920     
13921     getAutoCreate : function()
13922     {
13923         
13924         var cfg =  {
13925             cls :'form-group' ,
13926             cn : [
13927                
13928                 {
13929                     tag: 'label',
13930                    //cls : 'input-group-addon',
13931                     html : this.fieldLabel
13932
13933                 },
13934
13935                 {
13936                     tag: 'input',
13937                     type : 'hidden',
13938                     name : this.name,
13939                     value : this.value,
13940                     cls : 'd-none  form-control'
13941                 },
13942                 
13943                 {
13944                     tag: 'input',
13945                     multiple : 'multiple',
13946                     type : 'file',
13947                     cls : 'd-none  roo-card-upload-selector'
13948                 },
13949                 
13950                 {
13951                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13952                 },
13953                 {
13954                     cls : 'card-columns roo-card-uploader-container'
13955                 }
13956
13957             ]
13958         };
13959            
13960          
13961         return cfg;
13962     },
13963     
13964     getChildContainer : function() /// what children are added to.
13965     {
13966         return this.containerEl;
13967     },
13968    
13969     getButtonContainer : function() /// what children are added to.
13970     {
13971         return this.el.select(".roo-card-uploader-button-container").first();
13972     },
13973    
13974     initEvents : function()
13975     {
13976         
13977         Roo.bootstrap.Input.prototype.initEvents.call(this);
13978         
13979         var t = this;
13980         this.addxtype({
13981             xns: Roo.bootstrap,
13982
13983             xtype : 'Button',
13984             container_method : 'getButtonContainer' ,            
13985             html :  this.html, // fix changable?
13986             cls : 'w-100 ',
13987             listeners : {
13988                 'click' : function(btn, e) {
13989                     t.onClick(e);
13990                 }
13991             }
13992         });
13993         
13994         
13995         
13996         
13997         this.urlAPI = (window.createObjectURL && window) || 
13998                                 (window.URL && URL.revokeObjectURL && URL) || 
13999                                 (window.webkitURL && webkitURL);
14000                         
14001          
14002          
14003          
14004         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14005         
14006         this.selectorEl.on('change', this.onFileSelected, this);
14007         if (this.images) {
14008             var t = this;
14009             this.images.forEach(function(img) {
14010                 t.addCard(img)
14011             });
14012             this.images = false;
14013         }
14014         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14015          
14016        
14017     },
14018     
14019    
14020     onClick : function(e)
14021     {
14022         e.preventDefault();
14023          
14024         this.selectorEl.dom.click();
14025          
14026     },
14027     
14028     onFileSelected : function(e)
14029     {
14030         e.preventDefault();
14031         
14032         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14033             return;
14034         }
14035         
14036         Roo.each(this.selectorEl.dom.files, function(file){    
14037             this.addFile(file);
14038         }, this);
14039          
14040     },
14041     
14042       
14043     
14044       
14045     
14046     addFile : function(file)
14047     {
14048            
14049         if(typeof(file) === 'string'){
14050             throw "Add file by name?"; // should not happen
14051             return;
14052         }
14053         
14054         if(!file || !this.urlAPI){
14055             return;
14056         }
14057         
14058         // file;
14059         // file.type;
14060         
14061         var _this = this;
14062         
14063         
14064         var url = _this.urlAPI.createObjectURL( file);
14065            
14066         this.addCard({
14067             id : Roo.bootstrap.CardUploader.ID--,
14068             is_uploaded : false,
14069             src : url,
14070             srcfile : file,
14071             title : file.name,
14072             mimetype : file.type,
14073             preview : false,
14074             is_deleted : 0
14075         });
14076         
14077     },
14078     
14079     /**
14080      * addCard - add an Attachment to the uploader
14081      * @param data - the data about the image to upload
14082      *
14083      * {
14084           id : 123
14085           title : "Title of file",
14086           is_uploaded : false,
14087           src : "http://.....",
14088           srcfile : { the File upload object },
14089           mimetype : file.type,
14090           preview : false,
14091           is_deleted : 0
14092           .. any other data...
14093         }
14094      *
14095      * 
14096     */
14097     
14098     addCard : function (data)
14099     {
14100         // hidden input element?
14101         // if the file is not an image...
14102         //then we need to use something other that and header_image
14103         var t = this;
14104         //   remove.....
14105         var footer = [
14106             {
14107                 xns : Roo.bootstrap,
14108                 xtype : 'CardFooter',
14109                  items: [
14110                     {
14111                         xns : Roo.bootstrap,
14112                         xtype : 'Element',
14113                         cls : 'd-flex',
14114                         items : [
14115                             
14116                             {
14117                                 xns : Roo.bootstrap,
14118                                 xtype : 'Button',
14119                                 html : String.format("<small>{0}</small>", data.title),
14120                                 cls : 'col-10 text-left',
14121                                 size: 'sm',
14122                                 weight: 'link',
14123                                 fa : 'download',
14124                                 listeners : {
14125                                     click : function() {
14126                                      
14127                                         t.fireEvent( "download", t, data );
14128                                     }
14129                                 }
14130                             },
14131                           
14132                             {
14133                                 xns : Roo.bootstrap,
14134                                 xtype : 'Button',
14135                                 style: 'max-height: 28px; ',
14136                                 size : 'sm',
14137                                 weight: 'danger',
14138                                 cls : 'col-2',
14139                                 fa : 'times',
14140                                 listeners : {
14141                                     click : function() {
14142                                         t.removeCard(data.id)
14143                                     }
14144                                 }
14145                             }
14146                         ]
14147                     }
14148                     
14149                 ] 
14150             }
14151             
14152         ];
14153         
14154         var cn = this.addxtype(
14155             {
14156                  
14157                 xns : Roo.bootstrap,
14158                 xtype : 'Card',
14159                 closeable : true,
14160                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14161                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14162                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14163                 data : data,
14164                 html : false,
14165                  
14166                 items : footer,
14167                 initEvents : function() {
14168                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14169                     var card = this;
14170                     this.imgEl = this.el.select('.card-img-top').first();
14171                     if (this.imgEl) {
14172                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14173                         this.imgEl.set({ 'pointer' : 'cursor' });
14174                                   
14175                     }
14176                     this.getCardFooter().addClass('p-1');
14177                     
14178                   
14179                 }
14180                 
14181             }
14182         );
14183         // dont' really need ot update items.
14184         // this.items.push(cn);
14185         this.fileCollection.add(cn);
14186         
14187         if (!data.srcfile) {
14188             this.updateInput();
14189             return;
14190         }
14191             
14192         var _t = this;
14193         var reader = new FileReader();
14194         reader.addEventListener("load", function() {  
14195             data.srcdata =  reader.result;
14196             _t.updateInput();
14197         });
14198         reader.readAsDataURL(data.srcfile);
14199         
14200         
14201         
14202     },
14203     removeCard : function(id)
14204     {
14205         
14206         var card  = this.fileCollection.get(id);
14207         card.data.is_deleted = 1;
14208         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14209         //this.fileCollection.remove(card);
14210         //this.items = this.items.filter(function(e) { return e != card });
14211         // dont' really need ot update items.
14212         card.el.dom.parentNode.removeChild(card.el.dom);
14213         this.updateInput();
14214
14215         
14216     },
14217     reset: function()
14218     {
14219         this.fileCollection.each(function(card) {
14220             if (card.el.dom && card.el.dom.parentNode) {
14221                 card.el.dom.parentNode.removeChild(card.el.dom);
14222             }
14223         });
14224         this.fileCollection.clear();
14225         this.updateInput();
14226     },
14227     
14228     updateInput : function()
14229     {
14230          var data = [];
14231         this.fileCollection.each(function(e) {
14232             data.push(e.data);
14233             
14234         });
14235         this.inputEl().dom.value = JSON.stringify(data);
14236         
14237         
14238         
14239     }
14240     
14241     
14242 });
14243
14244
14245 Roo.bootstrap.CardUploader.ID = -1;/*
14246  * Based on:
14247  * Ext JS Library 1.1.1
14248  * Copyright(c) 2006-2007, Ext JS, LLC.
14249  *
14250  * Originally Released Under LGPL - original licence link has changed is not relivant.
14251  *
14252  * Fork - LGPL
14253  * <script type="text/javascript">
14254  */
14255
14256
14257 /**
14258  * @class Roo.data.SortTypes
14259  * @singleton
14260  * Defines the default sorting (casting?) comparison functions used when sorting data.
14261  */
14262 Roo.data.SortTypes = {
14263     /**
14264      * Default sort that does nothing
14265      * @param {Mixed} s The value being converted
14266      * @return {Mixed} The comparison value
14267      */
14268     none : function(s){
14269         return s;
14270     },
14271     
14272     /**
14273      * The regular expression used to strip tags
14274      * @type {RegExp}
14275      * @property
14276      */
14277     stripTagsRE : /<\/?[^>]+>/gi,
14278     
14279     /**
14280      * Strips all HTML tags to sort on text only
14281      * @param {Mixed} s The value being converted
14282      * @return {String} The comparison value
14283      */
14284     asText : function(s){
14285         return String(s).replace(this.stripTagsRE, "");
14286     },
14287     
14288     /**
14289      * Strips all HTML tags to sort on text only - Case insensitive
14290      * @param {Mixed} s The value being converted
14291      * @return {String} The comparison value
14292      */
14293     asUCText : function(s){
14294         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14295     },
14296     
14297     /**
14298      * Case insensitive string
14299      * @param {Mixed} s The value being converted
14300      * @return {String} The comparison value
14301      */
14302     asUCString : function(s) {
14303         return String(s).toUpperCase();
14304     },
14305     
14306     /**
14307      * Date sorting
14308      * @param {Mixed} s The value being converted
14309      * @return {Number} The comparison value
14310      */
14311     asDate : function(s) {
14312         if(!s){
14313             return 0;
14314         }
14315         if(s instanceof Date){
14316             return s.getTime();
14317         }
14318         return Date.parse(String(s));
14319     },
14320     
14321     /**
14322      * Float sorting
14323      * @param {Mixed} s The value being converted
14324      * @return {Float} The comparison value
14325      */
14326     asFloat : function(s) {
14327         var val = parseFloat(String(s).replace(/,/g, ""));
14328         if(isNaN(val)) {
14329             val = 0;
14330         }
14331         return val;
14332     },
14333     
14334     /**
14335      * Integer sorting
14336      * @param {Mixed} s The value being converted
14337      * @return {Number} The comparison value
14338      */
14339     asInt : function(s) {
14340         var val = parseInt(String(s).replace(/,/g, ""));
14341         if(isNaN(val)) {
14342             val = 0;
14343         }
14344         return val;
14345     }
14346 };/*
14347  * Based on:
14348  * Ext JS Library 1.1.1
14349  * Copyright(c) 2006-2007, Ext JS, LLC.
14350  *
14351  * Originally Released Under LGPL - original licence link has changed is not relivant.
14352  *
14353  * Fork - LGPL
14354  * <script type="text/javascript">
14355  */
14356
14357 /**
14358 * @class Roo.data.Record
14359  * Instances of this class encapsulate both record <em>definition</em> information, and record
14360  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14361  * to access Records cached in an {@link Roo.data.Store} object.<br>
14362  * <p>
14363  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14364  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14365  * objects.<br>
14366  * <p>
14367  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14368  * @constructor
14369  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14370  * {@link #create}. The parameters are the same.
14371  * @param {Array} data An associative Array of data values keyed by the field name.
14372  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14373  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14374  * not specified an integer id is generated.
14375  */
14376 Roo.data.Record = function(data, id){
14377     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14378     this.data = data;
14379 };
14380
14381 /**
14382  * Generate a constructor for a specific record layout.
14383  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14384  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14385  * Each field definition object may contain the following properties: <ul>
14386  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14387  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14388  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14389  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14390  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14391  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14392  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14393  * this may be omitted.</p></li>
14394  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14395  * <ul><li>auto (Default, implies no conversion)</li>
14396  * <li>string</li>
14397  * <li>int</li>
14398  * <li>float</li>
14399  * <li>boolean</li>
14400  * <li>date</li></ul></p></li>
14401  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14402  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14403  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14404  * by the Reader into an object that will be stored in the Record. It is passed the
14405  * following parameters:<ul>
14406  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14407  * </ul></p></li>
14408  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14409  * </ul>
14410  * <br>usage:<br><pre><code>
14411 var TopicRecord = Roo.data.Record.create(
14412     {name: 'title', mapping: 'topic_title'},
14413     {name: 'author', mapping: 'username'},
14414     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14415     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14416     {name: 'lastPoster', mapping: 'user2'},
14417     {name: 'excerpt', mapping: 'post_text'}
14418 );
14419
14420 var myNewRecord = new TopicRecord({
14421     title: 'Do my job please',
14422     author: 'noobie',
14423     totalPosts: 1,
14424     lastPost: new Date(),
14425     lastPoster: 'Animal',
14426     excerpt: 'No way dude!'
14427 });
14428 myStore.add(myNewRecord);
14429 </code></pre>
14430  * @method create
14431  * @static
14432  */
14433 Roo.data.Record.create = function(o){
14434     var f = function(){
14435         f.superclass.constructor.apply(this, arguments);
14436     };
14437     Roo.extend(f, Roo.data.Record);
14438     var p = f.prototype;
14439     p.fields = new Roo.util.MixedCollection(false, function(field){
14440         return field.name;
14441     });
14442     for(var i = 0, len = o.length; i < len; i++){
14443         p.fields.add(new Roo.data.Field(o[i]));
14444     }
14445     f.getField = function(name){
14446         return p.fields.get(name);  
14447     };
14448     return f;
14449 };
14450
14451 Roo.data.Record.AUTO_ID = 1000;
14452 Roo.data.Record.EDIT = 'edit';
14453 Roo.data.Record.REJECT = 'reject';
14454 Roo.data.Record.COMMIT = 'commit';
14455
14456 Roo.data.Record.prototype = {
14457     /**
14458      * Readonly flag - true if this record has been modified.
14459      * @type Boolean
14460      */
14461     dirty : false,
14462     editing : false,
14463     error: null,
14464     modified: null,
14465
14466     // private
14467     join : function(store){
14468         this.store = store;
14469     },
14470
14471     /**
14472      * Set the named field to the specified value.
14473      * @param {String} name The name of the field to set.
14474      * @param {Object} value The value to set the field to.
14475      */
14476     set : function(name, value){
14477         if(this.data[name] == value){
14478             return;
14479         }
14480         this.dirty = true;
14481         if(!this.modified){
14482             this.modified = {};
14483         }
14484         if(typeof this.modified[name] == 'undefined'){
14485             this.modified[name] = this.data[name];
14486         }
14487         this.data[name] = value;
14488         if(!this.editing && this.store){
14489             this.store.afterEdit(this);
14490         }       
14491     },
14492
14493     /**
14494      * Get the value of the named field.
14495      * @param {String} name The name of the field to get the value of.
14496      * @return {Object} The value of the field.
14497      */
14498     get : function(name){
14499         return this.data[name]; 
14500     },
14501
14502     // private
14503     beginEdit : function(){
14504         this.editing = true;
14505         this.modified = {}; 
14506     },
14507
14508     // private
14509     cancelEdit : function(){
14510         this.editing = false;
14511         delete this.modified;
14512     },
14513
14514     // private
14515     endEdit : function(){
14516         this.editing = false;
14517         if(this.dirty && this.store){
14518             this.store.afterEdit(this);
14519         }
14520     },
14521
14522     /**
14523      * Usually called by the {@link Roo.data.Store} which owns the Record.
14524      * Rejects all changes made to the Record since either creation, or the last commit operation.
14525      * Modified fields are reverted to their original values.
14526      * <p>
14527      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14528      * of reject operations.
14529      */
14530     reject : function(){
14531         var m = this.modified;
14532         for(var n in m){
14533             if(typeof m[n] != "function"){
14534                 this.data[n] = m[n];
14535             }
14536         }
14537         this.dirty = false;
14538         delete this.modified;
14539         this.editing = false;
14540         if(this.store){
14541             this.store.afterReject(this);
14542         }
14543     },
14544
14545     /**
14546      * Usually called by the {@link Roo.data.Store} which owns the Record.
14547      * Commits all changes made to the Record since either creation, or the last commit operation.
14548      * <p>
14549      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14550      * of commit operations.
14551      */
14552     commit : function(){
14553         this.dirty = false;
14554         delete this.modified;
14555         this.editing = false;
14556         if(this.store){
14557             this.store.afterCommit(this);
14558         }
14559     },
14560
14561     // private
14562     hasError : function(){
14563         return this.error != null;
14564     },
14565
14566     // private
14567     clearError : function(){
14568         this.error = null;
14569     },
14570
14571     /**
14572      * Creates a copy of this record.
14573      * @param {String} id (optional) A new record id if you don't want to use this record's id
14574      * @return {Record}
14575      */
14576     copy : function(newId) {
14577         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14578     }
14579 };/*
14580  * Based on:
14581  * Ext JS Library 1.1.1
14582  * Copyright(c) 2006-2007, Ext JS, LLC.
14583  *
14584  * Originally Released Under LGPL - original licence link has changed is not relivant.
14585  *
14586  * Fork - LGPL
14587  * <script type="text/javascript">
14588  */
14589
14590
14591
14592 /**
14593  * @class Roo.data.Store
14594  * @extends Roo.util.Observable
14595  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14596  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14597  * <p>
14598  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
14599  * has no knowledge of the format of the data returned by the Proxy.<br>
14600  * <p>
14601  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14602  * instances from the data object. These records are cached and made available through accessor functions.
14603  * @constructor
14604  * Creates a new Store.
14605  * @param {Object} config A config object containing the objects needed for the Store to access data,
14606  * and read the data into Records.
14607  */
14608 Roo.data.Store = function(config){
14609     this.data = new Roo.util.MixedCollection(false);
14610     this.data.getKey = function(o){
14611         return o.id;
14612     };
14613     this.baseParams = {};
14614     // private
14615     this.paramNames = {
14616         "start" : "start",
14617         "limit" : "limit",
14618         "sort" : "sort",
14619         "dir" : "dir",
14620         "multisort" : "_multisort"
14621     };
14622
14623     if(config && config.data){
14624         this.inlineData = config.data;
14625         delete config.data;
14626     }
14627
14628     Roo.apply(this, config);
14629     
14630     if(this.reader){ // reader passed
14631         this.reader = Roo.factory(this.reader, Roo.data);
14632         this.reader.xmodule = this.xmodule || false;
14633         if(!this.recordType){
14634             this.recordType = this.reader.recordType;
14635         }
14636         if(this.reader.onMetaChange){
14637             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14638         }
14639     }
14640
14641     if(this.recordType){
14642         this.fields = this.recordType.prototype.fields;
14643     }
14644     this.modified = [];
14645
14646     this.addEvents({
14647         /**
14648          * @event datachanged
14649          * Fires when the data cache has changed, and a widget which is using this Store
14650          * as a Record cache should refresh its view.
14651          * @param {Store} this
14652          */
14653         datachanged : true,
14654         /**
14655          * @event metachange
14656          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14657          * @param {Store} this
14658          * @param {Object} meta The JSON metadata
14659          */
14660         metachange : true,
14661         /**
14662          * @event add
14663          * Fires when Records have been added to the Store
14664          * @param {Store} this
14665          * @param {Roo.data.Record[]} records The array of Records added
14666          * @param {Number} index The index at which the record(s) were added
14667          */
14668         add : true,
14669         /**
14670          * @event remove
14671          * Fires when a Record has been removed from the Store
14672          * @param {Store} this
14673          * @param {Roo.data.Record} record The Record that was removed
14674          * @param {Number} index The index at which the record was removed
14675          */
14676         remove : true,
14677         /**
14678          * @event update
14679          * Fires when a Record has been updated
14680          * @param {Store} this
14681          * @param {Roo.data.Record} record The Record that was updated
14682          * @param {String} operation The update operation being performed.  Value may be one of:
14683          * <pre><code>
14684  Roo.data.Record.EDIT
14685  Roo.data.Record.REJECT
14686  Roo.data.Record.COMMIT
14687          * </code></pre>
14688          */
14689         update : true,
14690         /**
14691          * @event clear
14692          * Fires when the data cache has been cleared.
14693          * @param {Store} this
14694          */
14695         clear : true,
14696         /**
14697          * @event beforeload
14698          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14699          * the load action will be canceled.
14700          * @param {Store} this
14701          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14702          */
14703         beforeload : true,
14704         /**
14705          * @event beforeloadadd
14706          * Fires after a new set of Records has been loaded.
14707          * @param {Store} this
14708          * @param {Roo.data.Record[]} records The Records that were loaded
14709          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14710          */
14711         beforeloadadd : true,
14712         /**
14713          * @event load
14714          * Fires after a new set of Records has been loaded, before they are added to the store.
14715          * @param {Store} this
14716          * @param {Roo.data.Record[]} records The Records that were loaded
14717          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14718          * @params {Object} return from reader
14719          */
14720         load : true,
14721         /**
14722          * @event loadexception
14723          * Fires if an exception occurs in the Proxy during loading.
14724          * Called with the signature of the Proxy's "loadexception" event.
14725          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14726          * 
14727          * @param {Proxy} 
14728          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14729          * @param {Object} load options 
14730          * @param {Object} jsonData from your request (normally this contains the Exception)
14731          */
14732         loadexception : true
14733     });
14734     
14735     if(this.proxy){
14736         this.proxy = Roo.factory(this.proxy, Roo.data);
14737         this.proxy.xmodule = this.xmodule || false;
14738         this.relayEvents(this.proxy,  ["loadexception"]);
14739     }
14740     this.sortToggle = {};
14741     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14742
14743     Roo.data.Store.superclass.constructor.call(this);
14744
14745     if(this.inlineData){
14746         this.loadData(this.inlineData);
14747         delete this.inlineData;
14748     }
14749 };
14750
14751 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14752      /**
14753     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14754     * without a remote query - used by combo/forms at present.
14755     */
14756     
14757     /**
14758     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14759     */
14760     /**
14761     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14762     */
14763     /**
14764     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14765     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14766     */
14767     /**
14768     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14769     * on any HTTP request
14770     */
14771     /**
14772     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14773     */
14774     /**
14775     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14776     */
14777     multiSort: false,
14778     /**
14779     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14780     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14781     */
14782     remoteSort : false,
14783
14784     /**
14785     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14786      * loaded or when a record is removed. (defaults to false).
14787     */
14788     pruneModifiedRecords : false,
14789
14790     // private
14791     lastOptions : null,
14792
14793     /**
14794      * Add Records to the Store and fires the add event.
14795      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14796      */
14797     add : function(records){
14798         records = [].concat(records);
14799         for(var i = 0, len = records.length; i < len; i++){
14800             records[i].join(this);
14801         }
14802         var index = this.data.length;
14803         this.data.addAll(records);
14804         this.fireEvent("add", this, records, index);
14805     },
14806
14807     /**
14808      * Remove a Record from the Store and fires the remove event.
14809      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14810      */
14811     remove : function(record){
14812         var index = this.data.indexOf(record);
14813         this.data.removeAt(index);
14814  
14815         if(this.pruneModifiedRecords){
14816             this.modified.remove(record);
14817         }
14818         this.fireEvent("remove", this, record, index);
14819     },
14820
14821     /**
14822      * Remove all Records from the Store and fires the clear event.
14823      */
14824     removeAll : function(){
14825         this.data.clear();
14826         if(this.pruneModifiedRecords){
14827             this.modified = [];
14828         }
14829         this.fireEvent("clear", this);
14830     },
14831
14832     /**
14833      * Inserts Records to the Store at the given index and fires the add event.
14834      * @param {Number} index The start index at which to insert the passed Records.
14835      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14836      */
14837     insert : function(index, records){
14838         records = [].concat(records);
14839         for(var i = 0, len = records.length; i < len; i++){
14840             this.data.insert(index, records[i]);
14841             records[i].join(this);
14842         }
14843         this.fireEvent("add", this, records, index);
14844     },
14845
14846     /**
14847      * Get the index within the cache of the passed Record.
14848      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14849      * @return {Number} The index of the passed Record. Returns -1 if not found.
14850      */
14851     indexOf : function(record){
14852         return this.data.indexOf(record);
14853     },
14854
14855     /**
14856      * Get the index within the cache of the Record with the passed id.
14857      * @param {String} id The id of the Record to find.
14858      * @return {Number} The index of the Record. Returns -1 if not found.
14859      */
14860     indexOfId : function(id){
14861         return this.data.indexOfKey(id);
14862     },
14863
14864     /**
14865      * Get the Record with the specified id.
14866      * @param {String} id The id of the Record to find.
14867      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14868      */
14869     getById : function(id){
14870         return this.data.key(id);
14871     },
14872
14873     /**
14874      * Get the Record at the specified index.
14875      * @param {Number} index The index of the Record to find.
14876      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14877      */
14878     getAt : function(index){
14879         return this.data.itemAt(index);
14880     },
14881
14882     /**
14883      * Returns a range of Records between specified indices.
14884      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14885      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14886      * @return {Roo.data.Record[]} An array of Records
14887      */
14888     getRange : function(start, end){
14889         return this.data.getRange(start, end);
14890     },
14891
14892     // private
14893     storeOptions : function(o){
14894         o = Roo.apply({}, o);
14895         delete o.callback;
14896         delete o.scope;
14897         this.lastOptions = o;
14898     },
14899
14900     /**
14901      * Loads the Record cache from the configured Proxy using the configured Reader.
14902      * <p>
14903      * If using remote paging, then the first load call must specify the <em>start</em>
14904      * and <em>limit</em> properties in the options.params property to establish the initial
14905      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14906      * <p>
14907      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14908      * and this call will return before the new data has been loaded. Perform any post-processing
14909      * in a callback function, or in a "load" event handler.</strong>
14910      * <p>
14911      * @param {Object} options An object containing properties which control loading options:<ul>
14912      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14913      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14914      * passed the following arguments:<ul>
14915      * <li>r : Roo.data.Record[]</li>
14916      * <li>options: Options object from the load call</li>
14917      * <li>success: Boolean success indicator</li></ul></li>
14918      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14919      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14920      * </ul>
14921      */
14922     load : function(options){
14923         options = options || {};
14924         if(this.fireEvent("beforeload", this, options) !== false){
14925             this.storeOptions(options);
14926             var p = Roo.apply(options.params || {}, this.baseParams);
14927             // if meta was not loaded from remote source.. try requesting it.
14928             if (!this.reader.metaFromRemote) {
14929                 p._requestMeta = 1;
14930             }
14931             if(this.sortInfo && this.remoteSort){
14932                 var pn = this.paramNames;
14933                 p[pn["sort"]] = this.sortInfo.field;
14934                 p[pn["dir"]] = this.sortInfo.direction;
14935             }
14936             if (this.multiSort) {
14937                 var pn = this.paramNames;
14938                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14939             }
14940             
14941             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14942         }
14943     },
14944
14945     /**
14946      * Reloads the Record cache from the configured Proxy using the configured Reader and
14947      * the options from the last load operation performed.
14948      * @param {Object} options (optional) An object containing properties which may override the options
14949      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14950      * the most recently used options are reused).
14951      */
14952     reload : function(options){
14953         this.load(Roo.applyIf(options||{}, this.lastOptions));
14954     },
14955
14956     // private
14957     // Called as a callback by the Reader during a load operation.
14958     loadRecords : function(o, options, success){
14959         if(!o || success === false){
14960             if(success !== false){
14961                 this.fireEvent("load", this, [], options, o);
14962             }
14963             if(options.callback){
14964                 options.callback.call(options.scope || this, [], options, false);
14965             }
14966             return;
14967         }
14968         // if data returned failure - throw an exception.
14969         if (o.success === false) {
14970             // show a message if no listener is registered.
14971             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14972                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14973             }
14974             // loadmask wil be hooked into this..
14975             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14976             return;
14977         }
14978         var r = o.records, t = o.totalRecords || r.length;
14979         
14980         this.fireEvent("beforeloadadd", this, r, options, o);
14981         
14982         if(!options || options.add !== true){
14983             if(this.pruneModifiedRecords){
14984                 this.modified = [];
14985             }
14986             for(var i = 0, len = r.length; i < len; i++){
14987                 r[i].join(this);
14988             }
14989             if(this.snapshot){
14990                 this.data = this.snapshot;
14991                 delete this.snapshot;
14992             }
14993             this.data.clear();
14994             this.data.addAll(r);
14995             this.totalLength = t;
14996             this.applySort();
14997             this.fireEvent("datachanged", this);
14998         }else{
14999             this.totalLength = Math.max(t, this.data.length+r.length);
15000             this.add(r);
15001         }
15002         
15003         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15004                 
15005             var e = new Roo.data.Record({});
15006
15007             e.set(this.parent.displayField, this.parent.emptyTitle);
15008             e.set(this.parent.valueField, '');
15009
15010             this.insert(0, e);
15011         }
15012             
15013         this.fireEvent("load", this, r, options, o);
15014         if(options.callback){
15015             options.callback.call(options.scope || this, r, options, true);
15016         }
15017     },
15018
15019
15020     /**
15021      * Loads data from a passed data block. A Reader which understands the format of the data
15022      * must have been configured in the constructor.
15023      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15024      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15025      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15026      */
15027     loadData : function(o, append){
15028         var r = this.reader.readRecords(o);
15029         this.loadRecords(r, {add: append}, true);
15030     },
15031     
15032      /**
15033      * using 'cn' the nested child reader read the child array into it's child stores.
15034      * @param {Object} rec The record with a 'children array
15035      */
15036     loadDataFromChildren : function(rec)
15037     {
15038         this.loadData(this.reader.toLoadData(rec));
15039     },
15040     
15041
15042     /**
15043      * Gets the number of cached records.
15044      * <p>
15045      * <em>If using paging, this may not be the total size of the dataset. If the data object
15046      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15047      * the data set size</em>
15048      */
15049     getCount : function(){
15050         return this.data.length || 0;
15051     },
15052
15053     /**
15054      * Gets the total number of records in the dataset as returned by the server.
15055      * <p>
15056      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15057      * the dataset size</em>
15058      */
15059     getTotalCount : function(){
15060         return this.totalLength || 0;
15061     },
15062
15063     /**
15064      * Returns the sort state of the Store as an object with two properties:
15065      * <pre><code>
15066  field {String} The name of the field by which the Records are sorted
15067  direction {String} The sort order, "ASC" or "DESC"
15068      * </code></pre>
15069      */
15070     getSortState : function(){
15071         return this.sortInfo;
15072     },
15073
15074     // private
15075     applySort : function(){
15076         if(this.sortInfo && !this.remoteSort){
15077             var s = this.sortInfo, f = s.field;
15078             var st = this.fields.get(f).sortType;
15079             var fn = function(r1, r2){
15080                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15081                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15082             };
15083             this.data.sort(s.direction, fn);
15084             if(this.snapshot && this.snapshot != this.data){
15085                 this.snapshot.sort(s.direction, fn);
15086             }
15087         }
15088     },
15089
15090     /**
15091      * Sets the default sort column and order to be used by the next load operation.
15092      * @param {String} fieldName The name of the field to sort by.
15093      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15094      */
15095     setDefaultSort : function(field, dir){
15096         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15097     },
15098
15099     /**
15100      * Sort the Records.
15101      * If remote sorting is used, the sort is performed on the server, and the cache is
15102      * reloaded. If local sorting is used, the cache is sorted internally.
15103      * @param {String} fieldName The name of the field to sort by.
15104      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15105      */
15106     sort : function(fieldName, dir){
15107         var f = this.fields.get(fieldName);
15108         if(!dir){
15109             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15110             
15111             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15112                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15113             }else{
15114                 dir = f.sortDir;
15115             }
15116         }
15117         this.sortToggle[f.name] = dir;
15118         this.sortInfo = {field: f.name, direction: dir};
15119         if(!this.remoteSort){
15120             this.applySort();
15121             this.fireEvent("datachanged", this);
15122         }else{
15123             this.load(this.lastOptions);
15124         }
15125     },
15126
15127     /**
15128      * Calls the specified function for each of the Records in the cache.
15129      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15130      * Returning <em>false</em> aborts and exits the iteration.
15131      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15132      */
15133     each : function(fn, scope){
15134         this.data.each(fn, scope);
15135     },
15136
15137     /**
15138      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15139      * (e.g., during paging).
15140      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15141      */
15142     getModifiedRecords : function(){
15143         return this.modified;
15144     },
15145
15146     // private
15147     createFilterFn : function(property, value, anyMatch){
15148         if(!value.exec){ // not a regex
15149             value = String(value);
15150             if(value.length == 0){
15151                 return false;
15152             }
15153             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15154         }
15155         return function(r){
15156             return value.test(r.data[property]);
15157         };
15158     },
15159
15160     /**
15161      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15162      * @param {String} property A field on your records
15163      * @param {Number} start The record index to start at (defaults to 0)
15164      * @param {Number} end The last record index to include (defaults to length - 1)
15165      * @return {Number} The sum
15166      */
15167     sum : function(property, start, end){
15168         var rs = this.data.items, v = 0;
15169         start = start || 0;
15170         end = (end || end === 0) ? end : rs.length-1;
15171
15172         for(var i = start; i <= end; i++){
15173             v += (rs[i].data[property] || 0);
15174         }
15175         return v;
15176     },
15177
15178     /**
15179      * Filter the records by a specified property.
15180      * @param {String} field A field on your records
15181      * @param {String/RegExp} value Either a string that the field
15182      * should start with or a RegExp to test against the field
15183      * @param {Boolean} anyMatch True to match any part not just the beginning
15184      */
15185     filter : function(property, value, anyMatch){
15186         var fn = this.createFilterFn(property, value, anyMatch);
15187         return fn ? this.filterBy(fn) : this.clearFilter();
15188     },
15189
15190     /**
15191      * Filter by a function. The specified function will be called with each
15192      * record in this data source. If the function returns true the record is included,
15193      * otherwise it is filtered.
15194      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15195      * @param {Object} scope (optional) The scope of the function (defaults to this)
15196      */
15197     filterBy : function(fn, scope){
15198         this.snapshot = this.snapshot || this.data;
15199         this.data = this.queryBy(fn, scope||this);
15200         this.fireEvent("datachanged", this);
15201     },
15202
15203     /**
15204      * Query the records by a specified property.
15205      * @param {String} field A field on your records
15206      * @param {String/RegExp} value Either a string that the field
15207      * should start with or a RegExp to test against the field
15208      * @param {Boolean} anyMatch True to match any part not just the beginning
15209      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15210      */
15211     query : function(property, value, anyMatch){
15212         var fn = this.createFilterFn(property, value, anyMatch);
15213         return fn ? this.queryBy(fn) : this.data.clone();
15214     },
15215
15216     /**
15217      * Query by a function. The specified function will be called with each
15218      * record in this data source. If the function returns true the record is included
15219      * in the results.
15220      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15221      * @param {Object} scope (optional) The scope of the function (defaults to this)
15222       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15223      **/
15224     queryBy : function(fn, scope){
15225         var data = this.snapshot || this.data;
15226         return data.filterBy(fn, scope||this);
15227     },
15228
15229     /**
15230      * Collects unique values for a particular dataIndex from this store.
15231      * @param {String} dataIndex The property to collect
15232      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15233      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15234      * @return {Array} An array of the unique values
15235      **/
15236     collect : function(dataIndex, allowNull, bypassFilter){
15237         var d = (bypassFilter === true && this.snapshot) ?
15238                 this.snapshot.items : this.data.items;
15239         var v, sv, r = [], l = {};
15240         for(var i = 0, len = d.length; i < len; i++){
15241             v = d[i].data[dataIndex];
15242             sv = String(v);
15243             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15244                 l[sv] = true;
15245                 r[r.length] = v;
15246             }
15247         }
15248         return r;
15249     },
15250
15251     /**
15252      * Revert to a view of the Record cache with no filtering applied.
15253      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15254      */
15255     clearFilter : function(suppressEvent){
15256         if(this.snapshot && this.snapshot != this.data){
15257             this.data = this.snapshot;
15258             delete this.snapshot;
15259             if(suppressEvent !== true){
15260                 this.fireEvent("datachanged", this);
15261             }
15262         }
15263     },
15264
15265     // private
15266     afterEdit : function(record){
15267         if(this.modified.indexOf(record) == -1){
15268             this.modified.push(record);
15269         }
15270         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15271     },
15272     
15273     // private
15274     afterReject : function(record){
15275         this.modified.remove(record);
15276         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15277     },
15278
15279     // private
15280     afterCommit : function(record){
15281         this.modified.remove(record);
15282         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15283     },
15284
15285     /**
15286      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15287      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15288      */
15289     commitChanges : function(){
15290         var m = this.modified.slice(0);
15291         this.modified = [];
15292         for(var i = 0, len = m.length; i < len; i++){
15293             m[i].commit();
15294         }
15295     },
15296
15297     /**
15298      * Cancel outstanding changes on all changed records.
15299      */
15300     rejectChanges : function(){
15301         var m = this.modified.slice(0);
15302         this.modified = [];
15303         for(var i = 0, len = m.length; i < len; i++){
15304             m[i].reject();
15305         }
15306     },
15307
15308     onMetaChange : function(meta, rtype, o){
15309         this.recordType = rtype;
15310         this.fields = rtype.prototype.fields;
15311         delete this.snapshot;
15312         this.sortInfo = meta.sortInfo || this.sortInfo;
15313         this.modified = [];
15314         this.fireEvent('metachange', this, this.reader.meta);
15315     },
15316     
15317     moveIndex : function(data, type)
15318     {
15319         var index = this.indexOf(data);
15320         
15321         var newIndex = index + type;
15322         
15323         this.remove(data);
15324         
15325         this.insert(newIndex, data);
15326         
15327     }
15328 });/*
15329  * Based on:
15330  * Ext JS Library 1.1.1
15331  * Copyright(c) 2006-2007, Ext JS, LLC.
15332  *
15333  * Originally Released Under LGPL - original licence link has changed is not relivant.
15334  *
15335  * Fork - LGPL
15336  * <script type="text/javascript">
15337  */
15338
15339 /**
15340  * @class Roo.data.SimpleStore
15341  * @extends Roo.data.Store
15342  * Small helper class to make creating Stores from Array data easier.
15343  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15344  * @cfg {Array} fields An array of field definition objects, or field name strings.
15345  * @cfg {Object} an existing reader (eg. copied from another store)
15346  * @cfg {Array} data The multi-dimensional array of data
15347  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15348  * @cfg {Roo.data.Reader} reader  [not-required] 
15349  * @constructor
15350  * @param {Object} config
15351  */
15352 Roo.data.SimpleStore = function(config)
15353 {
15354     Roo.data.SimpleStore.superclass.constructor.call(this, {
15355         isLocal : true,
15356         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15357                 id: config.id
15358             },
15359             Roo.data.Record.create(config.fields)
15360         ),
15361         proxy : new Roo.data.MemoryProxy(config.data)
15362     });
15363     this.load();
15364 };
15365 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15366  * Based on:
15367  * Ext JS Library 1.1.1
15368  * Copyright(c) 2006-2007, Ext JS, LLC.
15369  *
15370  * Originally Released Under LGPL - original licence link has changed is not relivant.
15371  *
15372  * Fork - LGPL
15373  * <script type="text/javascript">
15374  */
15375
15376 /**
15377 /**
15378  * @extends Roo.data.Store
15379  * @class Roo.data.JsonStore
15380  * Small helper class to make creating Stores for JSON data easier. <br/>
15381 <pre><code>
15382 var store = new Roo.data.JsonStore({
15383     url: 'get-images.php',
15384     root: 'images',
15385     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15386 });
15387 </code></pre>
15388  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15389  * JsonReader and HttpProxy (unless inline data is provided).</b>
15390  * @cfg {Array} fields An array of field definition objects, or field name strings.
15391  * @constructor
15392  * @param {Object} config
15393  */
15394 Roo.data.JsonStore = function(c){
15395     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15396         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15397         reader: new Roo.data.JsonReader(c, c.fields)
15398     }));
15399 };
15400 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15401  * Based on:
15402  * Ext JS Library 1.1.1
15403  * Copyright(c) 2006-2007, Ext JS, LLC.
15404  *
15405  * Originally Released Under LGPL - original licence link has changed is not relivant.
15406  *
15407  * Fork - LGPL
15408  * <script type="text/javascript">
15409  */
15410
15411  
15412 Roo.data.Field = function(config){
15413     if(typeof config == "string"){
15414         config = {name: config};
15415     }
15416     Roo.apply(this, config);
15417     
15418     if(!this.type){
15419         this.type = "auto";
15420     }
15421     
15422     var st = Roo.data.SortTypes;
15423     // named sortTypes are supported, here we look them up
15424     if(typeof this.sortType == "string"){
15425         this.sortType = st[this.sortType];
15426     }
15427     
15428     // set default sortType for strings and dates
15429     if(!this.sortType){
15430         switch(this.type){
15431             case "string":
15432                 this.sortType = st.asUCString;
15433                 break;
15434             case "date":
15435                 this.sortType = st.asDate;
15436                 break;
15437             default:
15438                 this.sortType = st.none;
15439         }
15440     }
15441
15442     // define once
15443     var stripRe = /[\$,%]/g;
15444
15445     // prebuilt conversion function for this field, instead of
15446     // switching every time we're reading a value
15447     if(!this.convert){
15448         var cv, dateFormat = this.dateFormat;
15449         switch(this.type){
15450             case "":
15451             case "auto":
15452             case undefined:
15453                 cv = function(v){ return v; };
15454                 break;
15455             case "string":
15456                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15457                 break;
15458             case "int":
15459                 cv = function(v){
15460                     return v !== undefined && v !== null && v !== '' ?
15461                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15462                     };
15463                 break;
15464             case "float":
15465                 cv = function(v){
15466                     return v !== undefined && v !== null && v !== '' ?
15467                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15468                     };
15469                 break;
15470             case "bool":
15471             case "boolean":
15472                 cv = function(v){ return v === true || v === "true" || v == 1; };
15473                 break;
15474             case "date":
15475                 cv = function(v){
15476                     if(!v){
15477                         return '';
15478                     }
15479                     if(v instanceof Date){
15480                         return v;
15481                     }
15482                     if(dateFormat){
15483                         if(dateFormat == "timestamp"){
15484                             return new Date(v*1000);
15485                         }
15486                         return Date.parseDate(v, dateFormat);
15487                     }
15488                     var parsed = Date.parse(v);
15489                     return parsed ? new Date(parsed) : null;
15490                 };
15491              break;
15492             
15493         }
15494         this.convert = cv;
15495     }
15496 };
15497
15498 Roo.data.Field.prototype = {
15499     dateFormat: null,
15500     defaultValue: "",
15501     mapping: null,
15502     sortType : null,
15503     sortDir : "ASC"
15504 };/*
15505  * Based on:
15506  * Ext JS Library 1.1.1
15507  * Copyright(c) 2006-2007, Ext JS, LLC.
15508  *
15509  * Originally Released Under LGPL - original licence link has changed is not relivant.
15510  *
15511  * Fork - LGPL
15512  * <script type="text/javascript">
15513  */
15514  
15515 // Base class for reading structured data from a data source.  This class is intended to be
15516 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15517
15518 /**
15519  * @class Roo.data.DataReader
15520  * @abstract
15521  * Base class for reading structured data from a data source.  This class is intended to be
15522  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15523  */
15524
15525 Roo.data.DataReader = function(meta, recordType){
15526     
15527     this.meta = meta;
15528     
15529     this.recordType = recordType instanceof Array ? 
15530         Roo.data.Record.create(recordType) : recordType;
15531 };
15532
15533 Roo.data.DataReader.prototype = {
15534     
15535     
15536     readerType : 'Data',
15537      /**
15538      * Create an empty record
15539      * @param {Object} data (optional) - overlay some values
15540      * @return {Roo.data.Record} record created.
15541      */
15542     newRow :  function(d) {
15543         var da =  {};
15544         this.recordType.prototype.fields.each(function(c) {
15545             switch( c.type) {
15546                 case 'int' : da[c.name] = 0; break;
15547                 case 'date' : da[c.name] = new Date(); break;
15548                 case 'float' : da[c.name] = 0.0; break;
15549                 case 'boolean' : da[c.name] = false; break;
15550                 default : da[c.name] = ""; break;
15551             }
15552             
15553         });
15554         return new this.recordType(Roo.apply(da, d));
15555     }
15556     
15557     
15558 };/*
15559  * Based on:
15560  * Ext JS Library 1.1.1
15561  * Copyright(c) 2006-2007, Ext JS, LLC.
15562  *
15563  * Originally Released Under LGPL - original licence link has changed is not relivant.
15564  *
15565  * Fork - LGPL
15566  * <script type="text/javascript">
15567  */
15568
15569 /**
15570  * @class Roo.data.DataProxy
15571  * @extends Roo.data.Observable
15572  * @abstract
15573  * This class is an abstract base class for implementations which provide retrieval of
15574  * unformatted data objects.<br>
15575  * <p>
15576  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15577  * (of the appropriate type which knows how to parse the data object) to provide a block of
15578  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15579  * <p>
15580  * Custom implementations must implement the load method as described in
15581  * {@link Roo.data.HttpProxy#load}.
15582  */
15583 Roo.data.DataProxy = function(){
15584     this.addEvents({
15585         /**
15586          * @event beforeload
15587          * Fires before a network request is made to retrieve a data object.
15588          * @param {Object} This DataProxy object.
15589          * @param {Object} params The params parameter to the load function.
15590          */
15591         beforeload : true,
15592         /**
15593          * @event load
15594          * Fires before the load method's callback is called.
15595          * @param {Object} This DataProxy object.
15596          * @param {Object} o The data object.
15597          * @param {Object} arg The callback argument object passed to the load function.
15598          */
15599         load : true,
15600         /**
15601          * @event loadexception
15602          * Fires if an Exception occurs during data retrieval.
15603          * @param {Object} This DataProxy object.
15604          * @param {Object} o The data object.
15605          * @param {Object} arg The callback argument object passed to the load function.
15606          * @param {Object} e The Exception.
15607          */
15608         loadexception : true
15609     });
15610     Roo.data.DataProxy.superclass.constructor.call(this);
15611 };
15612
15613 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15614
15615     /**
15616      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15617      */
15618 /*
15619  * Based on:
15620  * Ext JS Library 1.1.1
15621  * Copyright(c) 2006-2007, Ext JS, LLC.
15622  *
15623  * Originally Released Under LGPL - original licence link has changed is not relivant.
15624  *
15625  * Fork - LGPL
15626  * <script type="text/javascript">
15627  */
15628 /**
15629  * @class Roo.data.MemoryProxy
15630  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15631  * to the Reader when its load method is called.
15632  * @constructor
15633  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15634  */
15635 Roo.data.MemoryProxy = function(data){
15636     if (data.data) {
15637         data = data.data;
15638     }
15639     Roo.data.MemoryProxy.superclass.constructor.call(this);
15640     this.data = data;
15641 };
15642
15643 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15644     
15645     /**
15646      * Load data from the requested source (in this case an in-memory
15647      * data object passed to the constructor), read the data object into
15648      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15649      * process that block using the passed callback.
15650      * @param {Object} params This parameter is not used by the MemoryProxy class.
15651      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15652      * object into a block of Roo.data.Records.
15653      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15654      * The function must be passed <ul>
15655      * <li>The Record block object</li>
15656      * <li>The "arg" argument from the load function</li>
15657      * <li>A boolean success indicator</li>
15658      * </ul>
15659      * @param {Object} scope The scope in which to call the callback
15660      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15661      */
15662     load : function(params, reader, callback, scope, arg){
15663         params = params || {};
15664         var result;
15665         try {
15666             result = reader.readRecords(params.data ? params.data :this.data);
15667         }catch(e){
15668             this.fireEvent("loadexception", this, arg, null, e);
15669             callback.call(scope, null, arg, false);
15670             return;
15671         }
15672         callback.call(scope, result, arg, true);
15673     },
15674     
15675     // private
15676     update : function(params, records){
15677         
15678     }
15679 });/*
15680  * Based on:
15681  * Ext JS Library 1.1.1
15682  * Copyright(c) 2006-2007, Ext JS, LLC.
15683  *
15684  * Originally Released Under LGPL - original licence link has changed is not relivant.
15685  *
15686  * Fork - LGPL
15687  * <script type="text/javascript">
15688  */
15689 /**
15690  * @class Roo.data.HttpProxy
15691  * @extends Roo.data.DataProxy
15692  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15693  * configured to reference a certain URL.<br><br>
15694  * <p>
15695  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15696  * from which the running page was served.<br><br>
15697  * <p>
15698  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15699  * <p>
15700  * Be aware that to enable the browser to parse an XML document, the server must set
15701  * the Content-Type header in the HTTP response to "text/xml".
15702  * @constructor
15703  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15704  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15705  * will be used to make the request.
15706  */
15707 Roo.data.HttpProxy = function(conn){
15708     Roo.data.HttpProxy.superclass.constructor.call(this);
15709     // is conn a conn config or a real conn?
15710     this.conn = conn;
15711     this.useAjax = !conn || !conn.events;
15712   
15713 };
15714
15715 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15716     // thse are take from connection...
15717     
15718     /**
15719      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15720      */
15721     /**
15722      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15723      * extra parameters to each request made by this object. (defaults to undefined)
15724      */
15725     /**
15726      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15727      *  to each request made by this object. (defaults to undefined)
15728      */
15729     /**
15730      * @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)
15731      */
15732     /**
15733      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15734      */
15735      /**
15736      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15737      * @type Boolean
15738      */
15739   
15740
15741     /**
15742      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15743      * @type Boolean
15744      */
15745     /**
15746      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15747      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15748      * a finer-grained basis than the DataProxy events.
15749      */
15750     getConnection : function(){
15751         return this.useAjax ? Roo.Ajax : this.conn;
15752     },
15753
15754     /**
15755      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15756      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15757      * process that block using the passed callback.
15758      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15759      * for the request to the remote server.
15760      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15761      * object into a block of Roo.data.Records.
15762      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15763      * The function must be passed <ul>
15764      * <li>The Record block object</li>
15765      * <li>The "arg" argument from the load function</li>
15766      * <li>A boolean success indicator</li>
15767      * </ul>
15768      * @param {Object} scope The scope in which to call the callback
15769      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15770      */
15771     load : function(params, reader, callback, scope, arg){
15772         if(this.fireEvent("beforeload", this, params) !== false){
15773             var  o = {
15774                 params : params || {},
15775                 request: {
15776                     callback : callback,
15777                     scope : scope,
15778                     arg : arg
15779                 },
15780                 reader: reader,
15781                 callback : this.loadResponse,
15782                 scope: this
15783             };
15784             if(this.useAjax){
15785                 Roo.applyIf(o, this.conn);
15786                 if(this.activeRequest){
15787                     Roo.Ajax.abort(this.activeRequest);
15788                 }
15789                 this.activeRequest = Roo.Ajax.request(o);
15790             }else{
15791                 this.conn.request(o);
15792             }
15793         }else{
15794             callback.call(scope||this, null, arg, false);
15795         }
15796     },
15797
15798     // private
15799     loadResponse : function(o, success, response){
15800         delete this.activeRequest;
15801         if(!success){
15802             this.fireEvent("loadexception", this, o, response);
15803             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15804             return;
15805         }
15806         var result;
15807         try {
15808             result = o.reader.read(response);
15809         }catch(e){
15810             this.fireEvent("loadexception", this, o, response, e);
15811             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15812             return;
15813         }
15814         
15815         this.fireEvent("load", this, o, o.request.arg);
15816         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15817     },
15818
15819     // private
15820     update : function(dataSet){
15821
15822     },
15823
15824     // private
15825     updateResponse : function(dataSet){
15826
15827     }
15828 });/*
15829  * Based on:
15830  * Ext JS Library 1.1.1
15831  * Copyright(c) 2006-2007, Ext JS, LLC.
15832  *
15833  * Originally Released Under LGPL - original licence link has changed is not relivant.
15834  *
15835  * Fork - LGPL
15836  * <script type="text/javascript">
15837  */
15838
15839 /**
15840  * @class Roo.data.ScriptTagProxy
15841  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15842  * other than the originating domain of the running page.<br><br>
15843  * <p>
15844  * <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
15845  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15846  * <p>
15847  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15848  * source code that is used as the source inside a &lt;script> tag.<br><br>
15849  * <p>
15850  * In order for the browser to process the returned data, the server must wrap the data object
15851  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15852  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15853  * depending on whether the callback name was passed:
15854  * <p>
15855  * <pre><code>
15856 boolean scriptTag = false;
15857 String cb = request.getParameter("callback");
15858 if (cb != null) {
15859     scriptTag = true;
15860     response.setContentType("text/javascript");
15861 } else {
15862     response.setContentType("application/x-json");
15863 }
15864 Writer out = response.getWriter();
15865 if (scriptTag) {
15866     out.write(cb + "(");
15867 }
15868 out.print(dataBlock.toJsonString());
15869 if (scriptTag) {
15870     out.write(");");
15871 }
15872 </pre></code>
15873  *
15874  * @constructor
15875  * @param {Object} config A configuration object.
15876  */
15877 Roo.data.ScriptTagProxy = function(config){
15878     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15879     Roo.apply(this, config);
15880     this.head = document.getElementsByTagName("head")[0];
15881 };
15882
15883 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15884
15885 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15886     /**
15887      * @cfg {String} url The URL from which to request the data object.
15888      */
15889     /**
15890      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15891      */
15892     timeout : 30000,
15893     /**
15894      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15895      * the server the name of the callback function set up by the load call to process the returned data object.
15896      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15897      * javascript output which calls this named function passing the data object as its only parameter.
15898      */
15899     callbackParam : "callback",
15900     /**
15901      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15902      * name to the request.
15903      */
15904     nocache : true,
15905
15906     /**
15907      * Load data from the configured URL, read the data object into
15908      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15909      * process that block using the passed callback.
15910      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15911      * for the request to the remote server.
15912      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15913      * object into a block of Roo.data.Records.
15914      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15915      * The function must be passed <ul>
15916      * <li>The Record block object</li>
15917      * <li>The "arg" argument from the load function</li>
15918      * <li>A boolean success indicator</li>
15919      * </ul>
15920      * @param {Object} scope The scope in which to call the callback
15921      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15922      */
15923     load : function(params, reader, callback, scope, arg){
15924         if(this.fireEvent("beforeload", this, params) !== false){
15925
15926             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15927
15928             var url = this.url;
15929             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15930             if(this.nocache){
15931                 url += "&_dc=" + (new Date().getTime());
15932             }
15933             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15934             var trans = {
15935                 id : transId,
15936                 cb : "stcCallback"+transId,
15937                 scriptId : "stcScript"+transId,
15938                 params : params,
15939                 arg : arg,
15940                 url : url,
15941                 callback : callback,
15942                 scope : scope,
15943                 reader : reader
15944             };
15945             var conn = this;
15946
15947             window[trans.cb] = function(o){
15948                 conn.handleResponse(o, trans);
15949             };
15950
15951             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15952
15953             if(this.autoAbort !== false){
15954                 this.abort();
15955             }
15956
15957             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15958
15959             var script = document.createElement("script");
15960             script.setAttribute("src", url);
15961             script.setAttribute("type", "text/javascript");
15962             script.setAttribute("id", trans.scriptId);
15963             this.head.appendChild(script);
15964
15965             this.trans = trans;
15966         }else{
15967             callback.call(scope||this, null, arg, false);
15968         }
15969     },
15970
15971     // private
15972     isLoading : function(){
15973         return this.trans ? true : false;
15974     },
15975
15976     /**
15977      * Abort the current server request.
15978      */
15979     abort : function(){
15980         if(this.isLoading()){
15981             this.destroyTrans(this.trans);
15982         }
15983     },
15984
15985     // private
15986     destroyTrans : function(trans, isLoaded){
15987         this.head.removeChild(document.getElementById(trans.scriptId));
15988         clearTimeout(trans.timeoutId);
15989         if(isLoaded){
15990             window[trans.cb] = undefined;
15991             try{
15992                 delete window[trans.cb];
15993             }catch(e){}
15994         }else{
15995             // if hasn't been loaded, wait for load to remove it to prevent script error
15996             window[trans.cb] = function(){
15997                 window[trans.cb] = undefined;
15998                 try{
15999                     delete window[trans.cb];
16000                 }catch(e){}
16001             };
16002         }
16003     },
16004
16005     // private
16006     handleResponse : function(o, trans){
16007         this.trans = false;
16008         this.destroyTrans(trans, true);
16009         var result;
16010         try {
16011             result = trans.reader.readRecords(o);
16012         }catch(e){
16013             this.fireEvent("loadexception", this, o, trans.arg, e);
16014             trans.callback.call(trans.scope||window, null, trans.arg, false);
16015             return;
16016         }
16017         this.fireEvent("load", this, o, trans.arg);
16018         trans.callback.call(trans.scope||window, result, trans.arg, true);
16019     },
16020
16021     // private
16022     handleFailure : function(trans){
16023         this.trans = false;
16024         this.destroyTrans(trans, false);
16025         this.fireEvent("loadexception", this, null, trans.arg);
16026         trans.callback.call(trans.scope||window, null, trans.arg, false);
16027     }
16028 });/*
16029  * Based on:
16030  * Ext JS Library 1.1.1
16031  * Copyright(c) 2006-2007, Ext JS, LLC.
16032  *
16033  * Originally Released Under LGPL - original licence link has changed is not relivant.
16034  *
16035  * Fork - LGPL
16036  * <script type="text/javascript">
16037  */
16038
16039 /**
16040  * @class Roo.data.JsonReader
16041  * @extends Roo.data.DataReader
16042  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16043  * based on mappings in a provided Roo.data.Record constructor.
16044  * 
16045  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16046  * in the reply previously. 
16047  * 
16048  * <p>
16049  * Example code:
16050  * <pre><code>
16051 var RecordDef = Roo.data.Record.create([
16052     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16053     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16054 ]);
16055 var myReader = new Roo.data.JsonReader({
16056     totalProperty: "results",    // The property which contains the total dataset size (optional)
16057     root: "rows",                // The property which contains an Array of row objects
16058     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16059 }, RecordDef);
16060 </code></pre>
16061  * <p>
16062  * This would consume a JSON file like this:
16063  * <pre><code>
16064 { 'results': 2, 'rows': [
16065     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16066     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16067 }
16068 </code></pre>
16069  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16070  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16071  * paged from the remote server.
16072  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16073  * @cfg {String} root name of the property which contains the Array of row objects.
16074  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16075  * @cfg {Array} fields Array of field definition objects
16076  * @constructor
16077  * Create a new JsonReader
16078  * @param {Object} meta Metadata configuration options
16079  * @param {Object} recordType Either an Array of field definition objects,
16080  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16081  */
16082 Roo.data.JsonReader = function(meta, recordType){
16083     
16084     meta = meta || {};
16085     // set some defaults:
16086     Roo.applyIf(meta, {
16087         totalProperty: 'total',
16088         successProperty : 'success',
16089         root : 'data',
16090         id : 'id'
16091     });
16092     
16093     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16094 };
16095 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16096     
16097     readerType : 'Json',
16098     
16099     /**
16100      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16101      * Used by Store query builder to append _requestMeta to params.
16102      * 
16103      */
16104     metaFromRemote : false,
16105     /**
16106      * This method is only used by a DataProxy which has retrieved data from a remote server.
16107      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16108      * @return {Object} data A data block which is used by an Roo.data.Store object as
16109      * a cache of Roo.data.Records.
16110      */
16111     read : function(response){
16112         var json = response.responseText;
16113        
16114         var o = /* eval:var:o */ eval("("+json+")");
16115         if(!o) {
16116             throw {message: "JsonReader.read: Json object not found"};
16117         }
16118         
16119         if(o.metaData){
16120             
16121             delete this.ef;
16122             this.metaFromRemote = true;
16123             this.meta = o.metaData;
16124             this.recordType = Roo.data.Record.create(o.metaData.fields);
16125             this.onMetaChange(this.meta, this.recordType, o);
16126         }
16127         return this.readRecords(o);
16128     },
16129
16130     // private function a store will implement
16131     onMetaChange : function(meta, recordType, o){
16132
16133     },
16134
16135     /**
16136          * @ignore
16137          */
16138     simpleAccess: function(obj, subsc) {
16139         return obj[subsc];
16140     },
16141
16142         /**
16143          * @ignore
16144          */
16145     getJsonAccessor: function(){
16146         var re = /[\[\.]/;
16147         return function(expr) {
16148             try {
16149                 return(re.test(expr))
16150                     ? new Function("obj", "return obj." + expr)
16151                     : function(obj){
16152                         return obj[expr];
16153                     };
16154             } catch(e){}
16155             return Roo.emptyFn;
16156         };
16157     }(),
16158
16159     /**
16160      * Create a data block containing Roo.data.Records from an XML document.
16161      * @param {Object} o An object which contains an Array of row objects in the property specified
16162      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16163      * which contains the total size of the dataset.
16164      * @return {Object} data A data block which is used by an Roo.data.Store object as
16165      * a cache of Roo.data.Records.
16166      */
16167     readRecords : function(o){
16168         /**
16169          * After any data loads, the raw JSON data is available for further custom processing.
16170          * @type Object
16171          */
16172         this.o = o;
16173         var s = this.meta, Record = this.recordType,
16174             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16175
16176 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16177         if (!this.ef) {
16178             if(s.totalProperty) {
16179                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16180                 }
16181                 if(s.successProperty) {
16182                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16183                 }
16184                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16185                 if (s.id) {
16186                         var g = this.getJsonAccessor(s.id);
16187                         this.getId = function(rec) {
16188                                 var r = g(rec);  
16189                                 return (r === undefined || r === "") ? null : r;
16190                         };
16191                 } else {
16192                         this.getId = function(){return null;};
16193                 }
16194             this.ef = [];
16195             for(var jj = 0; jj < fl; jj++){
16196                 f = fi[jj];
16197                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16198                 this.ef[jj] = this.getJsonAccessor(map);
16199             }
16200         }
16201
16202         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16203         if(s.totalProperty){
16204             var vt = parseInt(this.getTotal(o), 10);
16205             if(!isNaN(vt)){
16206                 totalRecords = vt;
16207             }
16208         }
16209         if(s.successProperty){
16210             var vs = this.getSuccess(o);
16211             if(vs === false || vs === 'false'){
16212                 success = false;
16213             }
16214         }
16215         var records = [];
16216         for(var i = 0; i < c; i++){
16217                 var n = root[i];
16218             var values = {};
16219             var id = this.getId(n);
16220             for(var j = 0; j < fl; j++){
16221                 f = fi[j];
16222             var v = this.ef[j](n);
16223             if (!f.convert) {
16224                 Roo.log('missing convert for ' + f.name);
16225                 Roo.log(f);
16226                 continue;
16227             }
16228             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16229             }
16230             var record = new Record(values, id);
16231             record.json = n;
16232             records[i] = record;
16233         }
16234         return {
16235             raw : o,
16236             success : success,
16237             records : records,
16238             totalRecords : totalRecords
16239         };
16240     },
16241     // used when loading children.. @see loadDataFromChildren
16242     toLoadData: function(rec)
16243     {
16244         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16245         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16246         return { data : data, total : data.length };
16247         
16248     }
16249 });/*
16250  * Based on:
16251  * Ext JS Library 1.1.1
16252  * Copyright(c) 2006-2007, Ext JS, LLC.
16253  *
16254  * Originally Released Under LGPL - original licence link has changed is not relivant.
16255  *
16256  * Fork - LGPL
16257  * <script type="text/javascript">
16258  */
16259
16260 /**
16261  * @class Roo.data.ArrayReader
16262  * @extends Roo.data.DataReader
16263  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16264  * Each element of that Array represents a row of data fields. The
16265  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16266  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16267  * <p>
16268  * Example code:.
16269  * <pre><code>
16270 var RecordDef = Roo.data.Record.create([
16271     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16272     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16273 ]);
16274 var myReader = new Roo.data.ArrayReader({
16275     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16276 }, RecordDef);
16277 </code></pre>
16278  * <p>
16279  * This would consume an Array like this:
16280  * <pre><code>
16281 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16282   </code></pre>
16283  
16284  * @constructor
16285  * Create a new JsonReader
16286  * @param {Object} meta Metadata configuration options.
16287  * @param {Object|Array} recordType Either an Array of field definition objects
16288  * 
16289  * @cfg {Array} fields Array of field definition objects
16290  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16291  * as specified to {@link Roo.data.Record#create},
16292  * or an {@link Roo.data.Record} object
16293  *
16294  * 
16295  * created using {@link Roo.data.Record#create}.
16296  */
16297 Roo.data.ArrayReader = function(meta, recordType)
16298 {    
16299     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16300 };
16301
16302 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16303     
16304       /**
16305      * Create a data block containing Roo.data.Records from an XML document.
16306      * @param {Object} o An Array of row objects which represents the dataset.
16307      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16308      * a cache of Roo.data.Records.
16309      */
16310     readRecords : function(o)
16311     {
16312         var sid = this.meta ? this.meta.id : null;
16313         var recordType = this.recordType, fields = recordType.prototype.fields;
16314         var records = [];
16315         var root = o;
16316         for(var i = 0; i < root.length; i++){
16317             var n = root[i];
16318             var values = {};
16319             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16320             for(var j = 0, jlen = fields.length; j < jlen; j++){
16321                 var f = fields.items[j];
16322                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16323                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16324                 v = f.convert(v);
16325                 values[f.name] = v;
16326             }
16327             var record = new recordType(values, id);
16328             record.json = n;
16329             records[records.length] = record;
16330         }
16331         return {
16332             records : records,
16333             totalRecords : records.length
16334         };
16335     },
16336     // used when loading children.. @see loadDataFromChildren
16337     toLoadData: function(rec)
16338     {
16339         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16340         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16341         
16342     }
16343     
16344     
16345 });/*
16346  * - LGPL
16347  * * 
16348  */
16349
16350 /**
16351  * @class Roo.bootstrap.ComboBox
16352  * @extends Roo.bootstrap.TriggerField
16353  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16354  * @cfg {Boolean} append (true|false) default false
16355  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16356  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16357  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16358  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16359  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16360  * @cfg {Boolean} animate default true
16361  * @cfg {Boolean} emptyResultText only for touch device
16362  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16363  * @cfg {String} emptyTitle default ''
16364  * @cfg {Number} width fixed with? experimental
16365  * @constructor
16366  * Create a new ComboBox.
16367  * @param {Object} config Configuration options
16368  */
16369 Roo.bootstrap.ComboBox = function(config){
16370     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16371     this.addEvents({
16372         /**
16373          * @event expand
16374          * Fires when the dropdown list is expanded
16375         * @param {Roo.bootstrap.ComboBox} combo This combo box
16376         */
16377         'expand' : true,
16378         /**
16379          * @event collapse
16380          * Fires when the dropdown list is collapsed
16381         * @param {Roo.bootstrap.ComboBox} combo This combo box
16382         */
16383         'collapse' : true,
16384         /**
16385          * @event beforeselect
16386          * Fires before a list item is selected. Return false to cancel the selection.
16387         * @param {Roo.bootstrap.ComboBox} combo This combo box
16388         * @param {Roo.data.Record} record The data record returned from the underlying store
16389         * @param {Number} index The index of the selected item in the dropdown list
16390         */
16391         'beforeselect' : true,
16392         /**
16393          * @event select
16394          * Fires when a list item is selected
16395         * @param {Roo.bootstrap.ComboBox} combo This combo box
16396         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16397         * @param {Number} index The index of the selected item in the dropdown list
16398         */
16399         'select' : true,
16400         /**
16401          * @event beforequery
16402          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16403          * The event object passed has these properties:
16404         * @param {Roo.bootstrap.ComboBox} combo This combo box
16405         * @param {String} query The query
16406         * @param {Boolean} forceAll true to force "all" query
16407         * @param {Boolean} cancel true to cancel the query
16408         * @param {Object} e The query event object
16409         */
16410         'beforequery': true,
16411          /**
16412          * @event add
16413          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16414         * @param {Roo.bootstrap.ComboBox} combo This combo box
16415         */
16416         'add' : true,
16417         /**
16418          * @event edit
16419          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16420         * @param {Roo.bootstrap.ComboBox} combo This combo box
16421         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16422         */
16423         'edit' : true,
16424         /**
16425          * @event remove
16426          * Fires when the remove value from the combobox array
16427         * @param {Roo.bootstrap.ComboBox} combo This combo box
16428         */
16429         'remove' : true,
16430         /**
16431          * @event afterremove
16432          * Fires when the remove value from the combobox array
16433         * @param {Roo.bootstrap.ComboBox} combo This combo box
16434         */
16435         'afterremove' : true,
16436         /**
16437          * @event specialfilter
16438          * Fires when specialfilter
16439             * @param {Roo.bootstrap.ComboBox} combo This combo box
16440             */
16441         'specialfilter' : true,
16442         /**
16443          * @event tick
16444          * Fires when tick the element
16445             * @param {Roo.bootstrap.ComboBox} combo This combo box
16446             */
16447         'tick' : true,
16448         /**
16449          * @event touchviewdisplay
16450          * Fires when touch view require special display (default is using displayField)
16451             * @param {Roo.bootstrap.ComboBox} combo This combo box
16452             * @param {Object} cfg set html .
16453             */
16454         'touchviewdisplay' : true
16455         
16456     });
16457     
16458     this.item = [];
16459     this.tickItems = [];
16460     
16461     this.selectedIndex = -1;
16462     if(this.mode == 'local'){
16463         if(config.queryDelay === undefined){
16464             this.queryDelay = 10;
16465         }
16466         if(config.minChars === undefined){
16467             this.minChars = 0;
16468         }
16469     }
16470 };
16471
16472 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16473      
16474     /**
16475      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16476      * rendering into an Roo.Editor, defaults to false)
16477      */
16478     /**
16479      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16480      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16481      */
16482     /**
16483      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16484      */
16485     /**
16486      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16487      * the dropdown list (defaults to undefined, with no header element)
16488      */
16489
16490      /**
16491      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16492      */
16493      
16494      /**
16495      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16496      */
16497     listWidth: undefined,
16498     /**
16499      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16500      * mode = 'remote' or 'text' if mode = 'local')
16501      */
16502     displayField: undefined,
16503     
16504     /**
16505      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16506      * mode = 'remote' or 'value' if mode = 'local'). 
16507      * Note: use of a valueField requires the user make a selection
16508      * in order for a value to be mapped.
16509      */
16510     valueField: undefined,
16511     /**
16512      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16513      */
16514     modalTitle : '',
16515     
16516     /**
16517      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16518      * field's data value (defaults to the underlying DOM element's name)
16519      */
16520     hiddenName: undefined,
16521     /**
16522      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16523      */
16524     listClass: '',
16525     /**
16526      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16527      */
16528     selectedClass: 'active',
16529     
16530     /**
16531      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16532      */
16533     shadow:'sides',
16534     /**
16535      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16536      * anchor positions (defaults to 'tl-bl')
16537      */
16538     listAlign: 'tl-bl?',
16539     /**
16540      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16541      */
16542     maxHeight: 300,
16543     /**
16544      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16545      * query specified by the allQuery config option (defaults to 'query')
16546      */
16547     triggerAction: 'query',
16548     /**
16549      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16550      * (defaults to 4, does not apply if editable = false)
16551      */
16552     minChars : 4,
16553     /**
16554      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16555      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16556      */
16557     typeAhead: false,
16558     /**
16559      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16560      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16561      */
16562     queryDelay: 500,
16563     /**
16564      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16565      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16566      */
16567     pageSize: 0,
16568     /**
16569      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16570      * when editable = true (defaults to false)
16571      */
16572     selectOnFocus:false,
16573     /**
16574      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16575      */
16576     queryParam: 'query',
16577     /**
16578      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16579      * when mode = 'remote' (defaults to 'Loading...')
16580      */
16581     loadingText: 'Loading...',
16582     /**
16583      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16584      */
16585     resizable: false,
16586     /**
16587      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16588      */
16589     handleHeight : 8,
16590     /**
16591      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16592      * traditional select (defaults to true)
16593      */
16594     editable: true,
16595     /**
16596      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16597      */
16598     allQuery: '',
16599     /**
16600      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16601      */
16602     mode: 'remote',
16603     /**
16604      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16605      * listWidth has a higher value)
16606      */
16607     minListWidth : 70,
16608     /**
16609      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16610      * allow the user to set arbitrary text into the field (defaults to false)
16611      */
16612     forceSelection:false,
16613     /**
16614      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16615      * if typeAhead = true (defaults to 250)
16616      */
16617     typeAheadDelay : 250,
16618     /**
16619      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16620      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16621      */
16622     valueNotFoundText : undefined,
16623     /**
16624      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16625      */
16626     blockFocus : false,
16627     
16628     /**
16629      * @cfg {Boolean} disableClear Disable showing of clear button.
16630      */
16631     disableClear : false,
16632     /**
16633      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16634      */
16635     alwaysQuery : false,
16636     
16637     /**
16638      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16639      */
16640     multiple : false,
16641     
16642     /**
16643      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16644      */
16645     invalidClass : "has-warning",
16646     
16647     /**
16648      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16649      */
16650     validClass : "has-success",
16651     
16652     /**
16653      * @cfg {Boolean} specialFilter (true|false) special filter default false
16654      */
16655     specialFilter : false,
16656     
16657     /**
16658      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16659      */
16660     mobileTouchView : true,
16661     
16662     /**
16663      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16664      */
16665     useNativeIOS : false,
16666     
16667     /**
16668      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16669      */
16670     mobile_restrict_height : false,
16671     
16672     ios_options : false,
16673     
16674     //private
16675     addicon : false,
16676     editicon: false,
16677     
16678     page: 0,
16679     hasQuery: false,
16680     append: false,
16681     loadNext: false,
16682     autoFocus : true,
16683     tickable : false,
16684     btnPosition : 'right',
16685     triggerList : true,
16686     showToggleBtn : true,
16687     animate : true,
16688     emptyResultText: 'Empty',
16689     triggerText : 'Select',
16690     emptyTitle : '',
16691     width : false,
16692     
16693     // element that contains real text value.. (when hidden is used..)
16694     
16695     getAutoCreate : function()
16696     {   
16697         var cfg = false;
16698         //render
16699         /*
16700          * Render classic select for iso
16701          */
16702         
16703         if(Roo.isIOS && this.useNativeIOS){
16704             cfg = this.getAutoCreateNativeIOS();
16705             return cfg;
16706         }
16707         
16708         /*
16709          * Touch Devices
16710          */
16711         
16712         if(Roo.isTouch && this.mobileTouchView){
16713             cfg = this.getAutoCreateTouchView();
16714             return cfg;;
16715         }
16716         
16717         /*
16718          *  Normal ComboBox
16719          */
16720         if(!this.tickable){
16721             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16722             return cfg;
16723         }
16724         
16725         /*
16726          *  ComboBox with tickable selections
16727          */
16728              
16729         var align = this.labelAlign || this.parentLabelAlign();
16730         
16731         cfg = {
16732             cls : 'form-group roo-combobox-tickable' //input-group
16733         };
16734         
16735         var btn_text_select = '';
16736         var btn_text_done = '';
16737         var btn_text_cancel = '';
16738         
16739         if (this.btn_text_show) {
16740             btn_text_select = 'Select';
16741             btn_text_done = 'Done';
16742             btn_text_cancel = 'Cancel'; 
16743         }
16744         
16745         var buttons = {
16746             tag : 'div',
16747             cls : 'tickable-buttons',
16748             cn : [
16749                 {
16750                     tag : 'button',
16751                     type : 'button',
16752                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16753                     //html : this.triggerText
16754                     html: btn_text_select
16755                 },
16756                 {
16757                     tag : 'button',
16758                     type : 'button',
16759                     name : 'ok',
16760                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16761                     //html : 'Done'
16762                     html: btn_text_done
16763                 },
16764                 {
16765                     tag : 'button',
16766                     type : 'button',
16767                     name : 'cancel',
16768                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16769                     //html : 'Cancel'
16770                     html: btn_text_cancel
16771                 }
16772             ]
16773         };
16774         
16775         if(this.editable){
16776             buttons.cn.unshift({
16777                 tag: 'input',
16778                 cls: 'roo-select2-search-field-input'
16779             });
16780         }
16781         
16782         var _this = this;
16783         
16784         Roo.each(buttons.cn, function(c){
16785             if (_this.size) {
16786                 c.cls += ' btn-' + _this.size;
16787             }
16788
16789             if (_this.disabled) {
16790                 c.disabled = true;
16791             }
16792         });
16793         
16794         var box = {
16795             tag: 'div',
16796             style : 'display: contents',
16797             cn: [
16798                 {
16799                     tag: 'input',
16800                     type : 'hidden',
16801                     cls: 'form-hidden-field'
16802                 },
16803                 {
16804                     tag: 'ul',
16805                     cls: 'roo-select2-choices',
16806                     cn:[
16807                         {
16808                             tag: 'li',
16809                             cls: 'roo-select2-search-field',
16810                             cn: [
16811                                 buttons
16812                             ]
16813                         }
16814                     ]
16815                 }
16816             ]
16817         };
16818         
16819         var combobox = {
16820             cls: 'roo-select2-container input-group roo-select2-container-multi',
16821             cn: [
16822                 
16823                 box
16824 //                {
16825 //                    tag: 'ul',
16826 //                    cls: 'typeahead typeahead-long dropdown-menu',
16827 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16828 //                }
16829             ]
16830         };
16831         
16832         if(this.hasFeedback && !this.allowBlank){
16833             
16834             var feedback = {
16835                 tag: 'span',
16836                 cls: 'glyphicon form-control-feedback'
16837             };
16838
16839             combobox.cn.push(feedback);
16840         }
16841         
16842         
16843         
16844         var indicator = {
16845             tag : 'i',
16846             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16847             tooltip : 'This field is required'
16848         };
16849         if (Roo.bootstrap.version == 4) {
16850             indicator = {
16851                 tag : 'i',
16852                 style : 'display:none'
16853             };
16854         }
16855         if (align ==='left' && this.fieldLabel.length) {
16856             
16857             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16858             
16859             cfg.cn = [
16860                 indicator,
16861                 {
16862                     tag: 'label',
16863                     'for' :  id,
16864                     cls : 'control-label col-form-label',
16865                     html : this.fieldLabel
16866
16867                 },
16868                 {
16869                     cls : "", 
16870                     cn: [
16871                         combobox
16872                     ]
16873                 }
16874
16875             ];
16876             
16877             var labelCfg = cfg.cn[1];
16878             var contentCfg = cfg.cn[2];
16879             
16880
16881             if(this.indicatorpos == 'right'){
16882                 
16883                 cfg.cn = [
16884                     {
16885                         tag: 'label',
16886                         'for' :  id,
16887                         cls : 'control-label col-form-label',
16888                         cn : [
16889                             {
16890                                 tag : 'span',
16891                                 html : this.fieldLabel
16892                             },
16893                             indicator
16894                         ]
16895                     },
16896                     {
16897                         cls : "",
16898                         cn: [
16899                             combobox
16900                         ]
16901                     }
16902
16903                 ];
16904                 
16905                 
16906                 
16907                 labelCfg = cfg.cn[0];
16908                 contentCfg = cfg.cn[1];
16909             
16910             }
16911             
16912             if(this.labelWidth > 12){
16913                 labelCfg.style = "width: " + this.labelWidth + 'px';
16914             }
16915             if(this.width * 1 > 0){
16916                 contentCfg.style = "width: " + this.width + 'px';
16917             }
16918             if(this.labelWidth < 13 && this.labelmd == 0){
16919                 this.labelmd = this.labelWidth;
16920             }
16921             
16922             if(this.labellg > 0){
16923                 labelCfg.cls += ' col-lg-' + this.labellg;
16924                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16925             }
16926             
16927             if(this.labelmd > 0){
16928                 labelCfg.cls += ' col-md-' + this.labelmd;
16929                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16930             }
16931             
16932             if(this.labelsm > 0){
16933                 labelCfg.cls += ' col-sm-' + this.labelsm;
16934                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16935             }
16936             
16937             if(this.labelxs > 0){
16938                 labelCfg.cls += ' col-xs-' + this.labelxs;
16939                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16940             }
16941                 
16942                 
16943         } else if ( this.fieldLabel.length) {
16944 //                Roo.log(" label");
16945                  cfg.cn = [
16946                    indicator,
16947                     {
16948                         tag: 'label',
16949                         //cls : 'input-group-addon',
16950                         html : this.fieldLabel
16951                     },
16952                     combobox
16953                 ];
16954                 
16955                 if(this.indicatorpos == 'right'){
16956                     cfg.cn = [
16957                         {
16958                             tag: 'label',
16959                             //cls : 'input-group-addon',
16960                             html : this.fieldLabel
16961                         },
16962                         indicator,
16963                         combobox
16964                     ];
16965                     
16966                 }
16967
16968         } else {
16969             
16970 //                Roo.log(" no label && no align");
16971                 cfg = combobox
16972                      
16973                 
16974         }
16975          
16976         var settings=this;
16977         ['xs','sm','md','lg'].map(function(size){
16978             if (settings[size]) {
16979                 cfg.cls += ' col-' + size + '-' + settings[size];
16980             }
16981         });
16982         
16983         return cfg;
16984         
16985     },
16986     
16987     _initEventsCalled : false,
16988     
16989     // private
16990     initEvents: function()
16991     {   
16992         if (this._initEventsCalled) { // as we call render... prevent looping...
16993             return;
16994         }
16995         this._initEventsCalled = true;
16996         
16997         if (!this.store) {
16998             throw "can not find store for combo";
16999         }
17000         
17001         this.indicator = this.indicatorEl();
17002         
17003         this.store = Roo.factory(this.store, Roo.data);
17004         this.store.parent = this;
17005         
17006         // if we are building from html. then this element is so complex, that we can not really
17007         // use the rendered HTML.
17008         // so we have to trash and replace the previous code.
17009         if (Roo.XComponent.build_from_html) {
17010             // remove this element....
17011             var e = this.el.dom, k=0;
17012             while (e ) { e = e.previousSibling;  ++k;}
17013
17014             this.el.remove();
17015             
17016             this.el=false;
17017             this.rendered = false;
17018             
17019             this.render(this.parent().getChildContainer(true), k);
17020         }
17021         
17022         if(Roo.isIOS && this.useNativeIOS){
17023             this.initIOSView();
17024             return;
17025         }
17026         
17027         /*
17028          * Touch Devices
17029          */
17030         
17031         if(Roo.isTouch && this.mobileTouchView){
17032             this.initTouchView();
17033             return;
17034         }
17035         
17036         if(this.tickable){
17037             this.initTickableEvents();
17038             return;
17039         }
17040         
17041         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17042         
17043         if(this.hiddenName){
17044             
17045             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17046             
17047             this.hiddenField.dom.value =
17048                 this.hiddenValue !== undefined ? this.hiddenValue :
17049                 this.value !== undefined ? this.value : '';
17050
17051             // prevent input submission
17052             this.el.dom.removeAttribute('name');
17053             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17054              
17055              
17056         }
17057         //if(Roo.isGecko){
17058         //    this.el.dom.setAttribute('autocomplete', 'off');
17059         //}
17060         
17061         var cls = 'x-combo-list';
17062         
17063         //this.list = new Roo.Layer({
17064         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17065         //});
17066         
17067         var _this = this;
17068         
17069         (function(){
17070             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17071             _this.list.setWidth(lw);
17072         }).defer(100);
17073         
17074         this.list.on('mouseover', this.onViewOver, this);
17075         this.list.on('mousemove', this.onViewMove, this);
17076         this.list.on('scroll', this.onViewScroll, this);
17077         
17078         /*
17079         this.list.swallowEvent('mousewheel');
17080         this.assetHeight = 0;
17081
17082         if(this.title){
17083             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17084             this.assetHeight += this.header.getHeight();
17085         }
17086
17087         this.innerList = this.list.createChild({cls:cls+'-inner'});
17088         this.innerList.on('mouseover', this.onViewOver, this);
17089         this.innerList.on('mousemove', this.onViewMove, this);
17090         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17091         
17092         if(this.allowBlank && !this.pageSize && !this.disableClear){
17093             this.footer = this.list.createChild({cls:cls+'-ft'});
17094             this.pageTb = new Roo.Toolbar(this.footer);
17095            
17096         }
17097         if(this.pageSize){
17098             this.footer = this.list.createChild({cls:cls+'-ft'});
17099             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17100                     {pageSize: this.pageSize});
17101             
17102         }
17103         
17104         if (this.pageTb && this.allowBlank && !this.disableClear) {
17105             var _this = this;
17106             this.pageTb.add(new Roo.Toolbar.Fill(), {
17107                 cls: 'x-btn-icon x-btn-clear',
17108                 text: '&#160;',
17109                 handler: function()
17110                 {
17111                     _this.collapse();
17112                     _this.clearValue();
17113                     _this.onSelect(false, -1);
17114                 }
17115             });
17116         }
17117         if (this.footer) {
17118             this.assetHeight += this.footer.getHeight();
17119         }
17120         */
17121             
17122         if(!this.tpl){
17123             this.tpl = Roo.bootstrap.version == 4 ?
17124                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17125                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17126         }
17127
17128         this.view = new Roo.View(this.list, this.tpl, {
17129             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17130         });
17131         //this.view.wrapEl.setDisplayed(false);
17132         this.view.on('click', this.onViewClick, this);
17133         
17134         
17135         this.store.on('beforeload', this.onBeforeLoad, this);
17136         this.store.on('load', this.onLoad, this);
17137         this.store.on('loadexception', this.onLoadException, this);
17138         /*
17139         if(this.resizable){
17140             this.resizer = new Roo.Resizable(this.list,  {
17141                pinned:true, handles:'se'
17142             });
17143             this.resizer.on('resize', function(r, w, h){
17144                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17145                 this.listWidth = w;
17146                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17147                 this.restrictHeight();
17148             }, this);
17149             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17150         }
17151         */
17152         if(!this.editable){
17153             this.editable = true;
17154             this.setEditable(false);
17155         }
17156         
17157         /*
17158         
17159         if (typeof(this.events.add.listeners) != 'undefined') {
17160             
17161             this.addicon = this.wrap.createChild(
17162                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17163        
17164             this.addicon.on('click', function(e) {
17165                 this.fireEvent('add', this);
17166             }, this);
17167         }
17168         if (typeof(this.events.edit.listeners) != 'undefined') {
17169             
17170             this.editicon = this.wrap.createChild(
17171                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17172             if (this.addicon) {
17173                 this.editicon.setStyle('margin-left', '40px');
17174             }
17175             this.editicon.on('click', function(e) {
17176                 
17177                 // we fire even  if inothing is selected..
17178                 this.fireEvent('edit', this, this.lastData );
17179                 
17180             }, this);
17181         }
17182         */
17183         
17184         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17185             "up" : function(e){
17186                 this.inKeyMode = true;
17187                 this.selectPrev();
17188             },
17189
17190             "down" : function(e){
17191                 if(!this.isExpanded()){
17192                     this.onTriggerClick();
17193                 }else{
17194                     this.inKeyMode = true;
17195                     this.selectNext();
17196                 }
17197             },
17198
17199             "enter" : function(e){
17200 //                this.onViewClick();
17201                 //return true;
17202                 this.collapse();
17203                 
17204                 if(this.fireEvent("specialkey", this, e)){
17205                     this.onViewClick(false);
17206                 }
17207                 
17208                 return true;
17209             },
17210
17211             "esc" : function(e){
17212                 this.collapse();
17213             },
17214
17215             "tab" : function(e){
17216                 this.collapse();
17217                 
17218                 if(this.fireEvent("specialkey", this, e)){
17219                     this.onViewClick(false);
17220                 }
17221                 
17222                 return true;
17223             },
17224
17225             scope : this,
17226
17227             doRelay : function(foo, bar, hname){
17228                 if(hname == 'down' || this.scope.isExpanded()){
17229                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17230                 }
17231                 return true;
17232             },
17233
17234             forceKeyDown: true
17235         });
17236         
17237         
17238         this.queryDelay = Math.max(this.queryDelay || 10,
17239                 this.mode == 'local' ? 10 : 250);
17240         
17241         
17242         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17243         
17244         if(this.typeAhead){
17245             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17246         }
17247         if(this.editable !== false){
17248             this.inputEl().on("keyup", this.onKeyUp, this);
17249         }
17250         if(this.forceSelection){
17251             this.inputEl().on('blur', this.doForce, this);
17252         }
17253         
17254         if(this.multiple){
17255             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17256             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17257         }
17258     },
17259     
17260     initTickableEvents: function()
17261     {   
17262         this.createList();
17263         
17264         if(this.hiddenName){
17265             
17266             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17267             
17268             this.hiddenField.dom.value =
17269                 this.hiddenValue !== undefined ? this.hiddenValue :
17270                 this.value !== undefined ? this.value : '';
17271
17272             // prevent input submission
17273             this.el.dom.removeAttribute('name');
17274             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17275              
17276              
17277         }
17278         
17279 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17280         
17281         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17282         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17283         if(this.triggerList){
17284             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17285         }
17286          
17287         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17288         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17289         
17290         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17291         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17292         
17293         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17294         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17295         
17296         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17297         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17298         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17299         
17300         this.okBtn.hide();
17301         this.cancelBtn.hide();
17302         
17303         var _this = this;
17304         
17305         (function(){
17306             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17307             _this.list.setWidth(lw);
17308         }).defer(100);
17309         
17310         this.list.on('mouseover', this.onViewOver, this);
17311         this.list.on('mousemove', this.onViewMove, this);
17312         
17313         this.list.on('scroll', this.onViewScroll, this);
17314         
17315         if(!this.tpl){
17316             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17317                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17318         }
17319
17320         this.view = new Roo.View(this.list, this.tpl, {
17321             singleSelect:true,
17322             tickable:true,
17323             parent:this,
17324             store: this.store,
17325             selectedClass: this.selectedClass
17326         });
17327         
17328         //this.view.wrapEl.setDisplayed(false);
17329         this.view.on('click', this.onViewClick, this);
17330         
17331         
17332         
17333         this.store.on('beforeload', this.onBeforeLoad, this);
17334         this.store.on('load', this.onLoad, this);
17335         this.store.on('loadexception', this.onLoadException, this);
17336         
17337         if(this.editable){
17338             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17339                 "up" : function(e){
17340                     this.inKeyMode = true;
17341                     this.selectPrev();
17342                 },
17343
17344                 "down" : function(e){
17345                     this.inKeyMode = true;
17346                     this.selectNext();
17347                 },
17348
17349                 "enter" : function(e){
17350                     if(this.fireEvent("specialkey", this, e)){
17351                         this.onViewClick(false);
17352                     }
17353                     
17354                     return true;
17355                 },
17356
17357                 "esc" : function(e){
17358                     this.onTickableFooterButtonClick(e, false, false);
17359                 },
17360
17361                 "tab" : function(e){
17362                     this.fireEvent("specialkey", this, e);
17363                     
17364                     this.onTickableFooterButtonClick(e, false, false);
17365                     
17366                     return true;
17367                 },
17368
17369                 scope : this,
17370
17371                 doRelay : function(e, fn, key){
17372                     if(this.scope.isExpanded()){
17373                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17374                     }
17375                     return true;
17376                 },
17377
17378                 forceKeyDown: true
17379             });
17380         }
17381         
17382         this.queryDelay = Math.max(this.queryDelay || 10,
17383                 this.mode == 'local' ? 10 : 250);
17384         
17385         
17386         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17387         
17388         if(this.typeAhead){
17389             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17390         }
17391         
17392         if(this.editable !== false){
17393             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17394         }
17395         
17396         this.indicator = this.indicatorEl();
17397         
17398         if(this.indicator){
17399             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17400             this.indicator.hide();
17401         }
17402         
17403     },
17404
17405     onDestroy : function(){
17406         if(this.view){
17407             this.view.setStore(null);
17408             this.view.el.removeAllListeners();
17409             this.view.el.remove();
17410             this.view.purgeListeners();
17411         }
17412         if(this.list){
17413             this.list.dom.innerHTML  = '';
17414         }
17415         
17416         if(this.store){
17417             this.store.un('beforeload', this.onBeforeLoad, this);
17418             this.store.un('load', this.onLoad, this);
17419             this.store.un('loadexception', this.onLoadException, this);
17420         }
17421         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17422     },
17423
17424     // private
17425     fireKey : function(e){
17426         if(e.isNavKeyPress() && !this.list.isVisible()){
17427             this.fireEvent("specialkey", this, e);
17428         }
17429     },
17430
17431     // private
17432     onResize: function(w, h)
17433     {
17434         
17435         
17436 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17437 //        
17438 //        if(typeof w != 'number'){
17439 //            // we do not handle it!?!?
17440 //            return;
17441 //        }
17442 //        var tw = this.trigger.getWidth();
17443 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17444 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17445 //        var x = w - tw;
17446 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17447 //            
17448 //        //this.trigger.setStyle('left', x+'px');
17449 //        
17450 //        if(this.list && this.listWidth === undefined){
17451 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17452 //            this.list.setWidth(lw);
17453 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17454 //        }
17455         
17456     
17457         
17458     },
17459
17460     /**
17461      * Allow or prevent the user from directly editing the field text.  If false is passed,
17462      * the user will only be able to select from the items defined in the dropdown list.  This method
17463      * is the runtime equivalent of setting the 'editable' config option at config time.
17464      * @param {Boolean} value True to allow the user to directly edit the field text
17465      */
17466     setEditable : function(value){
17467         if(value == this.editable){
17468             return;
17469         }
17470         this.editable = value;
17471         if(!value){
17472             this.inputEl().dom.setAttribute('readOnly', true);
17473             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17474             this.inputEl().addClass('x-combo-noedit');
17475         }else{
17476             this.inputEl().dom.removeAttribute('readOnly');
17477             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17478             this.inputEl().removeClass('x-combo-noedit');
17479         }
17480     },
17481
17482     // private
17483     
17484     onBeforeLoad : function(combo,opts){
17485         if(!this.hasFocus){
17486             return;
17487         }
17488          if (!opts.add) {
17489             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17490          }
17491         this.restrictHeight();
17492         this.selectedIndex = -1;
17493     },
17494
17495     // private
17496     onLoad : function(){
17497         
17498         this.hasQuery = false;
17499         
17500         if(!this.hasFocus){
17501             return;
17502         }
17503         
17504         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17505             this.loading.hide();
17506         }
17507         
17508         if(this.store.getCount() > 0){
17509             
17510             this.expand();
17511             this.restrictHeight();
17512             if(this.lastQuery == this.allQuery){
17513                 if(this.editable && !this.tickable){
17514                     this.inputEl().dom.select();
17515                 }
17516                 
17517                 if(
17518                     !this.selectByValue(this.value, true) &&
17519                     this.autoFocus && 
17520                     (
17521                         !this.store.lastOptions ||
17522                         typeof(this.store.lastOptions.add) == 'undefined' || 
17523                         this.store.lastOptions.add != true
17524                     )
17525                 ){
17526                     this.select(0, true);
17527                 }
17528             }else{
17529                 if(this.autoFocus){
17530                     this.selectNext();
17531                 }
17532                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17533                     this.taTask.delay(this.typeAheadDelay);
17534                 }
17535             }
17536         }else{
17537             this.onEmptyResults();
17538         }
17539         
17540         //this.el.focus();
17541     },
17542     // private
17543     onLoadException : function()
17544     {
17545         this.hasQuery = false;
17546         
17547         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17548             this.loading.hide();
17549         }
17550         
17551         if(this.tickable && this.editable){
17552             return;
17553         }
17554         
17555         this.collapse();
17556         // only causes errors at present
17557         //Roo.log(this.store.reader.jsonData);
17558         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17559             // fixme
17560             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17561         //}
17562         
17563         
17564     },
17565     // private
17566     onTypeAhead : function(){
17567         if(this.store.getCount() > 0){
17568             var r = this.store.getAt(0);
17569             var newValue = r.data[this.displayField];
17570             var len = newValue.length;
17571             var selStart = this.getRawValue().length;
17572             
17573             if(selStart != len){
17574                 this.setRawValue(newValue);
17575                 this.selectText(selStart, newValue.length);
17576             }
17577         }
17578     },
17579
17580     // private
17581     onSelect : function(record, index){
17582         
17583         if(this.fireEvent('beforeselect', this, record, index) !== false){
17584         
17585             this.setFromData(index > -1 ? record.data : false);
17586             
17587             this.collapse();
17588             this.fireEvent('select', this, record, index);
17589         }
17590     },
17591
17592     /**
17593      * Returns the currently selected field value or empty string if no value is set.
17594      * @return {String} value The selected value
17595      */
17596     getValue : function()
17597     {
17598         if(Roo.isIOS && this.useNativeIOS){
17599             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17600         }
17601         
17602         if(this.multiple){
17603             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17604         }
17605         
17606         if(this.valueField){
17607             return typeof this.value != 'undefined' ? this.value : '';
17608         }else{
17609             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17610         }
17611     },
17612     
17613     getRawValue : function()
17614     {
17615         if(Roo.isIOS && this.useNativeIOS){
17616             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17617         }
17618         
17619         var v = this.inputEl().getValue();
17620         
17621         return v;
17622     },
17623
17624     /**
17625      * Clears any text/value currently set in the field
17626      */
17627     clearValue : function(){
17628         
17629         if(this.hiddenField){
17630             this.hiddenField.dom.value = '';
17631         }
17632         this.value = '';
17633         this.setRawValue('');
17634         this.lastSelectionText = '';
17635         this.lastData = false;
17636         
17637         var close = this.closeTriggerEl();
17638         
17639         if(close){
17640             close.hide();
17641         }
17642         
17643         this.validate();
17644         
17645     },
17646
17647     /**
17648      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17649      * will be displayed in the field.  If the value does not match the data value of an existing item,
17650      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17651      * Otherwise the field will be blank (although the value will still be set).
17652      * @param {String} value The value to match
17653      */
17654     setValue : function(v)
17655     {
17656         if(Roo.isIOS && this.useNativeIOS){
17657             this.setIOSValue(v);
17658             return;
17659         }
17660         
17661         if(this.multiple){
17662             this.syncValue();
17663             return;
17664         }
17665         
17666         var text = v;
17667         if(this.valueField){
17668             var r = this.findRecord(this.valueField, v);
17669             if(r){
17670                 text = r.data[this.displayField];
17671             }else if(this.valueNotFoundText !== undefined){
17672                 text = this.valueNotFoundText;
17673             }
17674         }
17675         this.lastSelectionText = text;
17676         if(this.hiddenField){
17677             this.hiddenField.dom.value = v;
17678         }
17679         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17680         this.value = v;
17681         
17682         var close = this.closeTriggerEl();
17683         
17684         if(close){
17685             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17686         }
17687         
17688         this.validate();
17689     },
17690     /**
17691      * @property {Object} the last set data for the element
17692      */
17693     
17694     lastData : false,
17695     /**
17696      * Sets the value of the field based on a object which is related to the record format for the store.
17697      * @param {Object} value the value to set as. or false on reset?
17698      */
17699     setFromData : function(o){
17700         
17701         if(this.multiple){
17702             this.addItem(o);
17703             return;
17704         }
17705             
17706         var dv = ''; // display value
17707         var vv = ''; // value value..
17708         this.lastData = o;
17709         if (this.displayField) {
17710             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17711         } else {
17712             // this is an error condition!!!
17713             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17714         }
17715         
17716         if(this.valueField){
17717             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17718         }
17719         
17720         var close = this.closeTriggerEl();
17721         
17722         if(close){
17723             if(dv.length || vv * 1 > 0){
17724                 close.show() ;
17725                 this.blockFocus=true;
17726             } else {
17727                 close.hide();
17728             }             
17729         }
17730         
17731         if(this.hiddenField){
17732             this.hiddenField.dom.value = vv;
17733             
17734             this.lastSelectionText = dv;
17735             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17736             this.value = vv;
17737             return;
17738         }
17739         // no hidden field.. - we store the value in 'value', but still display
17740         // display field!!!!
17741         this.lastSelectionText = dv;
17742         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17743         this.value = vv;
17744         
17745         
17746         
17747     },
17748     // private
17749     reset : function(){
17750         // overridden so that last data is reset..
17751         
17752         if(this.multiple){
17753             this.clearItem();
17754             return;
17755         }
17756         
17757         this.setValue(this.originalValue);
17758         //this.clearInvalid();
17759         this.lastData = false;
17760         if (this.view) {
17761             this.view.clearSelections();
17762         }
17763         
17764         this.validate();
17765     },
17766     // private
17767     findRecord : function(prop, value){
17768         var record;
17769         if(this.store.getCount() > 0){
17770             this.store.each(function(r){
17771                 if(r.data[prop] == value){
17772                     record = r;
17773                     return false;
17774                 }
17775                 return true;
17776             });
17777         }
17778         return record;
17779     },
17780     
17781     getName: function()
17782     {
17783         // returns hidden if it's set..
17784         if (!this.rendered) {return ''};
17785         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17786         
17787     },
17788     // private
17789     onViewMove : function(e, t){
17790         this.inKeyMode = false;
17791     },
17792
17793     // private
17794     onViewOver : function(e, t){
17795         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17796             return;
17797         }
17798         var item = this.view.findItemFromChild(t);
17799         
17800         if(item){
17801             var index = this.view.indexOf(item);
17802             this.select(index, false);
17803         }
17804     },
17805
17806     // private
17807     onViewClick : function(view, doFocus, el, e)
17808     {
17809         var index = this.view.getSelectedIndexes()[0];
17810         
17811         var r = this.store.getAt(index);
17812         
17813         if(this.tickable){
17814             
17815             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17816                 return;
17817             }
17818             
17819             var rm = false;
17820             var _this = this;
17821             
17822             Roo.each(this.tickItems, function(v,k){
17823                 
17824                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17825                     Roo.log(v);
17826                     _this.tickItems.splice(k, 1);
17827                     
17828                     if(typeof(e) == 'undefined' && view == false){
17829                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17830                     }
17831                     
17832                     rm = true;
17833                     return;
17834                 }
17835             });
17836             
17837             if(rm){
17838                 return;
17839             }
17840             
17841             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17842                 this.tickItems.push(r.data);
17843             }
17844             
17845             if(typeof(e) == 'undefined' && view == false){
17846                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17847             }
17848                     
17849             return;
17850         }
17851         
17852         if(r){
17853             this.onSelect(r, index);
17854         }
17855         if(doFocus !== false && !this.blockFocus){
17856             this.inputEl().focus();
17857         }
17858     },
17859
17860     // private
17861     restrictHeight : function(){
17862         //this.innerList.dom.style.height = '';
17863         //var inner = this.innerList.dom;
17864         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17865         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17866         //this.list.beginUpdate();
17867         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17868         this.list.alignTo(this.inputEl(), this.listAlign);
17869         this.list.alignTo(this.inputEl(), this.listAlign);
17870         //this.list.endUpdate();
17871     },
17872
17873     // private
17874     onEmptyResults : function(){
17875         
17876         if(this.tickable && this.editable){
17877             this.hasFocus = false;
17878             this.restrictHeight();
17879             return;
17880         }
17881         
17882         this.collapse();
17883     },
17884
17885     /**
17886      * Returns true if the dropdown list is expanded, else false.
17887      */
17888     isExpanded : function(){
17889         return this.list.isVisible();
17890     },
17891
17892     /**
17893      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17894      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17895      * @param {String} value The data value of the item to select
17896      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17897      * selected item if it is not currently in view (defaults to true)
17898      * @return {Boolean} True if the value matched an item in the list, else false
17899      */
17900     selectByValue : function(v, scrollIntoView){
17901         if(v !== undefined && v !== null){
17902             var r = this.findRecord(this.valueField || this.displayField, v);
17903             if(r){
17904                 this.select(this.store.indexOf(r), scrollIntoView);
17905                 return true;
17906             }
17907         }
17908         return false;
17909     },
17910
17911     /**
17912      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17913      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17914      * @param {Number} index The zero-based index of the list item to select
17915      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17916      * selected item if it is not currently in view (defaults to true)
17917      */
17918     select : function(index, scrollIntoView){
17919         this.selectedIndex = index;
17920         this.view.select(index);
17921         if(scrollIntoView !== false){
17922             var el = this.view.getNode(index);
17923             /*
17924              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17925              */
17926             if(el){
17927                 this.list.scrollChildIntoView(el, false);
17928             }
17929         }
17930     },
17931
17932     // private
17933     selectNext : function(){
17934         var ct = this.store.getCount();
17935         if(ct > 0){
17936             if(this.selectedIndex == -1){
17937                 this.select(0);
17938             }else if(this.selectedIndex < ct-1){
17939                 this.select(this.selectedIndex+1);
17940             }
17941         }
17942     },
17943
17944     // private
17945     selectPrev : function(){
17946         var ct = this.store.getCount();
17947         if(ct > 0){
17948             if(this.selectedIndex == -1){
17949                 this.select(0);
17950             }else if(this.selectedIndex != 0){
17951                 this.select(this.selectedIndex-1);
17952             }
17953         }
17954     },
17955
17956     // private
17957     onKeyUp : function(e){
17958         if(this.editable !== false && !e.isSpecialKey()){
17959             this.lastKey = e.getKey();
17960             this.dqTask.delay(this.queryDelay);
17961         }
17962     },
17963
17964     // private
17965     validateBlur : function(){
17966         return !this.list || !this.list.isVisible();   
17967     },
17968
17969     // private
17970     initQuery : function(){
17971         
17972         var v = this.getRawValue();
17973         
17974         if(this.tickable && this.editable){
17975             v = this.tickableInputEl().getValue();
17976         }
17977         
17978         this.doQuery(v);
17979     },
17980
17981     // private
17982     doForce : function(){
17983         if(this.inputEl().dom.value.length > 0){
17984             this.inputEl().dom.value =
17985                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17986              
17987         }
17988     },
17989
17990     /**
17991      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17992      * query allowing the query action to be canceled if needed.
17993      * @param {String} query The SQL query to execute
17994      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17995      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17996      * saved in the current store (defaults to false)
17997      */
17998     doQuery : function(q, forceAll){
17999         
18000         if(q === undefined || q === null){
18001             q = '';
18002         }
18003         var qe = {
18004             query: q,
18005             forceAll: forceAll,
18006             combo: this,
18007             cancel:false
18008         };
18009         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18010             return false;
18011         }
18012         q = qe.query;
18013         
18014         forceAll = qe.forceAll;
18015         if(forceAll === true || (q.length >= this.minChars)){
18016             
18017             this.hasQuery = true;
18018             
18019             if(this.lastQuery != q || this.alwaysQuery){
18020                 this.lastQuery = q;
18021                 if(this.mode == 'local'){
18022                     this.selectedIndex = -1;
18023                     if(forceAll){
18024                         this.store.clearFilter();
18025                     }else{
18026                         
18027                         if(this.specialFilter){
18028                             this.fireEvent('specialfilter', this);
18029                             this.onLoad();
18030                             return;
18031                         }
18032                         
18033                         this.store.filter(this.displayField, q);
18034                     }
18035                     
18036                     this.store.fireEvent("datachanged", this.store);
18037                     
18038                     this.onLoad();
18039                     
18040                     
18041                 }else{
18042                     
18043                     this.store.baseParams[this.queryParam] = q;
18044                     
18045                     var options = {params : this.getParams(q)};
18046                     
18047                     if(this.loadNext){
18048                         options.add = true;
18049                         options.params.start = this.page * this.pageSize;
18050                     }
18051                     
18052                     this.store.load(options);
18053                     
18054                     /*
18055                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18056                      *  we should expand the list on onLoad
18057                      *  so command out it
18058                      */
18059 //                    this.expand();
18060                 }
18061             }else{
18062                 this.selectedIndex = -1;
18063                 this.onLoad();   
18064             }
18065         }
18066         
18067         this.loadNext = false;
18068     },
18069     
18070     // private
18071     getParams : function(q){
18072         var p = {};
18073         //p[this.queryParam] = q;
18074         
18075         if(this.pageSize){
18076             p.start = 0;
18077             p.limit = this.pageSize;
18078         }
18079         return p;
18080     },
18081
18082     /**
18083      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18084      */
18085     collapse : function(){
18086         if(!this.isExpanded()){
18087             return;
18088         }
18089         
18090         this.list.hide();
18091         
18092         this.hasFocus = false;
18093         
18094         if(this.tickable){
18095             this.okBtn.hide();
18096             this.cancelBtn.hide();
18097             this.trigger.show();
18098             
18099             if(this.editable){
18100                 this.tickableInputEl().dom.value = '';
18101                 this.tickableInputEl().blur();
18102             }
18103             
18104         }
18105         
18106         Roo.get(document).un('mousedown', this.collapseIf, this);
18107         Roo.get(document).un('mousewheel', this.collapseIf, this);
18108         if (!this.editable) {
18109             Roo.get(document).un('keydown', this.listKeyPress, this);
18110         }
18111         this.fireEvent('collapse', this);
18112         
18113         this.validate();
18114     },
18115
18116     // private
18117     collapseIf : function(e){
18118         var in_combo  = e.within(this.el);
18119         var in_list =  e.within(this.list);
18120         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18121         
18122         if (in_combo || in_list || is_list) {
18123             //e.stopPropagation();
18124             return;
18125         }
18126         
18127         if(this.tickable){
18128             this.onTickableFooterButtonClick(e, false, false);
18129         }
18130
18131         this.collapse();
18132         
18133     },
18134
18135     /**
18136      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18137      */
18138     expand : function(){
18139        
18140         if(this.isExpanded() || !this.hasFocus){
18141             return;
18142         }
18143         
18144         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18145         this.list.setWidth(lw);
18146         
18147         Roo.log('expand');
18148         
18149         this.list.show();
18150         
18151         this.restrictHeight();
18152         
18153         if(this.tickable){
18154             
18155             this.tickItems = Roo.apply([], this.item);
18156             
18157             this.okBtn.show();
18158             this.cancelBtn.show();
18159             this.trigger.hide();
18160             
18161             if(this.editable){
18162                 this.tickableInputEl().focus();
18163             }
18164             
18165         }
18166         
18167         Roo.get(document).on('mousedown', this.collapseIf, this);
18168         Roo.get(document).on('mousewheel', this.collapseIf, this);
18169         if (!this.editable) {
18170             Roo.get(document).on('keydown', this.listKeyPress, this);
18171         }
18172         
18173         this.fireEvent('expand', this);
18174     },
18175
18176     // private
18177     // Implements the default empty TriggerField.onTriggerClick function
18178     onTriggerClick : function(e)
18179     {
18180         Roo.log('trigger click');
18181         
18182         if(this.disabled || !this.triggerList){
18183             return;
18184         }
18185         
18186         this.page = 0;
18187         this.loadNext = false;
18188         
18189         if(this.isExpanded()){
18190             this.collapse();
18191             if (!this.blockFocus) {
18192                 this.inputEl().focus();
18193             }
18194             
18195         }else {
18196             this.hasFocus = true;
18197             if(this.triggerAction == 'all') {
18198                 this.doQuery(this.allQuery, true);
18199             } else {
18200                 this.doQuery(this.getRawValue());
18201             }
18202             if (!this.blockFocus) {
18203                 this.inputEl().focus();
18204             }
18205         }
18206     },
18207     
18208     onTickableTriggerClick : function(e)
18209     {
18210         if(this.disabled){
18211             return;
18212         }
18213         
18214         this.page = 0;
18215         this.loadNext = false;
18216         this.hasFocus = true;
18217         
18218         if(this.triggerAction == 'all') {
18219             this.doQuery(this.allQuery, true);
18220         } else {
18221             this.doQuery(this.getRawValue());
18222         }
18223     },
18224     
18225     onSearchFieldClick : function(e)
18226     {
18227         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18228             this.onTickableFooterButtonClick(e, false, false);
18229             return;
18230         }
18231         
18232         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18233             return;
18234         }
18235         
18236         this.page = 0;
18237         this.loadNext = false;
18238         this.hasFocus = true;
18239         
18240         if(this.triggerAction == 'all') {
18241             this.doQuery(this.allQuery, true);
18242         } else {
18243             this.doQuery(this.getRawValue());
18244         }
18245     },
18246     
18247     listKeyPress : function(e)
18248     {
18249         //Roo.log('listkeypress');
18250         // scroll to first matching element based on key pres..
18251         if (e.isSpecialKey()) {
18252             return false;
18253         }
18254         var k = String.fromCharCode(e.getKey()).toUpperCase();
18255         //Roo.log(k);
18256         var match  = false;
18257         var csel = this.view.getSelectedNodes();
18258         var cselitem = false;
18259         if (csel.length) {
18260             var ix = this.view.indexOf(csel[0]);
18261             cselitem  = this.store.getAt(ix);
18262             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18263                 cselitem = false;
18264             }
18265             
18266         }
18267         
18268         this.store.each(function(v) { 
18269             if (cselitem) {
18270                 // start at existing selection.
18271                 if (cselitem.id == v.id) {
18272                     cselitem = false;
18273                 }
18274                 return true;
18275             }
18276                 
18277             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18278                 match = this.store.indexOf(v);
18279                 return false;
18280             }
18281             return true;
18282         }, this);
18283         
18284         if (match === false) {
18285             return true; // no more action?
18286         }
18287         // scroll to?
18288         this.view.select(match);
18289         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18290         sn.scrollIntoView(sn.dom.parentNode, false);
18291     },
18292     
18293     onViewScroll : function(e, t){
18294         
18295         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){
18296             return;
18297         }
18298         
18299         this.hasQuery = true;
18300         
18301         this.loading = this.list.select('.loading', true).first();
18302         
18303         if(this.loading === null){
18304             this.list.createChild({
18305                 tag: 'div',
18306                 cls: 'loading roo-select2-more-results roo-select2-active',
18307                 html: 'Loading more results...'
18308             });
18309             
18310             this.loading = this.list.select('.loading', true).first();
18311             
18312             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18313             
18314             this.loading.hide();
18315         }
18316         
18317         this.loading.show();
18318         
18319         var _combo = this;
18320         
18321         this.page++;
18322         this.loadNext = true;
18323         
18324         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18325         
18326         return;
18327     },
18328     
18329     addItem : function(o)
18330     {   
18331         var dv = ''; // display value
18332         
18333         if (this.displayField) {
18334             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18335         } else {
18336             // this is an error condition!!!
18337             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18338         }
18339         
18340         if(!dv.length){
18341             return;
18342         }
18343         
18344         var choice = this.choices.createChild({
18345             tag: 'li',
18346             cls: 'roo-select2-search-choice',
18347             cn: [
18348                 {
18349                     tag: 'div',
18350                     html: dv
18351                 },
18352                 {
18353                     tag: 'a',
18354                     href: '#',
18355                     cls: 'roo-select2-search-choice-close fa fa-times',
18356                     tabindex: '-1'
18357                 }
18358             ]
18359             
18360         }, this.searchField);
18361         
18362         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18363         
18364         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18365         
18366         this.item.push(o);
18367         
18368         this.lastData = o;
18369         
18370         this.syncValue();
18371         
18372         this.inputEl().dom.value = '';
18373         
18374         this.validate();
18375     },
18376     
18377     onRemoveItem : function(e, _self, o)
18378     {
18379         e.preventDefault();
18380         
18381         this.lastItem = Roo.apply([], this.item);
18382         
18383         var index = this.item.indexOf(o.data) * 1;
18384         
18385         if( index < 0){
18386             Roo.log('not this item?!');
18387             return;
18388         }
18389         
18390         this.item.splice(index, 1);
18391         o.item.remove();
18392         
18393         this.syncValue();
18394         
18395         this.fireEvent('remove', this, e);
18396         
18397         this.validate();
18398         
18399     },
18400     
18401     syncValue : function()
18402     {
18403         if(!this.item.length){
18404             this.clearValue();
18405             return;
18406         }
18407             
18408         var value = [];
18409         var _this = this;
18410         Roo.each(this.item, function(i){
18411             if(_this.valueField){
18412                 value.push(i[_this.valueField]);
18413                 return;
18414             }
18415
18416             value.push(i);
18417         });
18418
18419         this.value = value.join(',');
18420
18421         if(this.hiddenField){
18422             this.hiddenField.dom.value = this.value;
18423         }
18424         
18425         this.store.fireEvent("datachanged", this.store);
18426         
18427         this.validate();
18428     },
18429     
18430     clearItem : function()
18431     {
18432         if(!this.multiple){
18433             return;
18434         }
18435         
18436         this.item = [];
18437         
18438         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18439            c.remove();
18440         });
18441         
18442         this.syncValue();
18443         
18444         this.validate();
18445         
18446         if(this.tickable && !Roo.isTouch){
18447             this.view.refresh();
18448         }
18449     },
18450     
18451     inputEl: function ()
18452     {
18453         if(Roo.isIOS && this.useNativeIOS){
18454             return this.el.select('select.roo-ios-select', true).first();
18455         }
18456         
18457         if(Roo.isTouch && this.mobileTouchView){
18458             return this.el.select('input.form-control',true).first();
18459         }
18460         
18461         if(this.tickable){
18462             return this.searchField;
18463         }
18464         
18465         return this.el.select('input.form-control',true).first();
18466     },
18467     
18468     onTickableFooterButtonClick : function(e, btn, el)
18469     {
18470         e.preventDefault();
18471         
18472         this.lastItem = Roo.apply([], this.item);
18473         
18474         if(btn && btn.name == 'cancel'){
18475             this.tickItems = Roo.apply([], this.item);
18476             this.collapse();
18477             return;
18478         }
18479         
18480         this.clearItem();
18481         
18482         var _this = this;
18483         
18484         Roo.each(this.tickItems, function(o){
18485             _this.addItem(o);
18486         });
18487         
18488         this.collapse();
18489         
18490     },
18491     
18492     validate : function()
18493     {
18494         if(this.getVisibilityEl().hasClass('hidden')){
18495             return true;
18496         }
18497         
18498         var v = this.getRawValue();
18499         
18500         if(this.multiple){
18501             v = this.getValue();
18502         }
18503         
18504         if(this.disabled || this.allowBlank || v.length){
18505             this.markValid();
18506             return true;
18507         }
18508         
18509         this.markInvalid();
18510         return false;
18511     },
18512     
18513     tickableInputEl : function()
18514     {
18515         if(!this.tickable || !this.editable){
18516             return this.inputEl();
18517         }
18518         
18519         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18520     },
18521     
18522     
18523     getAutoCreateTouchView : function()
18524     {
18525         var id = Roo.id();
18526         
18527         var cfg = {
18528             cls: 'form-group' //input-group
18529         };
18530         
18531         var input =  {
18532             tag: 'input',
18533             id : id,
18534             type : this.inputType,
18535             cls : 'form-control x-combo-noedit',
18536             autocomplete: 'new-password',
18537             placeholder : this.placeholder || '',
18538             readonly : true
18539         };
18540         
18541         if (this.name) {
18542             input.name = this.name;
18543         }
18544         
18545         if (this.size) {
18546             input.cls += ' input-' + this.size;
18547         }
18548         
18549         if (this.disabled) {
18550             input.disabled = true;
18551         }
18552         
18553         var inputblock = {
18554             cls : 'roo-combobox-wrap',
18555             cn : [
18556                 input
18557             ]
18558         };
18559         
18560         if(this.before){
18561             inputblock.cls += ' input-group';
18562             
18563             inputblock.cn.unshift({
18564                 tag :'span',
18565                 cls : 'input-group-addon input-group-prepend input-group-text',
18566                 html : this.before
18567             });
18568         }
18569         
18570         if(this.removable && !this.multiple){
18571             inputblock.cls += ' roo-removable';
18572             
18573             inputblock.cn.push({
18574                 tag: 'button',
18575                 html : 'x',
18576                 cls : 'roo-combo-removable-btn close'
18577             });
18578         }
18579
18580         if(this.hasFeedback && !this.allowBlank){
18581             
18582             inputblock.cls += ' has-feedback';
18583             
18584             inputblock.cn.push({
18585                 tag: 'span',
18586                 cls: 'glyphicon form-control-feedback'
18587             });
18588             
18589         }
18590         
18591         if (this.after) {
18592             
18593             inputblock.cls += (this.before) ? '' : ' input-group';
18594             
18595             inputblock.cn.push({
18596                 tag :'span',
18597                 cls : 'input-group-addon input-group-append input-group-text',
18598                 html : this.after
18599             });
18600         }
18601
18602         
18603         var ibwrap = inputblock;
18604         
18605         if(this.multiple){
18606             ibwrap = {
18607                 tag: 'ul',
18608                 cls: 'roo-select2-choices',
18609                 cn:[
18610                     {
18611                         tag: 'li',
18612                         cls: 'roo-select2-search-field',
18613                         cn: [
18614
18615                             inputblock
18616                         ]
18617                     }
18618                 ]
18619             };
18620         
18621             
18622         }
18623         
18624         var combobox = {
18625             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18626             cn: [
18627                 {
18628                     tag: 'input',
18629                     type : 'hidden',
18630                     cls: 'form-hidden-field'
18631                 },
18632                 ibwrap
18633             ]
18634         };
18635         
18636         if(!this.multiple && this.showToggleBtn){
18637             
18638             var caret = {
18639                 cls: 'caret'
18640             };
18641             
18642             if (this.caret != false) {
18643                 caret = {
18644                      tag: 'i',
18645                      cls: 'fa fa-' + this.caret
18646                 };
18647                 
18648             }
18649             
18650             combobox.cn.push({
18651                 tag :'span',
18652                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18653                 cn : [
18654                     Roo.bootstrap.version == 3 ? caret : '',
18655                     {
18656                         tag: 'span',
18657                         cls: 'combobox-clear',
18658                         cn  : [
18659                             {
18660                                 tag : 'i',
18661                                 cls: 'icon-remove'
18662                             }
18663                         ]
18664                     }
18665                 ]
18666
18667             })
18668         }
18669         
18670         if(this.multiple){
18671             combobox.cls += ' roo-select2-container-multi';
18672         }
18673         
18674         var required =  this.allowBlank ?  {
18675                     tag : 'i',
18676                     style: 'display: none'
18677                 } : {
18678                    tag : 'i',
18679                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18680                    tooltip : 'This field is required'
18681                 };
18682         
18683         var align = this.labelAlign || this.parentLabelAlign();
18684         
18685         if (align ==='left' && this.fieldLabel.length) {
18686
18687             cfg.cn = [
18688                 required,
18689                 {
18690                     tag: 'label',
18691                     cls : 'control-label col-form-label',
18692                     html : this.fieldLabel
18693
18694                 },
18695                 {
18696                     cls : 'roo-combobox-wrap ', 
18697                     cn: [
18698                         combobox
18699                     ]
18700                 }
18701             ];
18702             
18703             var labelCfg = cfg.cn[1];
18704             var contentCfg = cfg.cn[2];
18705             
18706
18707             if(this.indicatorpos == 'right'){
18708                 cfg.cn = [
18709                     {
18710                         tag: 'label',
18711                         'for' :  id,
18712                         cls : 'control-label col-form-label',
18713                         cn : [
18714                             {
18715                                 tag : 'span',
18716                                 html : this.fieldLabel
18717                             },
18718                             required
18719                         ]
18720                     },
18721                     {
18722                         cls : "roo-combobox-wrap ",
18723                         cn: [
18724                             combobox
18725                         ]
18726                     }
18727
18728                 ];
18729                 
18730                 labelCfg = cfg.cn[0];
18731                 contentCfg = cfg.cn[1];
18732             }
18733             
18734            
18735             
18736             if(this.labelWidth > 12){
18737                 labelCfg.style = "width: " + this.labelWidth + 'px';
18738             }
18739            
18740             if(this.labelWidth < 13 && this.labelmd == 0){
18741                 this.labelmd = this.labelWidth;
18742             }
18743             
18744             if(this.labellg > 0){
18745                 labelCfg.cls += ' col-lg-' + this.labellg;
18746                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18747             }
18748             
18749             if(this.labelmd > 0){
18750                 labelCfg.cls += ' col-md-' + this.labelmd;
18751                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18752             }
18753             
18754             if(this.labelsm > 0){
18755                 labelCfg.cls += ' col-sm-' + this.labelsm;
18756                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18757             }
18758             
18759             if(this.labelxs > 0){
18760                 labelCfg.cls += ' col-xs-' + this.labelxs;
18761                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18762             }
18763                 
18764                 
18765         } else if ( this.fieldLabel.length) {
18766             cfg.cn = [
18767                required,
18768                 {
18769                     tag: 'label',
18770                     cls : 'control-label',
18771                     html : this.fieldLabel
18772
18773                 },
18774                 {
18775                     cls : '', 
18776                     cn: [
18777                         combobox
18778                     ]
18779                 }
18780             ];
18781             
18782             if(this.indicatorpos == 'right'){
18783                 cfg.cn = [
18784                     {
18785                         tag: 'label',
18786                         cls : 'control-label',
18787                         html : this.fieldLabel,
18788                         cn : [
18789                             required
18790                         ]
18791                     },
18792                     {
18793                         cls : '', 
18794                         cn: [
18795                             combobox
18796                         ]
18797                     }
18798                 ];
18799             }
18800         } else {
18801             cfg.cn = combobox;    
18802         }
18803         
18804         
18805         var settings = this;
18806         
18807         ['xs','sm','md','lg'].map(function(size){
18808             if (settings[size]) {
18809                 cfg.cls += ' col-' + size + '-' + settings[size];
18810             }
18811         });
18812         
18813         return cfg;
18814     },
18815     
18816     initTouchView : function()
18817     {
18818         this.renderTouchView();
18819         
18820         this.touchViewEl.on('scroll', function(){
18821             this.el.dom.scrollTop = 0;
18822         }, this);
18823         
18824         this.originalValue = this.getValue();
18825         
18826         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18827         
18828         this.inputEl().on("click", this.showTouchView, this);
18829         if (this.triggerEl) {
18830             this.triggerEl.on("click", this.showTouchView, this);
18831         }
18832         
18833         
18834         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18835         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18836         
18837         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18838         
18839         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18840         this.store.on('load', this.onTouchViewLoad, this);
18841         this.store.on('loadexception', this.onTouchViewLoadException, this);
18842         
18843         if(this.hiddenName){
18844             
18845             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18846             
18847             this.hiddenField.dom.value =
18848                 this.hiddenValue !== undefined ? this.hiddenValue :
18849                 this.value !== undefined ? this.value : '';
18850         
18851             this.el.dom.removeAttribute('name');
18852             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18853         }
18854         
18855         if(this.multiple){
18856             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18857             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18858         }
18859         
18860         if(this.removable && !this.multiple){
18861             var close = this.closeTriggerEl();
18862             if(close){
18863                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18864                 close.on('click', this.removeBtnClick, this, close);
18865             }
18866         }
18867         /*
18868          * fix the bug in Safari iOS8
18869          */
18870         this.inputEl().on("focus", function(e){
18871             document.activeElement.blur();
18872         }, this);
18873         
18874         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18875         
18876         return;
18877         
18878         
18879     },
18880     
18881     renderTouchView : function()
18882     {
18883         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18884         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18885         
18886         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18887         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18888         
18889         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18890         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18891         this.touchViewBodyEl.setStyle('overflow', 'auto');
18892         
18893         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18894         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18895         
18896         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18897         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18898         
18899     },
18900     
18901     showTouchView : function()
18902     {
18903         if(this.disabled){
18904             return;
18905         }
18906         
18907         this.touchViewHeaderEl.hide();
18908
18909         if(this.modalTitle.length){
18910             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18911             this.touchViewHeaderEl.show();
18912         }
18913
18914         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18915         this.touchViewEl.show();
18916
18917         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18918         
18919         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18920         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18921
18922         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18923
18924         if(this.modalTitle.length){
18925             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18926         }
18927         
18928         this.touchViewBodyEl.setHeight(bodyHeight);
18929
18930         if(this.animate){
18931             var _this = this;
18932             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18933         }else{
18934             this.touchViewEl.addClass(['in','show']);
18935         }
18936         
18937         if(this._touchViewMask){
18938             Roo.get(document.body).addClass("x-body-masked");
18939             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18940             this._touchViewMask.setStyle('z-index', 10000);
18941             this._touchViewMask.addClass('show');
18942         }
18943         
18944         this.doTouchViewQuery();
18945         
18946     },
18947     
18948     hideTouchView : function()
18949     {
18950         this.touchViewEl.removeClass(['in','show']);
18951
18952         if(this.animate){
18953             var _this = this;
18954             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18955         }else{
18956             this.touchViewEl.setStyle('display', 'none');
18957         }
18958         
18959         if(this._touchViewMask){
18960             this._touchViewMask.removeClass('show');
18961             Roo.get(document.body).removeClass("x-body-masked");
18962         }
18963     },
18964     
18965     setTouchViewValue : function()
18966     {
18967         if(this.multiple){
18968             this.clearItem();
18969         
18970             var _this = this;
18971
18972             Roo.each(this.tickItems, function(o){
18973                 this.addItem(o);
18974             }, this);
18975         }
18976         
18977         this.hideTouchView();
18978     },
18979     
18980     doTouchViewQuery : function()
18981     {
18982         var qe = {
18983             query: '',
18984             forceAll: true,
18985             combo: this,
18986             cancel:false
18987         };
18988         
18989         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18990             return false;
18991         }
18992         
18993         if(!this.alwaysQuery || this.mode == 'local'){
18994             this.onTouchViewLoad();
18995             return;
18996         }
18997         
18998         this.store.load();
18999     },
19000     
19001     onTouchViewBeforeLoad : function(combo,opts)
19002     {
19003         return;
19004     },
19005
19006     // private
19007     onTouchViewLoad : function()
19008     {
19009         if(this.store.getCount() < 1){
19010             this.onTouchViewEmptyResults();
19011             return;
19012         }
19013         
19014         this.clearTouchView();
19015         
19016         var rawValue = this.getRawValue();
19017         
19018         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19019         
19020         this.tickItems = [];
19021         
19022         this.store.data.each(function(d, rowIndex){
19023             var row = this.touchViewListGroup.createChild(template);
19024             
19025             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19026                 row.addClass(d.data.cls);
19027             }
19028             
19029             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19030                 var cfg = {
19031                     data : d.data,
19032                     html : d.data[this.displayField]
19033                 };
19034                 
19035                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19036                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19037                 }
19038             }
19039             row.removeClass('selected');
19040             if(!this.multiple && this.valueField &&
19041                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19042             {
19043                 // radio buttons..
19044                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19045                 row.addClass('selected');
19046             }
19047             
19048             if(this.multiple && this.valueField &&
19049                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19050             {
19051                 
19052                 // checkboxes...
19053                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19054                 this.tickItems.push(d.data);
19055             }
19056             
19057             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19058             
19059         }, this);
19060         
19061         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19062         
19063         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19064
19065         if(this.modalTitle.length){
19066             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19067         }
19068
19069         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19070         
19071         if(this.mobile_restrict_height && listHeight < bodyHeight){
19072             this.touchViewBodyEl.setHeight(listHeight);
19073         }
19074         
19075         var _this = this;
19076         
19077         if(firstChecked && listHeight > bodyHeight){
19078             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19079         }
19080         
19081     },
19082     
19083     onTouchViewLoadException : function()
19084     {
19085         this.hideTouchView();
19086     },
19087     
19088     onTouchViewEmptyResults : function()
19089     {
19090         this.clearTouchView();
19091         
19092         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19093         
19094         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19095         
19096     },
19097     
19098     clearTouchView : function()
19099     {
19100         this.touchViewListGroup.dom.innerHTML = '';
19101     },
19102     
19103     onTouchViewClick : function(e, el, o)
19104     {
19105         e.preventDefault();
19106         
19107         var row = o.row;
19108         var rowIndex = o.rowIndex;
19109         
19110         var r = this.store.getAt(rowIndex);
19111         
19112         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19113             
19114             if(!this.multiple){
19115                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19116                     c.dom.removeAttribute('checked');
19117                 }, this);
19118
19119                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19120
19121                 this.setFromData(r.data);
19122
19123                 var close = this.closeTriggerEl();
19124
19125                 if(close){
19126                     close.show();
19127                 }
19128
19129                 this.hideTouchView();
19130
19131                 this.fireEvent('select', this, r, rowIndex);
19132
19133                 return;
19134             }
19135
19136             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19137                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19138                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19139                 return;
19140             }
19141
19142             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19143             this.addItem(r.data);
19144             this.tickItems.push(r.data);
19145         }
19146     },
19147     
19148     getAutoCreateNativeIOS : function()
19149     {
19150         var cfg = {
19151             cls: 'form-group' //input-group,
19152         };
19153         
19154         var combobox =  {
19155             tag: 'select',
19156             cls : 'roo-ios-select'
19157         };
19158         
19159         if (this.name) {
19160             combobox.name = this.name;
19161         }
19162         
19163         if (this.disabled) {
19164             combobox.disabled = true;
19165         }
19166         
19167         var settings = this;
19168         
19169         ['xs','sm','md','lg'].map(function(size){
19170             if (settings[size]) {
19171                 cfg.cls += ' col-' + size + '-' + settings[size];
19172             }
19173         });
19174         
19175         cfg.cn = combobox;
19176         
19177         return cfg;
19178         
19179     },
19180     
19181     initIOSView : function()
19182     {
19183         this.store.on('load', this.onIOSViewLoad, this);
19184         
19185         return;
19186     },
19187     
19188     onIOSViewLoad : function()
19189     {
19190         if(this.store.getCount() < 1){
19191             return;
19192         }
19193         
19194         this.clearIOSView();
19195         
19196         if(this.allowBlank) {
19197             
19198             var default_text = '-- SELECT --';
19199             
19200             if(this.placeholder.length){
19201                 default_text = this.placeholder;
19202             }
19203             
19204             if(this.emptyTitle.length){
19205                 default_text += ' - ' + this.emptyTitle + ' -';
19206             }
19207             
19208             var opt = this.inputEl().createChild({
19209                 tag: 'option',
19210                 value : 0,
19211                 html : default_text
19212             });
19213             
19214             var o = {};
19215             o[this.valueField] = 0;
19216             o[this.displayField] = default_text;
19217             
19218             this.ios_options.push({
19219                 data : o,
19220                 el : opt
19221             });
19222             
19223         }
19224         
19225         this.store.data.each(function(d, rowIndex){
19226             
19227             var html = '';
19228             
19229             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19230                 html = d.data[this.displayField];
19231             }
19232             
19233             var value = '';
19234             
19235             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19236                 value = d.data[this.valueField];
19237             }
19238             
19239             var option = {
19240                 tag: 'option',
19241                 value : value,
19242                 html : html
19243             };
19244             
19245             if(this.value == d.data[this.valueField]){
19246                 option['selected'] = true;
19247             }
19248             
19249             var opt = this.inputEl().createChild(option);
19250             
19251             this.ios_options.push({
19252                 data : d.data,
19253                 el : opt
19254             });
19255             
19256         }, this);
19257         
19258         this.inputEl().on('change', function(){
19259            this.fireEvent('select', this);
19260         }, this);
19261         
19262     },
19263     
19264     clearIOSView: function()
19265     {
19266         this.inputEl().dom.innerHTML = '';
19267         
19268         this.ios_options = [];
19269     },
19270     
19271     setIOSValue: function(v)
19272     {
19273         this.value = v;
19274         
19275         if(!this.ios_options){
19276             return;
19277         }
19278         
19279         Roo.each(this.ios_options, function(opts){
19280            
19281            opts.el.dom.removeAttribute('selected');
19282            
19283            if(opts.data[this.valueField] != v){
19284                return;
19285            }
19286            
19287            opts.el.dom.setAttribute('selected', true);
19288            
19289         }, this);
19290     }
19291
19292     /** 
19293     * @cfg {Boolean} grow 
19294     * @hide 
19295     */
19296     /** 
19297     * @cfg {Number} growMin 
19298     * @hide 
19299     */
19300     /** 
19301     * @cfg {Number} growMax 
19302     * @hide 
19303     */
19304     /**
19305      * @hide
19306      * @method autoSize
19307      */
19308 });
19309
19310 Roo.apply(Roo.bootstrap.ComboBox,  {
19311     
19312     header : {
19313         tag: 'div',
19314         cls: 'modal-header',
19315         cn: [
19316             {
19317                 tag: 'h4',
19318                 cls: 'modal-title'
19319             }
19320         ]
19321     },
19322     
19323     body : {
19324         tag: 'div',
19325         cls: 'modal-body',
19326         cn: [
19327             {
19328                 tag: 'ul',
19329                 cls: 'list-group'
19330             }
19331         ]
19332     },
19333     
19334     listItemRadio : {
19335         tag: 'li',
19336         cls: 'list-group-item',
19337         cn: [
19338             {
19339                 tag: 'span',
19340                 cls: 'roo-combobox-list-group-item-value'
19341             },
19342             {
19343                 tag: 'div',
19344                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19345                 cn: [
19346                     {
19347                         tag: 'input',
19348                         type: 'radio'
19349                     },
19350                     {
19351                         tag: 'label'
19352                     }
19353                 ]
19354             }
19355         ]
19356     },
19357     
19358     listItemCheckbox : {
19359         tag: 'li',
19360         cls: 'list-group-item',
19361         cn: [
19362             {
19363                 tag: 'span',
19364                 cls: 'roo-combobox-list-group-item-value'
19365             },
19366             {
19367                 tag: 'div',
19368                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19369                 cn: [
19370                     {
19371                         tag: 'input',
19372                         type: 'checkbox'
19373                     },
19374                     {
19375                         tag: 'label'
19376                     }
19377                 ]
19378             }
19379         ]
19380     },
19381     
19382     emptyResult : {
19383         tag: 'div',
19384         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19385     },
19386     
19387     footer : {
19388         tag: 'div',
19389         cls: 'modal-footer',
19390         cn: [
19391             {
19392                 tag: 'div',
19393                 cls: 'row',
19394                 cn: [
19395                     {
19396                         tag: 'div',
19397                         cls: 'col-xs-6 text-left',
19398                         cn: {
19399                             tag: 'button',
19400                             cls: 'btn btn-danger roo-touch-view-cancel',
19401                             html: 'Cancel'
19402                         }
19403                     },
19404                     {
19405                         tag: 'div',
19406                         cls: 'col-xs-6 text-right',
19407                         cn: {
19408                             tag: 'button',
19409                             cls: 'btn btn-success roo-touch-view-ok',
19410                             html: 'OK'
19411                         }
19412                     }
19413                 ]
19414             }
19415         ]
19416         
19417     }
19418 });
19419
19420 Roo.apply(Roo.bootstrap.ComboBox,  {
19421     
19422     touchViewTemplate : {
19423         tag: 'div',
19424         cls: 'modal fade roo-combobox-touch-view',
19425         cn: [
19426             {
19427                 tag: 'div',
19428                 cls: 'modal-dialog',
19429                 style : 'position:fixed', // we have to fix position....
19430                 cn: [
19431                     {
19432                         tag: 'div',
19433                         cls: 'modal-content',
19434                         cn: [
19435                             Roo.bootstrap.ComboBox.header,
19436                             Roo.bootstrap.ComboBox.body,
19437                             Roo.bootstrap.ComboBox.footer
19438                         ]
19439                     }
19440                 ]
19441             }
19442         ]
19443     }
19444 });/*
19445  * Based on:
19446  * Ext JS Library 1.1.1
19447  * Copyright(c) 2006-2007, Ext JS, LLC.
19448  *
19449  * Originally Released Under LGPL - original licence link has changed is not relivant.
19450  *
19451  * Fork - LGPL
19452  * <script type="text/javascript">
19453  */
19454
19455 /**
19456  * @class Roo.View
19457  * @extends Roo.util.Observable
19458  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19459  * This class also supports single and multi selection modes. <br>
19460  * Create a data model bound view:
19461  <pre><code>
19462  var store = new Roo.data.Store(...);
19463
19464  var view = new Roo.View({
19465     el : "my-element",
19466     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19467  
19468     singleSelect: true,
19469     selectedClass: "ydataview-selected",
19470     store: store
19471  });
19472
19473  // listen for node click?
19474  view.on("click", function(vw, index, node, e){
19475  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19476  });
19477
19478  // load XML data
19479  dataModel.load("foobar.xml");
19480  </code></pre>
19481  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19482  * <br><br>
19483  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19484  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19485  * 
19486  * Note: old style constructor is still suported (container, template, config)
19487  * 
19488  * @constructor
19489  * Create a new View
19490  * @param {Object} config The config object
19491  * 
19492  */
19493 Roo.View = function(config, depreciated_tpl, depreciated_config){
19494     
19495     this.parent = false;
19496     
19497     if (typeof(depreciated_tpl) == 'undefined') {
19498         // new way.. - universal constructor.
19499         Roo.apply(this, config);
19500         this.el  = Roo.get(this.el);
19501     } else {
19502         // old format..
19503         this.el  = Roo.get(config);
19504         this.tpl = depreciated_tpl;
19505         Roo.apply(this, depreciated_config);
19506     }
19507     this.wrapEl  = this.el.wrap().wrap();
19508     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19509     
19510     
19511     if(typeof(this.tpl) == "string"){
19512         this.tpl = new Roo.Template(this.tpl);
19513     } else {
19514         // support xtype ctors..
19515         this.tpl = new Roo.factory(this.tpl, Roo);
19516     }
19517     
19518     
19519     this.tpl.compile();
19520     
19521     /** @private */
19522     this.addEvents({
19523         /**
19524          * @event beforeclick
19525          * Fires before a click is processed. Returns false to cancel the default action.
19526          * @param {Roo.View} this
19527          * @param {Number} index The index of the target node
19528          * @param {HTMLElement} node The target node
19529          * @param {Roo.EventObject} e The raw event object
19530          */
19531             "beforeclick" : true,
19532         /**
19533          * @event click
19534          * Fires when a template node is clicked.
19535          * @param {Roo.View} this
19536          * @param {Number} index The index of the target node
19537          * @param {HTMLElement} node The target node
19538          * @param {Roo.EventObject} e The raw event object
19539          */
19540             "click" : true,
19541         /**
19542          * @event dblclick
19543          * Fires when a template node is double clicked.
19544          * @param {Roo.View} this
19545          * @param {Number} index The index of the target node
19546          * @param {HTMLElement} node The target node
19547          * @param {Roo.EventObject} e The raw event object
19548          */
19549             "dblclick" : true,
19550         /**
19551          * @event contextmenu
19552          * Fires when a template node is right clicked.
19553          * @param {Roo.View} this
19554          * @param {Number} index The index of the target node
19555          * @param {HTMLElement} node The target node
19556          * @param {Roo.EventObject} e The raw event object
19557          */
19558             "contextmenu" : true,
19559         /**
19560          * @event selectionchange
19561          * Fires when the selected nodes change.
19562          * @param {Roo.View} this
19563          * @param {Array} selections Array of the selected nodes
19564          */
19565             "selectionchange" : true,
19566     
19567         /**
19568          * @event beforeselect
19569          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19570          * @param {Roo.View} this
19571          * @param {HTMLElement} node The node to be selected
19572          * @param {Array} selections Array of currently selected nodes
19573          */
19574             "beforeselect" : true,
19575         /**
19576          * @event preparedata
19577          * Fires on every row to render, to allow you to change the data.
19578          * @param {Roo.View} this
19579          * @param {Object} data to be rendered (change this)
19580          */
19581           "preparedata" : true
19582           
19583           
19584         });
19585
19586
19587
19588     this.el.on({
19589         "click": this.onClick,
19590         "dblclick": this.onDblClick,
19591         "contextmenu": this.onContextMenu,
19592         scope:this
19593     });
19594
19595     this.selections = [];
19596     this.nodes = [];
19597     this.cmp = new Roo.CompositeElementLite([]);
19598     if(this.store){
19599         this.store = Roo.factory(this.store, Roo.data);
19600         this.setStore(this.store, true);
19601     }
19602     
19603     if ( this.footer && this.footer.xtype) {
19604            
19605          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19606         
19607         this.footer.dataSource = this.store;
19608         this.footer.container = fctr;
19609         this.footer = Roo.factory(this.footer, Roo);
19610         fctr.insertFirst(this.el);
19611         
19612         // this is a bit insane - as the paging toolbar seems to detach the el..
19613 //        dom.parentNode.parentNode.parentNode
19614          // they get detached?
19615     }
19616     
19617     
19618     Roo.View.superclass.constructor.call(this);
19619     
19620     
19621 };
19622
19623 Roo.extend(Roo.View, Roo.util.Observable, {
19624     
19625      /**
19626      * @cfg {Roo.data.Store} store Data store to load data from.
19627      */
19628     store : false,
19629     
19630     /**
19631      * @cfg {String|Roo.Element} el The container element.
19632      */
19633     el : '',
19634     
19635     /**
19636      * @cfg {String|Roo.Template} tpl The template used by this View 
19637      */
19638     tpl : false,
19639     /**
19640      * @cfg {String} dataName the named area of the template to use as the data area
19641      *                          Works with domtemplates roo-name="name"
19642      */
19643     dataName: false,
19644     /**
19645      * @cfg {String} selectedClass The css class to add to selected nodes
19646      */
19647     selectedClass : "x-view-selected",
19648      /**
19649      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19650      */
19651     emptyText : "",
19652     
19653     /**
19654      * @cfg {String} text to display on mask (default Loading)
19655      */
19656     mask : false,
19657     /**
19658      * @cfg {Boolean} multiSelect Allow multiple selection
19659      */
19660     multiSelect : false,
19661     /**
19662      * @cfg {Boolean} singleSelect Allow single selection
19663      */
19664     singleSelect:  false,
19665     
19666     /**
19667      * @cfg {Boolean} toggleSelect - selecting 
19668      */
19669     toggleSelect : false,
19670     
19671     /**
19672      * @cfg {Boolean} tickable - selecting 
19673      */
19674     tickable : false,
19675     
19676     /**
19677      * Returns the element this view is bound to.
19678      * @return {Roo.Element}
19679      */
19680     getEl : function(){
19681         return this.wrapEl;
19682     },
19683     
19684     
19685
19686     /**
19687      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19688      */
19689     refresh : function(){
19690         //Roo.log('refresh');
19691         var t = this.tpl;
19692         
19693         // if we are using something like 'domtemplate', then
19694         // the what gets used is:
19695         // t.applySubtemplate(NAME, data, wrapping data..)
19696         // the outer template then get' applied with
19697         //     the store 'extra data'
19698         // and the body get's added to the
19699         //      roo-name="data" node?
19700         //      <span class='roo-tpl-{name}'></span> ?????
19701         
19702         
19703         
19704         this.clearSelections();
19705         this.el.update("");
19706         var html = [];
19707         var records = this.store.getRange();
19708         if(records.length < 1) {
19709             
19710             // is this valid??  = should it render a template??
19711             
19712             this.el.update(this.emptyText);
19713             return;
19714         }
19715         var el = this.el;
19716         if (this.dataName) {
19717             this.el.update(t.apply(this.store.meta)); //????
19718             el = this.el.child('.roo-tpl-' + this.dataName);
19719         }
19720         
19721         for(var i = 0, len = records.length; i < len; i++){
19722             var data = this.prepareData(records[i].data, i, records[i]);
19723             this.fireEvent("preparedata", this, data, i, records[i]);
19724             
19725             var d = Roo.apply({}, data);
19726             
19727             if(this.tickable){
19728                 Roo.apply(d, {'roo-id' : Roo.id()});
19729                 
19730                 var _this = this;
19731             
19732                 Roo.each(this.parent.item, function(item){
19733                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19734                         return;
19735                     }
19736                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19737                 });
19738             }
19739             
19740             html[html.length] = Roo.util.Format.trim(
19741                 this.dataName ?
19742                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19743                     t.apply(d)
19744             );
19745         }
19746         
19747         
19748         
19749         el.update(html.join(""));
19750         this.nodes = el.dom.childNodes;
19751         this.updateIndexes(0);
19752     },
19753     
19754
19755     /**
19756      * Function to override to reformat the data that is sent to
19757      * the template for each node.
19758      * DEPRICATED - use the preparedata event handler.
19759      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19760      * a JSON object for an UpdateManager bound view).
19761      */
19762     prepareData : function(data, index, record)
19763     {
19764         this.fireEvent("preparedata", this, data, index, record);
19765         return data;
19766     },
19767
19768     onUpdate : function(ds, record){
19769         // Roo.log('on update');   
19770         this.clearSelections();
19771         var index = this.store.indexOf(record);
19772         var n = this.nodes[index];
19773         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19774         n.parentNode.removeChild(n);
19775         this.updateIndexes(index, index);
19776     },
19777
19778     
19779     
19780 // --------- FIXME     
19781     onAdd : function(ds, records, index)
19782     {
19783         //Roo.log(['on Add', ds, records, index] );        
19784         this.clearSelections();
19785         if(this.nodes.length == 0){
19786             this.refresh();
19787             return;
19788         }
19789         var n = this.nodes[index];
19790         for(var i = 0, len = records.length; i < len; i++){
19791             var d = this.prepareData(records[i].data, i, records[i]);
19792             if(n){
19793                 this.tpl.insertBefore(n, d);
19794             }else{
19795                 
19796                 this.tpl.append(this.el, d);
19797             }
19798         }
19799         this.updateIndexes(index);
19800     },
19801
19802     onRemove : function(ds, record, index){
19803        // Roo.log('onRemove');
19804         this.clearSelections();
19805         var el = this.dataName  ?
19806             this.el.child('.roo-tpl-' + this.dataName) :
19807             this.el; 
19808         
19809         el.dom.removeChild(this.nodes[index]);
19810         this.updateIndexes(index);
19811     },
19812
19813     /**
19814      * Refresh an individual node.
19815      * @param {Number} index
19816      */
19817     refreshNode : function(index){
19818         this.onUpdate(this.store, this.store.getAt(index));
19819     },
19820
19821     updateIndexes : function(startIndex, endIndex){
19822         var ns = this.nodes;
19823         startIndex = startIndex || 0;
19824         endIndex = endIndex || ns.length - 1;
19825         for(var i = startIndex; i <= endIndex; i++){
19826             ns[i].nodeIndex = i;
19827         }
19828     },
19829
19830     /**
19831      * Changes the data store this view uses and refresh the view.
19832      * @param {Store} store
19833      */
19834     setStore : function(store, initial){
19835         if(!initial && this.store){
19836             this.store.un("datachanged", this.refresh);
19837             this.store.un("add", this.onAdd);
19838             this.store.un("remove", this.onRemove);
19839             this.store.un("update", this.onUpdate);
19840             this.store.un("clear", this.refresh);
19841             this.store.un("beforeload", this.onBeforeLoad);
19842             this.store.un("load", this.onLoad);
19843             this.store.un("loadexception", this.onLoad);
19844         }
19845         if(store){
19846           
19847             store.on("datachanged", this.refresh, this);
19848             store.on("add", this.onAdd, this);
19849             store.on("remove", this.onRemove, this);
19850             store.on("update", this.onUpdate, this);
19851             store.on("clear", this.refresh, this);
19852             store.on("beforeload", this.onBeforeLoad, this);
19853             store.on("load", this.onLoad, this);
19854             store.on("loadexception", this.onLoad, this);
19855         }
19856         
19857         if(store){
19858             this.refresh();
19859         }
19860     },
19861     /**
19862      * onbeforeLoad - masks the loading area.
19863      *
19864      */
19865     onBeforeLoad : function(store,opts)
19866     {
19867          //Roo.log('onBeforeLoad');   
19868         if (!opts.add) {
19869             this.el.update("");
19870         }
19871         this.el.mask(this.mask ? this.mask : "Loading" ); 
19872     },
19873     onLoad : function ()
19874     {
19875         this.el.unmask();
19876     },
19877     
19878
19879     /**
19880      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19881      * @param {HTMLElement} node
19882      * @return {HTMLElement} The template node
19883      */
19884     findItemFromChild : function(node){
19885         var el = this.dataName  ?
19886             this.el.child('.roo-tpl-' + this.dataName,true) :
19887             this.el.dom; 
19888         
19889         if(!node || node.parentNode == el){
19890                     return node;
19891             }
19892             var p = node.parentNode;
19893             while(p && p != el){
19894             if(p.parentNode == el){
19895                 return p;
19896             }
19897             p = p.parentNode;
19898         }
19899             return null;
19900     },
19901
19902     /** @ignore */
19903     onClick : function(e){
19904         var item = this.findItemFromChild(e.getTarget());
19905         if(item){
19906             var index = this.indexOf(item);
19907             if(this.onItemClick(item, index, e) !== false){
19908                 this.fireEvent("click", this, index, item, e);
19909             }
19910         }else{
19911             this.clearSelections();
19912         }
19913     },
19914
19915     /** @ignore */
19916     onContextMenu : function(e){
19917         var item = this.findItemFromChild(e.getTarget());
19918         if(item){
19919             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19920         }
19921     },
19922
19923     /** @ignore */
19924     onDblClick : function(e){
19925         var item = this.findItemFromChild(e.getTarget());
19926         if(item){
19927             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19928         }
19929     },
19930
19931     onItemClick : function(item, index, e)
19932     {
19933         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19934             return false;
19935         }
19936         if (this.toggleSelect) {
19937             var m = this.isSelected(item) ? 'unselect' : 'select';
19938             //Roo.log(m);
19939             var _t = this;
19940             _t[m](item, true, false);
19941             return true;
19942         }
19943         if(this.multiSelect || this.singleSelect){
19944             if(this.multiSelect && e.shiftKey && this.lastSelection){
19945                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19946             }else{
19947                 this.select(item, this.multiSelect && e.ctrlKey);
19948                 this.lastSelection = item;
19949             }
19950             
19951             if(!this.tickable){
19952                 e.preventDefault();
19953             }
19954             
19955         }
19956         return true;
19957     },
19958
19959     /**
19960      * Get the number of selected nodes.
19961      * @return {Number}
19962      */
19963     getSelectionCount : function(){
19964         return this.selections.length;
19965     },
19966
19967     /**
19968      * Get the currently selected nodes.
19969      * @return {Array} An array of HTMLElements
19970      */
19971     getSelectedNodes : function(){
19972         return this.selections;
19973     },
19974
19975     /**
19976      * Get the indexes of the selected nodes.
19977      * @return {Array}
19978      */
19979     getSelectedIndexes : function(){
19980         var indexes = [], s = this.selections;
19981         for(var i = 0, len = s.length; i < len; i++){
19982             indexes.push(s[i].nodeIndex);
19983         }
19984         return indexes;
19985     },
19986
19987     /**
19988      * Clear all selections
19989      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19990      */
19991     clearSelections : function(suppressEvent){
19992         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19993             this.cmp.elements = this.selections;
19994             this.cmp.removeClass(this.selectedClass);
19995             this.selections = [];
19996             if(!suppressEvent){
19997                 this.fireEvent("selectionchange", this, this.selections);
19998             }
19999         }
20000     },
20001
20002     /**
20003      * Returns true if the passed node is selected
20004      * @param {HTMLElement/Number} node The node or node index
20005      * @return {Boolean}
20006      */
20007     isSelected : function(node){
20008         var s = this.selections;
20009         if(s.length < 1){
20010             return false;
20011         }
20012         node = this.getNode(node);
20013         return s.indexOf(node) !== -1;
20014     },
20015
20016     /**
20017      * Selects nodes.
20018      * @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
20019      * @param {Boolean} keepExisting (optional) true to keep existing selections
20020      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20021      */
20022     select : function(nodeInfo, keepExisting, suppressEvent){
20023         if(nodeInfo instanceof Array){
20024             if(!keepExisting){
20025                 this.clearSelections(true);
20026             }
20027             for(var i = 0, len = nodeInfo.length; i < len; i++){
20028                 this.select(nodeInfo[i], true, true);
20029             }
20030             return;
20031         } 
20032         var node = this.getNode(nodeInfo);
20033         if(!node || this.isSelected(node)){
20034             return; // already selected.
20035         }
20036         if(!keepExisting){
20037             this.clearSelections(true);
20038         }
20039         
20040         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20041             Roo.fly(node).addClass(this.selectedClass);
20042             this.selections.push(node);
20043             if(!suppressEvent){
20044                 this.fireEvent("selectionchange", this, this.selections);
20045             }
20046         }
20047         
20048         
20049     },
20050       /**
20051      * Unselects nodes.
20052      * @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
20053      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20054      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20055      */
20056     unselect : function(nodeInfo, keepExisting, suppressEvent)
20057     {
20058         if(nodeInfo instanceof Array){
20059             Roo.each(this.selections, function(s) {
20060                 this.unselect(s, nodeInfo);
20061             }, this);
20062             return;
20063         }
20064         var node = this.getNode(nodeInfo);
20065         if(!node || !this.isSelected(node)){
20066             //Roo.log("not selected");
20067             return; // not selected.
20068         }
20069         // fireevent???
20070         var ns = [];
20071         Roo.each(this.selections, function(s) {
20072             if (s == node ) {
20073                 Roo.fly(node).removeClass(this.selectedClass);
20074
20075                 return;
20076             }
20077             ns.push(s);
20078         },this);
20079         
20080         this.selections= ns;
20081         this.fireEvent("selectionchange", this, this.selections);
20082     },
20083
20084     /**
20085      * Gets a template node.
20086      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20087      * @return {HTMLElement} The node or null if it wasn't found
20088      */
20089     getNode : function(nodeInfo){
20090         if(typeof nodeInfo == "string"){
20091             return document.getElementById(nodeInfo);
20092         }else if(typeof nodeInfo == "number"){
20093             return this.nodes[nodeInfo];
20094         }
20095         return nodeInfo;
20096     },
20097
20098     /**
20099      * Gets a range template nodes.
20100      * @param {Number} startIndex
20101      * @param {Number} endIndex
20102      * @return {Array} An array of nodes
20103      */
20104     getNodes : function(start, end){
20105         var ns = this.nodes;
20106         start = start || 0;
20107         end = typeof end == "undefined" ? ns.length - 1 : end;
20108         var nodes = [];
20109         if(start <= end){
20110             for(var i = start; i <= end; i++){
20111                 nodes.push(ns[i]);
20112             }
20113         } else{
20114             for(var i = start; i >= end; i--){
20115                 nodes.push(ns[i]);
20116             }
20117         }
20118         return nodes;
20119     },
20120
20121     /**
20122      * Finds the index of the passed node
20123      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20124      * @return {Number} The index of the node or -1
20125      */
20126     indexOf : function(node){
20127         node = this.getNode(node);
20128         if(typeof node.nodeIndex == "number"){
20129             return node.nodeIndex;
20130         }
20131         var ns = this.nodes;
20132         for(var i = 0, len = ns.length; i < len; i++){
20133             if(ns[i] == node){
20134                 return i;
20135             }
20136         }
20137         return -1;
20138     }
20139 });
20140 /*
20141  * - LGPL
20142  *
20143  * based on jquery fullcalendar
20144  * 
20145  */
20146
20147 Roo.bootstrap = Roo.bootstrap || {};
20148 /**
20149  * @class Roo.bootstrap.Calendar
20150  * @extends Roo.bootstrap.Component
20151  * Bootstrap Calendar class
20152  * @cfg {Boolean} loadMask (true|false) default false
20153  * @cfg {Object} header generate the user specific header of the calendar, default false
20154
20155  * @constructor
20156  * Create a new Container
20157  * @param {Object} config The config object
20158  */
20159
20160
20161
20162 Roo.bootstrap.Calendar = function(config){
20163     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20164      this.addEvents({
20165         /**
20166              * @event select
20167              * Fires when a date is selected
20168              * @param {DatePicker} this
20169              * @param {Date} date The selected date
20170              */
20171         'select': true,
20172         /**
20173              * @event monthchange
20174              * Fires when the displayed month changes 
20175              * @param {DatePicker} this
20176              * @param {Date} date The selected month
20177              */
20178         'monthchange': true,
20179         /**
20180              * @event evententer
20181              * Fires when mouse over an event
20182              * @param {Calendar} this
20183              * @param {event} Event
20184              */
20185         'evententer': true,
20186         /**
20187              * @event eventleave
20188              * Fires when the mouse leaves an
20189              * @param {Calendar} this
20190              * @param {event}
20191              */
20192         'eventleave': true,
20193         /**
20194              * @event eventclick
20195              * Fires when the mouse click an
20196              * @param {Calendar} this
20197              * @param {event}
20198              */
20199         'eventclick': true
20200         
20201     });
20202
20203 };
20204
20205 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20206     
20207           /**
20208      * @cfg {Roo.data.Store} store
20209      * The data source for the calendar
20210      */
20211         store : false,
20212      /**
20213      * @cfg {Number} startDay
20214      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20215      */
20216     startDay : 0,
20217     
20218     loadMask : false,
20219     
20220     header : false,
20221       
20222     getAutoCreate : function(){
20223         
20224         
20225         var fc_button = function(name, corner, style, content ) {
20226             return Roo.apply({},{
20227                 tag : 'span',
20228                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20229                          (corner.length ?
20230                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20231                             ''
20232                         ),
20233                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20234                 unselectable: 'on'
20235             });
20236         };
20237         
20238         var header = {};
20239         
20240         if(!this.header){
20241             header = {
20242                 tag : 'table',
20243                 cls : 'fc-header',
20244                 style : 'width:100%',
20245                 cn : [
20246                     {
20247                         tag: 'tr',
20248                         cn : [
20249                             {
20250                                 tag : 'td',
20251                                 cls : 'fc-header-left',
20252                                 cn : [
20253                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20254                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20255                                     { tag: 'span', cls: 'fc-header-space' },
20256                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20257
20258
20259                                 ]
20260                             },
20261
20262                             {
20263                                 tag : 'td',
20264                                 cls : 'fc-header-center',
20265                                 cn : [
20266                                     {
20267                                         tag: 'span',
20268                                         cls: 'fc-header-title',
20269                                         cn : {
20270                                             tag: 'H2',
20271                                             html : 'month / year'
20272                                         }
20273                                     }
20274
20275                                 ]
20276                             },
20277                             {
20278                                 tag : 'td',
20279                                 cls : 'fc-header-right',
20280                                 cn : [
20281                               /*      fc_button('month', 'left', '', 'month' ),
20282                                     fc_button('week', '', '', 'week' ),
20283                                     fc_button('day', 'right', '', 'day' )
20284                                 */    
20285
20286                                 ]
20287                             }
20288
20289                         ]
20290                     }
20291                 ]
20292             };
20293         }
20294         
20295         header = this.header;
20296         
20297        
20298         var cal_heads = function() {
20299             var ret = [];
20300             // fixme - handle this.
20301             
20302             for (var i =0; i < Date.dayNames.length; i++) {
20303                 var d = Date.dayNames[i];
20304                 ret.push({
20305                     tag: 'th',
20306                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20307                     html : d.substring(0,3)
20308                 });
20309                 
20310             }
20311             ret[0].cls += ' fc-first';
20312             ret[6].cls += ' fc-last';
20313             return ret;
20314         };
20315         var cal_cell = function(n) {
20316             return  {
20317                 tag: 'td',
20318                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20319                 cn : [
20320                     {
20321                         cn : [
20322                             {
20323                                 cls: 'fc-day-number',
20324                                 html: 'D'
20325                             },
20326                             {
20327                                 cls: 'fc-day-content',
20328                              
20329                                 cn : [
20330                                      {
20331                                         style: 'position: relative;' // height: 17px;
20332                                     }
20333                                 ]
20334                             }
20335                             
20336                             
20337                         ]
20338                     }
20339                 ]
20340                 
20341             }
20342         };
20343         var cal_rows = function() {
20344             
20345             var ret = [];
20346             for (var r = 0; r < 6; r++) {
20347                 var row= {
20348                     tag : 'tr',
20349                     cls : 'fc-week',
20350                     cn : []
20351                 };
20352                 
20353                 for (var i =0; i < Date.dayNames.length; i++) {
20354                     var d = Date.dayNames[i];
20355                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20356
20357                 }
20358                 row.cn[0].cls+=' fc-first';
20359                 row.cn[0].cn[0].style = 'min-height:90px';
20360                 row.cn[6].cls+=' fc-last';
20361                 ret.push(row);
20362                 
20363             }
20364             ret[0].cls += ' fc-first';
20365             ret[4].cls += ' fc-prev-last';
20366             ret[5].cls += ' fc-last';
20367             return ret;
20368             
20369         };
20370         
20371         var cal_table = {
20372             tag: 'table',
20373             cls: 'fc-border-separate',
20374             style : 'width:100%',
20375             cellspacing  : 0,
20376             cn : [
20377                 { 
20378                     tag: 'thead',
20379                     cn : [
20380                         { 
20381                             tag: 'tr',
20382                             cls : 'fc-first fc-last',
20383                             cn : cal_heads()
20384                         }
20385                     ]
20386                 },
20387                 { 
20388                     tag: 'tbody',
20389                     cn : cal_rows()
20390                 }
20391                   
20392             ]
20393         };
20394          
20395          var cfg = {
20396             cls : 'fc fc-ltr',
20397             cn : [
20398                 header,
20399                 {
20400                     cls : 'fc-content',
20401                     style : "position: relative;",
20402                     cn : [
20403                         {
20404                             cls : 'fc-view fc-view-month fc-grid',
20405                             style : 'position: relative',
20406                             unselectable : 'on',
20407                             cn : [
20408                                 {
20409                                     cls : 'fc-event-container',
20410                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20411                                 },
20412                                 cal_table
20413                             ]
20414                         }
20415                     ]
20416     
20417                 }
20418            ] 
20419             
20420         };
20421         
20422          
20423         
20424         return cfg;
20425     },
20426     
20427     
20428     initEvents : function()
20429     {
20430         if(!this.store){
20431             throw "can not find store for calendar";
20432         }
20433         
20434         var mark = {
20435             tag: "div",
20436             cls:"x-dlg-mask",
20437             style: "text-align:center",
20438             cn: [
20439                 {
20440                     tag: "div",
20441                     style: "background-color:white;width:50%;margin:250 auto",
20442                     cn: [
20443                         {
20444                             tag: "img",
20445                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20446                         },
20447                         {
20448                             tag: "span",
20449                             html: "Loading"
20450                         }
20451                         
20452                     ]
20453                 }
20454             ]
20455         };
20456         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20457         
20458         var size = this.el.select('.fc-content', true).first().getSize();
20459         this.maskEl.setSize(size.width, size.height);
20460         this.maskEl.enableDisplayMode("block");
20461         if(!this.loadMask){
20462             this.maskEl.hide();
20463         }
20464         
20465         this.store = Roo.factory(this.store, Roo.data);
20466         this.store.on('load', this.onLoad, this);
20467         this.store.on('beforeload', this.onBeforeLoad, this);
20468         
20469         this.resize();
20470         
20471         this.cells = this.el.select('.fc-day',true);
20472         //Roo.log(this.cells);
20473         this.textNodes = this.el.query('.fc-day-number');
20474         this.cells.addClassOnOver('fc-state-hover');
20475         
20476         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20477         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20478         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20479         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20480         
20481         this.on('monthchange', this.onMonthChange, this);
20482         
20483         this.update(new Date().clearTime());
20484     },
20485     
20486     resize : function() {
20487         var sz  = this.el.getSize();
20488         
20489         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20490         this.el.select('.fc-day-content div',true).setHeight(34);
20491     },
20492     
20493     
20494     // private
20495     showPrevMonth : function(e){
20496         this.update(this.activeDate.add("mo", -1));
20497     },
20498     showToday : function(e){
20499         this.update(new Date().clearTime());
20500     },
20501     // private
20502     showNextMonth : function(e){
20503         this.update(this.activeDate.add("mo", 1));
20504     },
20505
20506     // private
20507     showPrevYear : function(){
20508         this.update(this.activeDate.add("y", -1));
20509     },
20510
20511     // private
20512     showNextYear : function(){
20513         this.update(this.activeDate.add("y", 1));
20514     },
20515
20516     
20517    // private
20518     update : function(date)
20519     {
20520         var vd = this.activeDate;
20521         this.activeDate = date;
20522 //        if(vd && this.el){
20523 //            var t = date.getTime();
20524 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20525 //                Roo.log('using add remove');
20526 //                
20527 //                this.fireEvent('monthchange', this, date);
20528 //                
20529 //                this.cells.removeClass("fc-state-highlight");
20530 //                this.cells.each(function(c){
20531 //                   if(c.dateValue == t){
20532 //                       c.addClass("fc-state-highlight");
20533 //                       setTimeout(function(){
20534 //                            try{c.dom.firstChild.focus();}catch(e){}
20535 //                       }, 50);
20536 //                       return false;
20537 //                   }
20538 //                   return true;
20539 //                });
20540 //                return;
20541 //            }
20542 //        }
20543         
20544         var days = date.getDaysInMonth();
20545         
20546         var firstOfMonth = date.getFirstDateOfMonth();
20547         var startingPos = firstOfMonth.getDay()-this.startDay;
20548         
20549         if(startingPos < this.startDay){
20550             startingPos += 7;
20551         }
20552         
20553         var pm = date.add(Date.MONTH, -1);
20554         var prevStart = pm.getDaysInMonth()-startingPos;
20555 //        
20556         this.cells = this.el.select('.fc-day',true);
20557         this.textNodes = this.el.query('.fc-day-number');
20558         this.cells.addClassOnOver('fc-state-hover');
20559         
20560         var cells = this.cells.elements;
20561         var textEls = this.textNodes;
20562         
20563         Roo.each(cells, function(cell){
20564             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20565         });
20566         
20567         days += startingPos;
20568
20569         // convert everything to numbers so it's fast
20570         var day = 86400000;
20571         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20572         //Roo.log(d);
20573         //Roo.log(pm);
20574         //Roo.log(prevStart);
20575         
20576         var today = new Date().clearTime().getTime();
20577         var sel = date.clearTime().getTime();
20578         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20579         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20580         var ddMatch = this.disabledDatesRE;
20581         var ddText = this.disabledDatesText;
20582         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20583         var ddaysText = this.disabledDaysText;
20584         var format = this.format;
20585         
20586         var setCellClass = function(cal, cell){
20587             cell.row = 0;
20588             cell.events = [];
20589             cell.more = [];
20590             //Roo.log('set Cell Class');
20591             cell.title = "";
20592             var t = d.getTime();
20593             
20594             //Roo.log(d);
20595             
20596             cell.dateValue = t;
20597             if(t == today){
20598                 cell.className += " fc-today";
20599                 cell.className += " fc-state-highlight";
20600                 cell.title = cal.todayText;
20601             }
20602             if(t == sel){
20603                 // disable highlight in other month..
20604                 //cell.className += " fc-state-highlight";
20605                 
20606             }
20607             // disabling
20608             if(t < min) {
20609                 cell.className = " fc-state-disabled";
20610                 cell.title = cal.minText;
20611                 return;
20612             }
20613             if(t > max) {
20614                 cell.className = " fc-state-disabled";
20615                 cell.title = cal.maxText;
20616                 return;
20617             }
20618             if(ddays){
20619                 if(ddays.indexOf(d.getDay()) != -1){
20620                     cell.title = ddaysText;
20621                     cell.className = " fc-state-disabled";
20622                 }
20623             }
20624             if(ddMatch && format){
20625                 var fvalue = d.dateFormat(format);
20626                 if(ddMatch.test(fvalue)){
20627                     cell.title = ddText.replace("%0", fvalue);
20628                     cell.className = " fc-state-disabled";
20629                 }
20630             }
20631             
20632             if (!cell.initialClassName) {
20633                 cell.initialClassName = cell.dom.className;
20634             }
20635             
20636             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20637         };
20638
20639         var i = 0;
20640         
20641         for(; i < startingPos; i++) {
20642             textEls[i].innerHTML = (++prevStart);
20643             d.setDate(d.getDate()+1);
20644             
20645             cells[i].className = "fc-past fc-other-month";
20646             setCellClass(this, cells[i]);
20647         }
20648         
20649         var intDay = 0;
20650         
20651         for(; i < days; i++){
20652             intDay = i - startingPos + 1;
20653             textEls[i].innerHTML = (intDay);
20654             d.setDate(d.getDate()+1);
20655             
20656             cells[i].className = ''; // "x-date-active";
20657             setCellClass(this, cells[i]);
20658         }
20659         var extraDays = 0;
20660         
20661         for(; i < 42; i++) {
20662             textEls[i].innerHTML = (++extraDays);
20663             d.setDate(d.getDate()+1);
20664             
20665             cells[i].className = "fc-future fc-other-month";
20666             setCellClass(this, cells[i]);
20667         }
20668         
20669         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20670         
20671         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20672         
20673         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20674         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20675         
20676         if(totalRows != 6){
20677             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20678             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20679         }
20680         
20681         this.fireEvent('monthchange', this, date);
20682         
20683         
20684         /*
20685         if(!this.internalRender){
20686             var main = this.el.dom.firstChild;
20687             var w = main.offsetWidth;
20688             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20689             Roo.fly(main).setWidth(w);
20690             this.internalRender = true;
20691             // opera does not respect the auto grow header center column
20692             // then, after it gets a width opera refuses to recalculate
20693             // without a second pass
20694             if(Roo.isOpera && !this.secondPass){
20695                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20696                 this.secondPass = true;
20697                 this.update.defer(10, this, [date]);
20698             }
20699         }
20700         */
20701         
20702     },
20703     
20704     findCell : function(dt) {
20705         dt = dt.clearTime().getTime();
20706         var ret = false;
20707         this.cells.each(function(c){
20708             //Roo.log("check " +c.dateValue + '?=' + dt);
20709             if(c.dateValue == dt){
20710                 ret = c;
20711                 return false;
20712             }
20713             return true;
20714         });
20715         
20716         return ret;
20717     },
20718     
20719     findCells : function(ev) {
20720         var s = ev.start.clone().clearTime().getTime();
20721        // Roo.log(s);
20722         var e= ev.end.clone().clearTime().getTime();
20723        // Roo.log(e);
20724         var ret = [];
20725         this.cells.each(function(c){
20726              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20727             
20728             if(c.dateValue > e){
20729                 return ;
20730             }
20731             if(c.dateValue < s){
20732                 return ;
20733             }
20734             ret.push(c);
20735         });
20736         
20737         return ret;    
20738     },
20739     
20740 //    findBestRow: function(cells)
20741 //    {
20742 //        var ret = 0;
20743 //        
20744 //        for (var i =0 ; i < cells.length;i++) {
20745 //            ret  = Math.max(cells[i].rows || 0,ret);
20746 //        }
20747 //        return ret;
20748 //        
20749 //    },
20750     
20751     
20752     addItem : function(ev)
20753     {
20754         // look for vertical location slot in
20755         var cells = this.findCells(ev);
20756         
20757 //        ev.row = this.findBestRow(cells);
20758         
20759         // work out the location.
20760         
20761         var crow = false;
20762         var rows = [];
20763         for(var i =0; i < cells.length; i++) {
20764             
20765             cells[i].row = cells[0].row;
20766             
20767             if(i == 0){
20768                 cells[i].row = cells[i].row + 1;
20769             }
20770             
20771             if (!crow) {
20772                 crow = {
20773                     start : cells[i],
20774                     end :  cells[i]
20775                 };
20776                 continue;
20777             }
20778             if (crow.start.getY() == cells[i].getY()) {
20779                 // on same row.
20780                 crow.end = cells[i];
20781                 continue;
20782             }
20783             // different row.
20784             rows.push(crow);
20785             crow = {
20786                 start: cells[i],
20787                 end : cells[i]
20788             };
20789             
20790         }
20791         
20792         rows.push(crow);
20793         ev.els = [];
20794         ev.rows = rows;
20795         ev.cells = cells;
20796         
20797         cells[0].events.push(ev);
20798         
20799         this.calevents.push(ev);
20800     },
20801     
20802     clearEvents: function() {
20803         
20804         if(!this.calevents){
20805             return;
20806         }
20807         
20808         Roo.each(this.cells.elements, function(c){
20809             c.row = 0;
20810             c.events = [];
20811             c.more = [];
20812         });
20813         
20814         Roo.each(this.calevents, function(e) {
20815             Roo.each(e.els, function(el) {
20816                 el.un('mouseenter' ,this.onEventEnter, this);
20817                 el.un('mouseleave' ,this.onEventLeave, this);
20818                 el.remove();
20819             },this);
20820         },this);
20821         
20822         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20823             e.remove();
20824         });
20825         
20826     },
20827     
20828     renderEvents: function()
20829     {   
20830         var _this = this;
20831         
20832         this.cells.each(function(c) {
20833             
20834             if(c.row < 5){
20835                 return;
20836             }
20837             
20838             var ev = c.events;
20839             
20840             var r = 4;
20841             if(c.row != c.events.length){
20842                 r = 4 - (4 - (c.row - c.events.length));
20843             }
20844             
20845             c.events = ev.slice(0, r);
20846             c.more = ev.slice(r);
20847             
20848             if(c.more.length && c.more.length == 1){
20849                 c.events.push(c.more.pop());
20850             }
20851             
20852             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20853             
20854         });
20855             
20856         this.cells.each(function(c) {
20857             
20858             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20859             
20860             
20861             for (var e = 0; e < c.events.length; e++){
20862                 var ev = c.events[e];
20863                 var rows = ev.rows;
20864                 
20865                 for(var i = 0; i < rows.length; i++) {
20866                 
20867                     // how many rows should it span..
20868
20869                     var  cfg = {
20870                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20871                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20872
20873                         unselectable : "on",
20874                         cn : [
20875                             {
20876                                 cls: 'fc-event-inner',
20877                                 cn : [
20878     //                                {
20879     //                                  tag:'span',
20880     //                                  cls: 'fc-event-time',
20881     //                                  html : cells.length > 1 ? '' : ev.time
20882     //                                },
20883                                     {
20884                                       tag:'span',
20885                                       cls: 'fc-event-title',
20886                                       html : String.format('{0}', ev.title)
20887                                     }
20888
20889
20890                                 ]
20891                             },
20892                             {
20893                                 cls: 'ui-resizable-handle ui-resizable-e',
20894                                 html : '&nbsp;&nbsp;&nbsp'
20895                             }
20896
20897                         ]
20898                     };
20899
20900                     if (i == 0) {
20901                         cfg.cls += ' fc-event-start';
20902                     }
20903                     if ((i+1) == rows.length) {
20904                         cfg.cls += ' fc-event-end';
20905                     }
20906
20907                     var ctr = _this.el.select('.fc-event-container',true).first();
20908                     var cg = ctr.createChild(cfg);
20909
20910                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20911                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20912
20913                     var r = (c.more.length) ? 1 : 0;
20914                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20915                     cg.setWidth(ebox.right - sbox.x -2);
20916
20917                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20918                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20919                     cg.on('click', _this.onEventClick, _this, ev);
20920
20921                     ev.els.push(cg);
20922                     
20923                 }
20924                 
20925             }
20926             
20927             
20928             if(c.more.length){
20929                 var  cfg = {
20930                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20931                     style : 'position: absolute',
20932                     unselectable : "on",
20933                     cn : [
20934                         {
20935                             cls: 'fc-event-inner',
20936                             cn : [
20937                                 {
20938                                   tag:'span',
20939                                   cls: 'fc-event-title',
20940                                   html : 'More'
20941                                 }
20942
20943
20944                             ]
20945                         },
20946                         {
20947                             cls: 'ui-resizable-handle ui-resizable-e',
20948                             html : '&nbsp;&nbsp;&nbsp'
20949                         }
20950
20951                     ]
20952                 };
20953
20954                 var ctr = _this.el.select('.fc-event-container',true).first();
20955                 var cg = ctr.createChild(cfg);
20956
20957                 var sbox = c.select('.fc-day-content',true).first().getBox();
20958                 var ebox = c.select('.fc-day-content',true).first().getBox();
20959                 //Roo.log(cg);
20960                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20961                 cg.setWidth(ebox.right - sbox.x -2);
20962
20963                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20964                 
20965             }
20966             
20967         });
20968         
20969         
20970         
20971     },
20972     
20973     onEventEnter: function (e, el,event,d) {
20974         this.fireEvent('evententer', this, el, event);
20975     },
20976     
20977     onEventLeave: function (e, el,event,d) {
20978         this.fireEvent('eventleave', this, el, event);
20979     },
20980     
20981     onEventClick: function (e, el,event,d) {
20982         this.fireEvent('eventclick', this, el, event);
20983     },
20984     
20985     onMonthChange: function () {
20986         this.store.load();
20987     },
20988     
20989     onMoreEventClick: function(e, el, more)
20990     {
20991         var _this = this;
20992         
20993         this.calpopover.placement = 'right';
20994         this.calpopover.setTitle('More');
20995         
20996         this.calpopover.setContent('');
20997         
20998         var ctr = this.calpopover.el.select('.popover-content', true).first();
20999         
21000         Roo.each(more, function(m){
21001             var cfg = {
21002                 cls : 'fc-event-hori fc-event-draggable',
21003                 html : m.title
21004             };
21005             var cg = ctr.createChild(cfg);
21006             
21007             cg.on('click', _this.onEventClick, _this, m);
21008         });
21009         
21010         this.calpopover.show(el);
21011         
21012         
21013     },
21014     
21015     onLoad: function () 
21016     {   
21017         this.calevents = [];
21018         var cal = this;
21019         
21020         if(this.store.getCount() > 0){
21021             this.store.data.each(function(d){
21022                cal.addItem({
21023                     id : d.data.id,
21024                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21025                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21026                     time : d.data.start_time,
21027                     title : d.data.title,
21028                     description : d.data.description,
21029                     venue : d.data.venue
21030                 });
21031             });
21032         }
21033         
21034         this.renderEvents();
21035         
21036         if(this.calevents.length && this.loadMask){
21037             this.maskEl.hide();
21038         }
21039     },
21040     
21041     onBeforeLoad: function()
21042     {
21043         this.clearEvents();
21044         if(this.loadMask){
21045             this.maskEl.show();
21046         }
21047     }
21048 });
21049
21050  
21051  /*
21052  * - LGPL
21053  *
21054  * element
21055  * 
21056  */
21057
21058 /**
21059  * @class Roo.bootstrap.Popover
21060  * @extends Roo.bootstrap.Component
21061  * Bootstrap Popover class
21062  * @cfg {String} html contents of the popover   (or false to use children..)
21063  * @cfg {String} title of popover (or false to hide)
21064  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21065  * @cfg {String} trigger click || hover (or false to trigger manually)
21066  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21067  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21068  *      - if false and it has a 'parent' then it will be automatically added to that element
21069  *      - if string - Roo.get  will be called 
21070  * @cfg {Number} delay - delay before showing
21071  
21072  * @constructor
21073  * Create a new Popover
21074  * @param {Object} config The config object
21075  */
21076
21077 Roo.bootstrap.Popover = function(config){
21078     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21079     
21080     this.addEvents({
21081         // raw events
21082          /**
21083          * @event show
21084          * After the popover show
21085          * 
21086          * @param {Roo.bootstrap.Popover} this
21087          */
21088         "show" : true,
21089         /**
21090          * @event hide
21091          * After the popover hide
21092          * 
21093          * @param {Roo.bootstrap.Popover} this
21094          */
21095         "hide" : true
21096     });
21097 };
21098
21099 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21100     
21101     title: false,
21102     html: false,
21103     
21104     placement : 'right',
21105     trigger : 'hover', // hover
21106     modal : false,
21107     delay : 0,
21108     
21109     over: false,
21110     
21111     can_build_overlaid : false,
21112     
21113     maskEl : false, // the mask element
21114     headerEl : false,
21115     contentEl : false,
21116     alignEl : false, // when show is called with an element - this get's stored.
21117     
21118     getChildContainer : function()
21119     {
21120         return this.contentEl;
21121         
21122     },
21123     getPopoverHeader : function()
21124     {
21125         this.title = true; // flag not to hide it..
21126         this.headerEl.addClass('p-0');
21127         return this.headerEl
21128     },
21129     
21130     
21131     getAutoCreate : function(){
21132          
21133         var cfg = {
21134            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21135            style: 'display:block',
21136            cn : [
21137                 {
21138                     cls : 'arrow'
21139                 },
21140                 {
21141                     cls : 'popover-inner ',
21142                     cn : [
21143                         {
21144                             tag: 'h3',
21145                             cls: 'popover-title popover-header',
21146                             html : this.title === false ? '' : this.title
21147                         },
21148                         {
21149                             cls : 'popover-content popover-body '  + (this.cls || ''),
21150                             html : this.html || ''
21151                         }
21152                     ]
21153                     
21154                 }
21155            ]
21156         };
21157         
21158         return cfg;
21159     },
21160     /**
21161      * @param {string} the title
21162      */
21163     setTitle: function(str)
21164     {
21165         this.title = str;
21166         if (this.el) {
21167             this.headerEl.dom.innerHTML = str;
21168         }
21169         
21170     },
21171     /**
21172      * @param {string} the body content
21173      */
21174     setContent: function(str)
21175     {
21176         this.html = str;
21177         if (this.contentEl) {
21178             this.contentEl.dom.innerHTML = str;
21179         }
21180         
21181     },
21182     // as it get's added to the bottom of the page.
21183     onRender : function(ct, position)
21184     {
21185         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21186         
21187         
21188         
21189         if(!this.el){
21190             var cfg = Roo.apply({},  this.getAutoCreate());
21191             cfg.id = Roo.id();
21192             
21193             if (this.cls) {
21194                 cfg.cls += ' ' + this.cls;
21195             }
21196             if (this.style) {
21197                 cfg.style = this.style;
21198             }
21199             //Roo.log("adding to ");
21200             this.el = Roo.get(document.body).createChild(cfg, position);
21201 //            Roo.log(this.el);
21202         }
21203         
21204         this.contentEl = this.el.select('.popover-content',true).first();
21205         this.headerEl =  this.el.select('.popover-title',true).first();
21206         
21207         var nitems = [];
21208         if(typeof(this.items) != 'undefined'){
21209             var items = this.items;
21210             delete this.items;
21211
21212             for(var i =0;i < items.length;i++) {
21213                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21214             }
21215         }
21216
21217         this.items = nitems;
21218         
21219         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21220         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21221         
21222         
21223         
21224         this.initEvents();
21225     },
21226     
21227     resizeMask : function()
21228     {
21229         this.maskEl.setSize(
21230             Roo.lib.Dom.getViewWidth(true),
21231             Roo.lib.Dom.getViewHeight(true)
21232         );
21233     },
21234     
21235     initEvents : function()
21236     {
21237         
21238         if (!this.modal) { 
21239             Roo.bootstrap.Popover.register(this);
21240         }
21241          
21242         this.arrowEl = this.el.select('.arrow',true).first();
21243         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21244         this.el.enableDisplayMode('block');
21245         this.el.hide();
21246  
21247         
21248         if (this.over === false && !this.parent()) {
21249             return; 
21250         }
21251         if (this.triggers === false) {
21252             return;
21253         }
21254          
21255         // support parent
21256         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21257         var triggers = this.trigger ? this.trigger.split(' ') : [];
21258         Roo.each(triggers, function(trigger) {
21259         
21260             if (trigger == 'click') {
21261                 on_el.on('click', this.toggle, this);
21262             } else if (trigger != 'manual') {
21263                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21264                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21265       
21266                 on_el.on(eventIn  ,this.enter, this);
21267                 on_el.on(eventOut, this.leave, this);
21268             }
21269         }, this);
21270     },
21271     
21272     
21273     // private
21274     timeout : null,
21275     hoverState : null,
21276     
21277     toggle : function () {
21278         this.hoverState == 'in' ? this.leave() : this.enter();
21279     },
21280     
21281     enter : function () {
21282         
21283         clearTimeout(this.timeout);
21284     
21285         this.hoverState = 'in';
21286     
21287         if (!this.delay || !this.delay.show) {
21288             this.show();
21289             return;
21290         }
21291         var _t = this;
21292         this.timeout = setTimeout(function () {
21293             if (_t.hoverState == 'in') {
21294                 _t.show();
21295             }
21296         }, this.delay.show)
21297     },
21298     
21299     leave : function() {
21300         clearTimeout(this.timeout);
21301     
21302         this.hoverState = 'out';
21303     
21304         if (!this.delay || !this.delay.hide) {
21305             this.hide();
21306             return;
21307         }
21308         var _t = this;
21309         this.timeout = setTimeout(function () {
21310             if (_t.hoverState == 'out') {
21311                 _t.hide();
21312             }
21313         }, this.delay.hide)
21314     },
21315     /**
21316      * Show the popover
21317      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21318      * @param {string} (left|right|top|bottom) position
21319      */
21320     show : function (on_el, placement)
21321     {
21322         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21323         on_el = on_el || false; // default to false
21324          
21325         if (!on_el) {
21326             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21327                 on_el = this.parent().el;
21328             } else if (this.over) {
21329                 on_el = Roo.get(this.over);
21330             }
21331             
21332         }
21333         
21334         this.alignEl = Roo.get( on_el );
21335
21336         if (!this.el) {
21337             this.render(document.body);
21338         }
21339         
21340         
21341          
21342         
21343         if (this.title === false) {
21344             this.headerEl.hide();
21345         }
21346         
21347        
21348         this.el.show();
21349         this.el.dom.style.display = 'block';
21350          
21351  
21352         if (this.alignEl) {
21353             this.updatePosition(this.placement, true);
21354              
21355         } else {
21356             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21357             var es = this.el.getSize();
21358             var x = Roo.lib.Dom.getViewWidth()/2;
21359             var y = Roo.lib.Dom.getViewHeight()/2;
21360             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21361             
21362         }
21363
21364         
21365         //var arrow = this.el.select('.arrow',true).first();
21366         //arrow.set(align[2], 
21367         
21368         this.el.addClass('in');
21369         
21370          
21371         
21372         this.hoverState = 'in';
21373         
21374         if (this.modal) {
21375             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21376             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21377             this.maskEl.dom.style.display = 'block';
21378             this.maskEl.addClass('show');
21379         }
21380         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21381  
21382         this.fireEvent('show', this);
21383         
21384     },
21385     /**
21386      * fire this manually after loading a grid in the table for example
21387      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21388      * @param {Boolean} try and move it if we cant get right position.
21389      */
21390     updatePosition : function(placement, try_move)
21391     {
21392         // allow for calling with no parameters
21393         placement = placement   ? placement :  this.placement;
21394         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21395         
21396         this.el.removeClass([
21397             'fade','top','bottom', 'left', 'right','in',
21398             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21399         ]);
21400         this.el.addClass(placement + ' bs-popover-' + placement);
21401         
21402         if (!this.alignEl ) {
21403             return false;
21404         }
21405         
21406         switch (placement) {
21407             case 'right':
21408                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21409                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21410                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21411                     //normal display... or moved up/down.
21412                     this.el.setXY(offset);
21413                     var xy = this.alignEl.getAnchorXY('tr', false);
21414                     xy[0]+=2;xy[1]+=5;
21415                     this.arrowEl.setXY(xy);
21416                     return true;
21417                 }
21418                 // continue through...
21419                 return this.updatePosition('left', false);
21420                 
21421             
21422             case 'left':
21423                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21424                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21425                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21426                     //normal display... or moved up/down.
21427                     this.el.setXY(offset);
21428                     var xy = this.alignEl.getAnchorXY('tl', false);
21429                     xy[0]-=10;xy[1]+=5; // << fix me
21430                     this.arrowEl.setXY(xy);
21431                     return true;
21432                 }
21433                 // call self...
21434                 return this.updatePosition('right', false);
21435             
21436             case 'top':
21437                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21438                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21439                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21440                     //normal display... or moved up/down.
21441                     this.el.setXY(offset);
21442                     var xy = this.alignEl.getAnchorXY('t', false);
21443                     xy[1]-=10; // << fix me
21444                     this.arrowEl.setXY(xy);
21445                     return true;
21446                 }
21447                 // fall through
21448                return this.updatePosition('bottom', false);
21449             
21450             case 'bottom':
21451                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21452                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21453                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21454                     //normal display... or moved up/down.
21455                     this.el.setXY(offset);
21456                     var xy = this.alignEl.getAnchorXY('b', false);
21457                      xy[1]+=2; // << fix me
21458                     this.arrowEl.setXY(xy);
21459                     return true;
21460                 }
21461                 // fall through
21462                 return this.updatePosition('top', false);
21463                 
21464             
21465         }
21466         
21467         
21468         return false;
21469     },
21470     
21471     hide : function()
21472     {
21473         this.el.setXY([0,0]);
21474         this.el.removeClass('in');
21475         this.el.hide();
21476         this.hoverState = null;
21477         this.maskEl.hide(); // always..
21478         this.fireEvent('hide', this);
21479     }
21480     
21481 });
21482
21483
21484 Roo.apply(Roo.bootstrap.Popover, {
21485
21486     alignment : {
21487         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21488         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21489         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21490         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21491     },
21492     
21493     zIndex : 20001,
21494
21495     clickHander : false,
21496     
21497     
21498
21499     onMouseDown : function(e)
21500     {
21501         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21502             /// what is nothing is showing..
21503             this.hideAll();
21504         }
21505          
21506     },
21507     
21508     
21509     popups : [],
21510     
21511     register : function(popup)
21512     {
21513         if (!Roo.bootstrap.Popover.clickHandler) {
21514             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21515         }
21516         // hide other popups.
21517         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21518         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21519         this.hideAll(); //<< why?
21520         //this.popups.push(popup);
21521     },
21522     hideAll : function()
21523     {
21524         this.popups.forEach(function(p) {
21525             p.hide();
21526         });
21527     },
21528     onShow : function() {
21529         Roo.bootstrap.Popover.popups.push(this);
21530     },
21531     onHide : function() {
21532         Roo.bootstrap.Popover.popups.remove(this);
21533     } 
21534
21535 });/*
21536  * - LGPL
21537  *
21538  * Card header - holder for the card header elements.
21539  * 
21540  */
21541
21542 /**
21543  * @class Roo.bootstrap.PopoverNav
21544  * @extends Roo.bootstrap.NavGroup
21545  * Bootstrap Popover header navigation class
21546  * @constructor
21547  * Create a new Popover Header Navigation 
21548  * @param {Object} config The config object
21549  */
21550
21551 Roo.bootstrap.PopoverNav = function(config){
21552     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21553 };
21554
21555 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21556     
21557     
21558     container_method : 'getPopoverHeader' 
21559     
21560      
21561     
21562     
21563    
21564 });
21565
21566  
21567
21568  /*
21569  * - LGPL
21570  *
21571  * Progress
21572  * 
21573  */
21574
21575 /**
21576  * @class Roo.bootstrap.Progress
21577  * @extends Roo.bootstrap.Component
21578  * Bootstrap Progress class
21579  * @cfg {Boolean} striped striped of the progress bar
21580  * @cfg {Boolean} active animated of the progress bar
21581  * 
21582  * 
21583  * @constructor
21584  * Create a new Progress
21585  * @param {Object} config The config object
21586  */
21587
21588 Roo.bootstrap.Progress = function(config){
21589     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21590 };
21591
21592 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21593     
21594     striped : false,
21595     active: false,
21596     
21597     getAutoCreate : function(){
21598         var cfg = {
21599             tag: 'div',
21600             cls: 'progress'
21601         };
21602         
21603         
21604         if(this.striped){
21605             cfg.cls += ' progress-striped';
21606         }
21607       
21608         if(this.active){
21609             cfg.cls += ' active';
21610         }
21611         
21612         
21613         return cfg;
21614     }
21615    
21616 });
21617
21618  
21619
21620  /*
21621  * - LGPL
21622  *
21623  * ProgressBar
21624  * 
21625  */
21626
21627 /**
21628  * @class Roo.bootstrap.ProgressBar
21629  * @extends Roo.bootstrap.Component
21630  * Bootstrap ProgressBar class
21631  * @cfg {Number} aria_valuenow aria-value now
21632  * @cfg {Number} aria_valuemin aria-value min
21633  * @cfg {Number} aria_valuemax aria-value max
21634  * @cfg {String} label label for the progress bar
21635  * @cfg {String} panel (success | info | warning | danger )
21636  * @cfg {String} role role of the progress bar
21637  * @cfg {String} sr_only text
21638  * 
21639  * 
21640  * @constructor
21641  * Create a new ProgressBar
21642  * @param {Object} config The config object
21643  */
21644
21645 Roo.bootstrap.ProgressBar = function(config){
21646     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21647 };
21648
21649 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21650     
21651     aria_valuenow : 0,
21652     aria_valuemin : 0,
21653     aria_valuemax : 100,
21654     label : false,
21655     panel : false,
21656     role : false,
21657     sr_only: false,
21658     
21659     getAutoCreate : function()
21660     {
21661         
21662         var cfg = {
21663             tag: 'div',
21664             cls: 'progress-bar',
21665             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21666         };
21667         
21668         if(this.sr_only){
21669             cfg.cn = {
21670                 tag: 'span',
21671                 cls: 'sr-only',
21672                 html: this.sr_only
21673             }
21674         }
21675         
21676         if(this.role){
21677             cfg.role = this.role;
21678         }
21679         
21680         if(this.aria_valuenow){
21681             cfg['aria-valuenow'] = this.aria_valuenow;
21682         }
21683         
21684         if(this.aria_valuemin){
21685             cfg['aria-valuemin'] = this.aria_valuemin;
21686         }
21687         
21688         if(this.aria_valuemax){
21689             cfg['aria-valuemax'] = this.aria_valuemax;
21690         }
21691         
21692         if(this.label && !this.sr_only){
21693             cfg.html = this.label;
21694         }
21695         
21696         if(this.panel){
21697             cfg.cls += ' progress-bar-' + this.panel;
21698         }
21699         
21700         return cfg;
21701     },
21702     
21703     update : function(aria_valuenow)
21704     {
21705         this.aria_valuenow = aria_valuenow;
21706         
21707         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21708     }
21709    
21710 });
21711
21712  
21713
21714  /*
21715  * - LGPL
21716  *
21717  * column
21718  * 
21719  */
21720
21721 /**
21722  * @class Roo.bootstrap.TabGroup
21723  * @extends Roo.bootstrap.Column
21724  * Bootstrap Column class
21725  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21726  * @cfg {Boolean} carousel true to make the group behave like a carousel
21727  * @cfg {Boolean} bullets show bullets for the panels
21728  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21729  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21730  * @cfg {Boolean} showarrow (true|false) show arrow default true
21731  * 
21732  * @constructor
21733  * Create a new TabGroup
21734  * @param {Object} config The config object
21735  */
21736
21737 Roo.bootstrap.TabGroup = function(config){
21738     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21739     if (!this.navId) {
21740         this.navId = Roo.id();
21741     }
21742     this.tabs = [];
21743     Roo.bootstrap.TabGroup.register(this);
21744     
21745 };
21746
21747 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21748     
21749     carousel : false,
21750     transition : false,
21751     bullets : 0,
21752     timer : 0,
21753     autoslide : false,
21754     slideFn : false,
21755     slideOnTouch : false,
21756     showarrow : true,
21757     
21758     getAutoCreate : function()
21759     {
21760         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21761         
21762         cfg.cls += ' tab-content';
21763         
21764         if (this.carousel) {
21765             cfg.cls += ' carousel slide';
21766             
21767             cfg.cn = [{
21768                cls : 'carousel-inner',
21769                cn : []
21770             }];
21771         
21772             if(this.bullets  && !Roo.isTouch){
21773                 
21774                 var bullets = {
21775                     cls : 'carousel-bullets',
21776                     cn : []
21777                 };
21778                
21779                 if(this.bullets_cls){
21780                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21781                 }
21782                 
21783                 bullets.cn.push({
21784                     cls : 'clear'
21785                 });
21786                 
21787                 cfg.cn[0].cn.push(bullets);
21788             }
21789             
21790             if(this.showarrow){
21791                 cfg.cn[0].cn.push({
21792                     tag : 'div',
21793                     class : 'carousel-arrow',
21794                     cn : [
21795                         {
21796                             tag : 'div',
21797                             class : 'carousel-prev',
21798                             cn : [
21799                                 {
21800                                     tag : 'i',
21801                                     class : 'fa fa-chevron-left'
21802                                 }
21803                             ]
21804                         },
21805                         {
21806                             tag : 'div',
21807                             class : 'carousel-next',
21808                             cn : [
21809                                 {
21810                                     tag : 'i',
21811                                     class : 'fa fa-chevron-right'
21812                                 }
21813                             ]
21814                         }
21815                     ]
21816                 });
21817             }
21818             
21819         }
21820         
21821         return cfg;
21822     },
21823     
21824     initEvents:  function()
21825     {
21826 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21827 //            this.el.on("touchstart", this.onTouchStart, this);
21828 //        }
21829         
21830         if(this.autoslide){
21831             var _this = this;
21832             
21833             this.slideFn = window.setInterval(function() {
21834                 _this.showPanelNext();
21835             }, this.timer);
21836         }
21837         
21838         if(this.showarrow){
21839             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21840             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21841         }
21842         
21843         
21844     },
21845     
21846 //    onTouchStart : function(e, el, o)
21847 //    {
21848 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21849 //            return;
21850 //        }
21851 //        
21852 //        this.showPanelNext();
21853 //    },
21854     
21855     
21856     getChildContainer : function()
21857     {
21858         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21859     },
21860     
21861     /**
21862     * register a Navigation item
21863     * @param {Roo.bootstrap.NavItem} the navitem to add
21864     */
21865     register : function(item)
21866     {
21867         this.tabs.push( item);
21868         item.navId = this.navId; // not really needed..
21869         this.addBullet();
21870     
21871     },
21872     
21873     getActivePanel : function()
21874     {
21875         var r = false;
21876         Roo.each(this.tabs, function(t) {
21877             if (t.active) {
21878                 r = t;
21879                 return false;
21880             }
21881             return null;
21882         });
21883         return r;
21884         
21885     },
21886     getPanelByName : function(n)
21887     {
21888         var r = false;
21889         Roo.each(this.tabs, function(t) {
21890             if (t.tabId == n) {
21891                 r = t;
21892                 return false;
21893             }
21894             return null;
21895         });
21896         return r;
21897     },
21898     indexOfPanel : function(p)
21899     {
21900         var r = false;
21901         Roo.each(this.tabs, function(t,i) {
21902             if (t.tabId == p.tabId) {
21903                 r = i;
21904                 return false;
21905             }
21906             return null;
21907         });
21908         return r;
21909     },
21910     /**
21911      * show a specific panel
21912      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21913      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21914      */
21915     showPanel : function (pan)
21916     {
21917         if(this.transition || typeof(pan) == 'undefined'){
21918             Roo.log("waiting for the transitionend");
21919             return false;
21920         }
21921         
21922         if (typeof(pan) == 'number') {
21923             pan = this.tabs[pan];
21924         }
21925         
21926         if (typeof(pan) == 'string') {
21927             pan = this.getPanelByName(pan);
21928         }
21929         
21930         var cur = this.getActivePanel();
21931         
21932         if(!pan || !cur){
21933             Roo.log('pan or acitve pan is undefined');
21934             return false;
21935         }
21936         
21937         if (pan.tabId == this.getActivePanel().tabId) {
21938             return true;
21939         }
21940         
21941         if (false === cur.fireEvent('beforedeactivate')) {
21942             return false;
21943         }
21944         
21945         if(this.bullets > 0 && !Roo.isTouch){
21946             this.setActiveBullet(this.indexOfPanel(pan));
21947         }
21948         
21949         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21950             
21951             //class="carousel-item carousel-item-next carousel-item-left"
21952             
21953             this.transition = true;
21954             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21955             var lr = dir == 'next' ? 'left' : 'right';
21956             pan.el.addClass(dir); // or prev
21957             pan.el.addClass('carousel-item-' + dir); // or prev
21958             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21959             cur.el.addClass(lr); // or right
21960             pan.el.addClass(lr);
21961             cur.el.addClass('carousel-item-' +lr); // or right
21962             pan.el.addClass('carousel-item-' +lr);
21963             
21964             
21965             var _this = this;
21966             cur.el.on('transitionend', function() {
21967                 Roo.log("trans end?");
21968                 
21969                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21970                 pan.setActive(true);
21971                 
21972                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21973                 cur.setActive(false);
21974                 
21975                 _this.transition = false;
21976                 
21977             }, this, { single:  true } );
21978             
21979             return true;
21980         }
21981         
21982         cur.setActive(false);
21983         pan.setActive(true);
21984         
21985         return true;
21986         
21987     },
21988     showPanelNext : function()
21989     {
21990         var i = this.indexOfPanel(this.getActivePanel());
21991         
21992         if (i >= this.tabs.length - 1 && !this.autoslide) {
21993             return;
21994         }
21995         
21996         if (i >= this.tabs.length - 1 && this.autoslide) {
21997             i = -1;
21998         }
21999         
22000         this.showPanel(this.tabs[i+1]);
22001     },
22002     
22003     showPanelPrev : function()
22004     {
22005         var i = this.indexOfPanel(this.getActivePanel());
22006         
22007         if (i  < 1 && !this.autoslide) {
22008             return;
22009         }
22010         
22011         if (i < 1 && this.autoslide) {
22012             i = this.tabs.length;
22013         }
22014         
22015         this.showPanel(this.tabs[i-1]);
22016     },
22017     
22018     
22019     addBullet: function()
22020     {
22021         if(!this.bullets || Roo.isTouch){
22022             return;
22023         }
22024         var ctr = this.el.select('.carousel-bullets',true).first();
22025         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22026         var bullet = ctr.createChild({
22027             cls : 'bullet bullet-' + i
22028         },ctr.dom.lastChild);
22029         
22030         
22031         var _this = this;
22032         
22033         bullet.on('click', (function(e, el, o, ii, t){
22034
22035             e.preventDefault();
22036
22037             this.showPanel(ii);
22038
22039             if(this.autoslide && this.slideFn){
22040                 clearInterval(this.slideFn);
22041                 this.slideFn = window.setInterval(function() {
22042                     _this.showPanelNext();
22043                 }, this.timer);
22044             }
22045
22046         }).createDelegate(this, [i, bullet], true));
22047                 
22048         
22049     },
22050      
22051     setActiveBullet : function(i)
22052     {
22053         if(Roo.isTouch){
22054             return;
22055         }
22056         
22057         Roo.each(this.el.select('.bullet', true).elements, function(el){
22058             el.removeClass('selected');
22059         });
22060
22061         var bullet = this.el.select('.bullet-' + i, true).first();
22062         
22063         if(!bullet){
22064             return;
22065         }
22066         
22067         bullet.addClass('selected');
22068     }
22069     
22070     
22071   
22072 });
22073
22074  
22075
22076  
22077  
22078 Roo.apply(Roo.bootstrap.TabGroup, {
22079     
22080     groups: {},
22081      /**
22082     * register a Navigation Group
22083     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22084     */
22085     register : function(navgrp)
22086     {
22087         this.groups[navgrp.navId] = navgrp;
22088         
22089     },
22090     /**
22091     * fetch a Navigation Group based on the navigation ID
22092     * if one does not exist , it will get created.
22093     * @param {string} the navgroup to add
22094     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22095     */
22096     get: function(navId) {
22097         if (typeof(this.groups[navId]) == 'undefined') {
22098             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22099         }
22100         return this.groups[navId] ;
22101     }
22102     
22103     
22104     
22105 });
22106
22107  /*
22108  * - LGPL
22109  *
22110  * TabPanel
22111  * 
22112  */
22113
22114 /**
22115  * @class Roo.bootstrap.TabPanel
22116  * @extends Roo.bootstrap.Component
22117  * Bootstrap TabPanel class
22118  * @cfg {Boolean} active panel active
22119  * @cfg {String} html panel content
22120  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22121  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22122  * @cfg {String} href click to link..
22123  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22124  * 
22125  * 
22126  * @constructor
22127  * Create a new TabPanel
22128  * @param {Object} config The config object
22129  */
22130
22131 Roo.bootstrap.TabPanel = function(config){
22132     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22133     this.addEvents({
22134         /**
22135              * @event changed
22136              * Fires when the active status changes
22137              * @param {Roo.bootstrap.TabPanel} this
22138              * @param {Boolean} state the new state
22139             
22140          */
22141         'changed': true,
22142         /**
22143              * @event beforedeactivate
22144              * Fires before a tab is de-activated - can be used to do validation on a form.
22145              * @param {Roo.bootstrap.TabPanel} this
22146              * @return {Boolean} false if there is an error
22147             
22148          */
22149         'beforedeactivate': true
22150      });
22151     
22152     this.tabId = this.tabId || Roo.id();
22153   
22154 };
22155
22156 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22157     
22158     active: false,
22159     html: false,
22160     tabId: false,
22161     navId : false,
22162     href : '',
22163     touchSlide : false,
22164     getAutoCreate : function(){
22165         
22166         
22167         var cfg = {
22168             tag: 'div',
22169             // item is needed for carousel - not sure if it has any effect otherwise
22170             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22171             html: this.html || ''
22172         };
22173         
22174         if(this.active){
22175             cfg.cls += ' active';
22176         }
22177         
22178         if(this.tabId){
22179             cfg.tabId = this.tabId;
22180         }
22181         
22182         
22183         
22184         return cfg;
22185     },
22186     
22187     initEvents:  function()
22188     {
22189         var p = this.parent();
22190         
22191         this.navId = this.navId || p.navId;
22192         
22193         if (typeof(this.navId) != 'undefined') {
22194             // not really needed.. but just in case.. parent should be a NavGroup.
22195             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22196             
22197             tg.register(this);
22198             
22199             var i = tg.tabs.length - 1;
22200             
22201             if(this.active && tg.bullets > 0 && i < tg.bullets){
22202                 tg.setActiveBullet(i);
22203             }
22204         }
22205         
22206         this.el.on('click', this.onClick, this);
22207         
22208         if(Roo.isTouch && this.touchSlide){
22209             this.el.on("touchstart", this.onTouchStart, this);
22210             this.el.on("touchmove", this.onTouchMove, this);
22211             this.el.on("touchend", this.onTouchEnd, this);
22212         }
22213         
22214     },
22215     
22216     onRender : function(ct, position)
22217     {
22218         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22219     },
22220     
22221     setActive : function(state)
22222     {
22223         Roo.log("panel - set active " + this.tabId + "=" + state);
22224         
22225         this.active = state;
22226         if (!state) {
22227             this.el.removeClass('active');
22228             
22229         } else  if (!this.el.hasClass('active')) {
22230             this.el.addClass('active');
22231         }
22232         
22233         this.fireEvent('changed', this, state);
22234     },
22235     
22236     onClick : function(e)
22237     {
22238         e.preventDefault();
22239         
22240         if(!this.href.length){
22241             return;
22242         }
22243         
22244         window.location.href = this.href;
22245     },
22246     
22247     startX : 0,
22248     startY : 0,
22249     endX : 0,
22250     endY : 0,
22251     swiping : false,
22252     
22253     onTouchStart : function(e)
22254     {
22255         this.swiping = false;
22256         
22257         this.startX = e.browserEvent.touches[0].clientX;
22258         this.startY = e.browserEvent.touches[0].clientY;
22259     },
22260     
22261     onTouchMove : function(e)
22262     {
22263         this.swiping = true;
22264         
22265         this.endX = e.browserEvent.touches[0].clientX;
22266         this.endY = e.browserEvent.touches[0].clientY;
22267     },
22268     
22269     onTouchEnd : function(e)
22270     {
22271         if(!this.swiping){
22272             this.onClick(e);
22273             return;
22274         }
22275         
22276         var tabGroup = this.parent();
22277         
22278         if(this.endX > this.startX){ // swiping right
22279             tabGroup.showPanelPrev();
22280             return;
22281         }
22282         
22283         if(this.startX > this.endX){ // swiping left
22284             tabGroup.showPanelNext();
22285             return;
22286         }
22287     }
22288     
22289     
22290 });
22291  
22292
22293  
22294
22295  /*
22296  * - LGPL
22297  *
22298  * DateField
22299  * 
22300  */
22301
22302 /**
22303  * @class Roo.bootstrap.DateField
22304  * @extends Roo.bootstrap.Input
22305  * Bootstrap DateField class
22306  * @cfg {Number} weekStart default 0
22307  * @cfg {String} viewMode default empty, (months|years)
22308  * @cfg {String} minViewMode default empty, (months|years)
22309  * @cfg {Number} startDate default -Infinity
22310  * @cfg {Number} endDate default Infinity
22311  * @cfg {Boolean} todayHighlight default false
22312  * @cfg {Boolean} todayBtn default false
22313  * @cfg {Boolean} calendarWeeks default false
22314  * @cfg {Object} daysOfWeekDisabled default empty
22315  * @cfg {Boolean} singleMode default false (true | false)
22316  * 
22317  * @cfg {Boolean} keyboardNavigation default true
22318  * @cfg {String} language default en
22319  * 
22320  * @constructor
22321  * Create a new DateField
22322  * @param {Object} config The config object
22323  */
22324
22325 Roo.bootstrap.DateField = function(config){
22326     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22327      this.addEvents({
22328             /**
22329              * @event show
22330              * Fires when this field show.
22331              * @param {Roo.bootstrap.DateField} this
22332              * @param {Mixed} date The date value
22333              */
22334             show : true,
22335             /**
22336              * @event show
22337              * Fires when this field hide.
22338              * @param {Roo.bootstrap.DateField} this
22339              * @param {Mixed} date The date value
22340              */
22341             hide : true,
22342             /**
22343              * @event select
22344              * Fires when select a date.
22345              * @param {Roo.bootstrap.DateField} this
22346              * @param {Mixed} date The date value
22347              */
22348             select : true,
22349             /**
22350              * @event beforeselect
22351              * Fires when before select a date.
22352              * @param {Roo.bootstrap.DateField} this
22353              * @param {Mixed} date The date value
22354              */
22355             beforeselect : true
22356         });
22357 };
22358
22359 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22360     
22361     /**
22362      * @cfg {String} format
22363      * The default date format string which can be overriden for localization support.  The format must be
22364      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22365      */
22366     format : "m/d/y",
22367     /**
22368      * @cfg {String} altFormats
22369      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22370      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22371      */
22372     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22373     
22374     weekStart : 0,
22375     
22376     viewMode : '',
22377     
22378     minViewMode : '',
22379     
22380     todayHighlight : false,
22381     
22382     todayBtn: false,
22383     
22384     language: 'en',
22385     
22386     keyboardNavigation: true,
22387     
22388     calendarWeeks: false,
22389     
22390     startDate: -Infinity,
22391     
22392     endDate: Infinity,
22393     
22394     daysOfWeekDisabled: [],
22395     
22396     _events: [],
22397     
22398     singleMode : false,
22399     
22400     UTCDate: function()
22401     {
22402         return new Date(Date.UTC.apply(Date, arguments));
22403     },
22404     
22405     UTCToday: function()
22406     {
22407         var today = new Date();
22408         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22409     },
22410     
22411     getDate: function() {
22412             var d = this.getUTCDate();
22413             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22414     },
22415     
22416     getUTCDate: function() {
22417             return this.date;
22418     },
22419     
22420     setDate: function(d) {
22421             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22422     },
22423     
22424     setUTCDate: function(d) {
22425             this.date = d;
22426             this.setValue(this.formatDate(this.date));
22427     },
22428         
22429     onRender: function(ct, position)
22430     {
22431         
22432         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22433         
22434         this.language = this.language || 'en';
22435         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22436         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22437         
22438         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22439         this.format = this.format || 'm/d/y';
22440         this.isInline = false;
22441         this.isInput = true;
22442         this.component = this.el.select('.add-on', true).first() || false;
22443         this.component = (this.component && this.component.length === 0) ? false : this.component;
22444         this.hasInput = this.component && this.inputEl().length;
22445         
22446         if (typeof(this.minViewMode === 'string')) {
22447             switch (this.minViewMode) {
22448                 case 'months':
22449                     this.minViewMode = 1;
22450                     break;
22451                 case 'years':
22452                     this.minViewMode = 2;
22453                     break;
22454                 default:
22455                     this.minViewMode = 0;
22456                     break;
22457             }
22458         }
22459         
22460         if (typeof(this.viewMode === 'string')) {
22461             switch (this.viewMode) {
22462                 case 'months':
22463                     this.viewMode = 1;
22464                     break;
22465                 case 'years':
22466                     this.viewMode = 2;
22467                     break;
22468                 default:
22469                     this.viewMode = 0;
22470                     break;
22471             }
22472         }
22473                 
22474         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22475         
22476 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22477         
22478         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22479         
22480         this.picker().on('mousedown', this.onMousedown, this);
22481         this.picker().on('click', this.onClick, this);
22482         
22483         this.picker().addClass('datepicker-dropdown');
22484         
22485         this.startViewMode = this.viewMode;
22486         
22487         if(this.singleMode){
22488             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22489                 v.setVisibilityMode(Roo.Element.DISPLAY);
22490                 v.hide();
22491             });
22492             
22493             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22494                 v.setStyle('width', '189px');
22495             });
22496         }
22497         
22498         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22499             if(!this.calendarWeeks){
22500                 v.remove();
22501                 return;
22502             }
22503             
22504             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22505             v.attr('colspan', function(i, val){
22506                 return parseInt(val) + 1;
22507             });
22508         });
22509                         
22510         
22511         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22512         
22513         this.setStartDate(this.startDate);
22514         this.setEndDate(this.endDate);
22515         
22516         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22517         
22518         this.fillDow();
22519         this.fillMonths();
22520         this.update();
22521         this.showMode();
22522         
22523         if(this.isInline) {
22524             this.showPopup();
22525         }
22526     },
22527     
22528     picker : function()
22529     {
22530         return this.pickerEl;
22531 //        return this.el.select('.datepicker', true).first();
22532     },
22533     
22534     fillDow: function()
22535     {
22536         var dowCnt = this.weekStart;
22537         
22538         var dow = {
22539             tag: 'tr',
22540             cn: [
22541                 
22542             ]
22543         };
22544         
22545         if(this.calendarWeeks){
22546             dow.cn.push({
22547                 tag: 'th',
22548                 cls: 'cw',
22549                 html: '&nbsp;'
22550             })
22551         }
22552         
22553         while (dowCnt < this.weekStart + 7) {
22554             dow.cn.push({
22555                 tag: 'th',
22556                 cls: 'dow',
22557                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22558             });
22559         }
22560         
22561         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22562     },
22563     
22564     fillMonths: function()
22565     {    
22566         var i = 0;
22567         var months = this.picker().select('>.datepicker-months td', true).first();
22568         
22569         months.dom.innerHTML = '';
22570         
22571         while (i < 12) {
22572             var month = {
22573                 tag: 'span',
22574                 cls: 'month',
22575                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22576             };
22577             
22578             months.createChild(month);
22579         }
22580         
22581     },
22582     
22583     update: function()
22584     {
22585         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;
22586         
22587         if (this.date < this.startDate) {
22588             this.viewDate = new Date(this.startDate);
22589         } else if (this.date > this.endDate) {
22590             this.viewDate = new Date(this.endDate);
22591         } else {
22592             this.viewDate = new Date(this.date);
22593         }
22594         
22595         this.fill();
22596     },
22597     
22598     fill: function() 
22599     {
22600         var d = new Date(this.viewDate),
22601                 year = d.getUTCFullYear(),
22602                 month = d.getUTCMonth(),
22603                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22604                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22605                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22606                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22607                 currentDate = this.date && this.date.valueOf(),
22608                 today = this.UTCToday();
22609         
22610         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22611         
22612 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22613         
22614 //        this.picker.select('>tfoot th.today').
22615 //                                              .text(dates[this.language].today)
22616 //                                              .toggle(this.todayBtn !== false);
22617     
22618         this.updateNavArrows();
22619         this.fillMonths();
22620                                                 
22621         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22622         
22623         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22624          
22625         prevMonth.setUTCDate(day);
22626         
22627         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22628         
22629         var nextMonth = new Date(prevMonth);
22630         
22631         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22632         
22633         nextMonth = nextMonth.valueOf();
22634         
22635         var fillMonths = false;
22636         
22637         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22638         
22639         while(prevMonth.valueOf() <= nextMonth) {
22640             var clsName = '';
22641             
22642             if (prevMonth.getUTCDay() === this.weekStart) {
22643                 if(fillMonths){
22644                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22645                 }
22646                     
22647                 fillMonths = {
22648                     tag: 'tr',
22649                     cn: []
22650                 };
22651                 
22652                 if(this.calendarWeeks){
22653                     // ISO 8601: First week contains first thursday.
22654                     // ISO also states week starts on Monday, but we can be more abstract here.
22655                     var
22656                     // Start of current week: based on weekstart/current date
22657                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22658                     // Thursday of this week
22659                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22660                     // First Thursday of year, year from thursday
22661                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22662                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22663                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22664                     
22665                     fillMonths.cn.push({
22666                         tag: 'td',
22667                         cls: 'cw',
22668                         html: calWeek
22669                     });
22670                 }
22671             }
22672             
22673             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22674                 clsName += ' old';
22675             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22676                 clsName += ' new';
22677             }
22678             if (this.todayHighlight &&
22679                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22680                 prevMonth.getUTCMonth() == today.getMonth() &&
22681                 prevMonth.getUTCDate() == today.getDate()) {
22682                 clsName += ' today';
22683             }
22684             
22685             if (currentDate && prevMonth.valueOf() === currentDate) {
22686                 clsName += ' active';
22687             }
22688             
22689             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22690                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22691                     clsName += ' disabled';
22692             }
22693             
22694             fillMonths.cn.push({
22695                 tag: 'td',
22696                 cls: 'day ' + clsName,
22697                 html: prevMonth.getDate()
22698             });
22699             
22700             prevMonth.setDate(prevMonth.getDate()+1);
22701         }
22702           
22703         var currentYear = this.date && this.date.getUTCFullYear();
22704         var currentMonth = this.date && this.date.getUTCMonth();
22705         
22706         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22707         
22708         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22709             v.removeClass('active');
22710             
22711             if(currentYear === year && k === currentMonth){
22712                 v.addClass('active');
22713             }
22714             
22715             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22716                 v.addClass('disabled');
22717             }
22718             
22719         });
22720         
22721         
22722         year = parseInt(year/10, 10) * 10;
22723         
22724         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22725         
22726         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22727         
22728         year -= 1;
22729         for (var i = -1; i < 11; i++) {
22730             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22731                 tag: 'span',
22732                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22733                 html: year
22734             });
22735             
22736             year += 1;
22737         }
22738     },
22739     
22740     showMode: function(dir) 
22741     {
22742         if (dir) {
22743             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22744         }
22745         
22746         Roo.each(this.picker().select('>div',true).elements, function(v){
22747             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22748             v.hide();
22749         });
22750         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22751     },
22752     
22753     place: function()
22754     {
22755         if(this.isInline) {
22756             return;
22757         }
22758         
22759         this.picker().removeClass(['bottom', 'top']);
22760         
22761         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22762             /*
22763              * place to the top of element!
22764              *
22765              */
22766             
22767             this.picker().addClass('top');
22768             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22769             
22770             return;
22771         }
22772         
22773         this.picker().addClass('bottom');
22774         
22775         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22776     },
22777     
22778     parseDate : function(value)
22779     {
22780         if(!value || value instanceof Date){
22781             return value;
22782         }
22783         var v = Date.parseDate(value, this.format);
22784         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22785             v = Date.parseDate(value, 'Y-m-d');
22786         }
22787         if(!v && this.altFormats){
22788             if(!this.altFormatsArray){
22789                 this.altFormatsArray = this.altFormats.split("|");
22790             }
22791             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22792                 v = Date.parseDate(value, this.altFormatsArray[i]);
22793             }
22794         }
22795         return v;
22796     },
22797     
22798     formatDate : function(date, fmt)
22799     {   
22800         return (!date || !(date instanceof Date)) ?
22801         date : date.dateFormat(fmt || this.format);
22802     },
22803     
22804     onFocus : function()
22805     {
22806         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22807         this.showPopup();
22808     },
22809     
22810     onBlur : function()
22811     {
22812         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22813         
22814         var d = this.inputEl().getValue();
22815         
22816         this.setValue(d);
22817                 
22818         this.hidePopup();
22819     },
22820     
22821     showPopup : function()
22822     {
22823         this.picker().show();
22824         this.update();
22825         this.place();
22826         
22827         this.fireEvent('showpopup', this, this.date);
22828     },
22829     
22830     hidePopup : function()
22831     {
22832         if(this.isInline) {
22833             return;
22834         }
22835         this.picker().hide();
22836         this.viewMode = this.startViewMode;
22837         this.showMode();
22838         
22839         this.fireEvent('hidepopup', this, this.date);
22840         
22841     },
22842     
22843     onMousedown: function(e)
22844     {
22845         e.stopPropagation();
22846         e.preventDefault();
22847     },
22848     
22849     keyup: function(e)
22850     {
22851         Roo.bootstrap.DateField.superclass.keyup.call(this);
22852         this.update();
22853     },
22854
22855     setValue: function(v)
22856     {
22857         if(this.fireEvent('beforeselect', this, v) !== false){
22858             var d = new Date(this.parseDate(v) ).clearTime();
22859         
22860             if(isNaN(d.getTime())){
22861                 this.date = this.viewDate = '';
22862                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22863                 return;
22864             }
22865
22866             v = this.formatDate(d);
22867
22868             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22869
22870             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22871
22872             this.update();
22873
22874             this.fireEvent('select', this, this.date);
22875         }
22876     },
22877     
22878     getValue: function()
22879     {
22880         return this.formatDate(this.date);
22881     },
22882     
22883     fireKey: function(e)
22884     {
22885         if (!this.picker().isVisible()){
22886             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22887                 this.showPopup();
22888             }
22889             return;
22890         }
22891         
22892         var dateChanged = false,
22893         dir, day, month,
22894         newDate, newViewDate;
22895         
22896         switch(e.keyCode){
22897             case 27: // escape
22898                 this.hidePopup();
22899                 e.preventDefault();
22900                 break;
22901             case 37: // left
22902             case 39: // right
22903                 if (!this.keyboardNavigation) {
22904                     break;
22905                 }
22906                 dir = e.keyCode == 37 ? -1 : 1;
22907                 
22908                 if (e.ctrlKey){
22909                     newDate = this.moveYear(this.date, dir);
22910                     newViewDate = this.moveYear(this.viewDate, dir);
22911                 } else if (e.shiftKey){
22912                     newDate = this.moveMonth(this.date, dir);
22913                     newViewDate = this.moveMonth(this.viewDate, dir);
22914                 } else {
22915                     newDate = new Date(this.date);
22916                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22917                     newViewDate = new Date(this.viewDate);
22918                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22919                 }
22920                 if (this.dateWithinRange(newDate)){
22921                     this.date = newDate;
22922                     this.viewDate = newViewDate;
22923                     this.setValue(this.formatDate(this.date));
22924 //                    this.update();
22925                     e.preventDefault();
22926                     dateChanged = true;
22927                 }
22928                 break;
22929             case 38: // up
22930             case 40: // down
22931                 if (!this.keyboardNavigation) {
22932                     break;
22933                 }
22934                 dir = e.keyCode == 38 ? -1 : 1;
22935                 if (e.ctrlKey){
22936                     newDate = this.moveYear(this.date, dir);
22937                     newViewDate = this.moveYear(this.viewDate, dir);
22938                 } else if (e.shiftKey){
22939                     newDate = this.moveMonth(this.date, dir);
22940                     newViewDate = this.moveMonth(this.viewDate, dir);
22941                 } else {
22942                     newDate = new Date(this.date);
22943                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22944                     newViewDate = new Date(this.viewDate);
22945                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22946                 }
22947                 if (this.dateWithinRange(newDate)){
22948                     this.date = newDate;
22949                     this.viewDate = newViewDate;
22950                     this.setValue(this.formatDate(this.date));
22951 //                    this.update();
22952                     e.preventDefault();
22953                     dateChanged = true;
22954                 }
22955                 break;
22956             case 13: // enter
22957                 this.setValue(this.formatDate(this.date));
22958                 this.hidePopup();
22959                 e.preventDefault();
22960                 break;
22961             case 9: // tab
22962                 this.setValue(this.formatDate(this.date));
22963                 this.hidePopup();
22964                 break;
22965             case 16: // shift
22966             case 17: // ctrl
22967             case 18: // alt
22968                 break;
22969             default :
22970                 this.hidePopup();
22971                 
22972         }
22973     },
22974     
22975     
22976     onClick: function(e) 
22977     {
22978         e.stopPropagation();
22979         e.preventDefault();
22980         
22981         var target = e.getTarget();
22982         
22983         if(target.nodeName.toLowerCase() === 'i'){
22984             target = Roo.get(target).dom.parentNode;
22985         }
22986         
22987         var nodeName = target.nodeName;
22988         var className = target.className;
22989         var html = target.innerHTML;
22990         //Roo.log(nodeName);
22991         
22992         switch(nodeName.toLowerCase()) {
22993             case 'th':
22994                 switch(className) {
22995                     case 'switch':
22996                         this.showMode(1);
22997                         break;
22998                     case 'prev':
22999                     case 'next':
23000                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23001                         switch(this.viewMode){
23002                                 case 0:
23003                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23004                                         break;
23005                                 case 1:
23006                                 case 2:
23007                                         this.viewDate = this.moveYear(this.viewDate, dir);
23008                                         break;
23009                         }
23010                         this.fill();
23011                         break;
23012                     case 'today':
23013                         var date = new Date();
23014                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23015 //                        this.fill()
23016                         this.setValue(this.formatDate(this.date));
23017                         
23018                         this.hidePopup();
23019                         break;
23020                 }
23021                 break;
23022             case 'span':
23023                 if (className.indexOf('disabled') < 0) {
23024                 if (!this.viewDate) {
23025                     this.viewDate = new Date();
23026                 }
23027                 this.viewDate.setUTCDate(1);
23028                     if (className.indexOf('month') > -1) {
23029                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23030                     } else {
23031                         var year = parseInt(html, 10) || 0;
23032                         this.viewDate.setUTCFullYear(year);
23033                         
23034                     }
23035                     
23036                     if(this.singleMode){
23037                         this.setValue(this.formatDate(this.viewDate));
23038                         this.hidePopup();
23039                         return;
23040                     }
23041                     
23042                     this.showMode(-1);
23043                     this.fill();
23044                 }
23045                 break;
23046                 
23047             case 'td':
23048                 //Roo.log(className);
23049                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23050                     var day = parseInt(html, 10) || 1;
23051                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23052                         month = (this.viewDate || new Date()).getUTCMonth();
23053
23054                     if (className.indexOf('old') > -1) {
23055                         if(month === 0 ){
23056                             month = 11;
23057                             year -= 1;
23058                         }else{
23059                             month -= 1;
23060                         }
23061                     } else if (className.indexOf('new') > -1) {
23062                         if (month == 11) {
23063                             month = 0;
23064                             year += 1;
23065                         } else {
23066                             month += 1;
23067                         }
23068                     }
23069                     //Roo.log([year,month,day]);
23070                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23071                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23072 //                    this.fill();
23073                     //Roo.log(this.formatDate(this.date));
23074                     this.setValue(this.formatDate(this.date));
23075                     this.hidePopup();
23076                 }
23077                 break;
23078         }
23079     },
23080     
23081     setStartDate: function(startDate)
23082     {
23083         this.startDate = startDate || -Infinity;
23084         if (this.startDate !== -Infinity) {
23085             this.startDate = this.parseDate(this.startDate);
23086         }
23087         this.update();
23088         this.updateNavArrows();
23089     },
23090
23091     setEndDate: function(endDate)
23092     {
23093         this.endDate = endDate || Infinity;
23094         if (this.endDate !== Infinity) {
23095             this.endDate = this.parseDate(this.endDate);
23096         }
23097         this.update();
23098         this.updateNavArrows();
23099     },
23100     
23101     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23102     {
23103         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23104         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23105             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23106         }
23107         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23108             return parseInt(d, 10);
23109         });
23110         this.update();
23111         this.updateNavArrows();
23112     },
23113     
23114     updateNavArrows: function() 
23115     {
23116         if(this.singleMode){
23117             return;
23118         }
23119         
23120         var d = new Date(this.viewDate),
23121         year = d.getUTCFullYear(),
23122         month = d.getUTCMonth();
23123         
23124         Roo.each(this.picker().select('.prev', true).elements, function(v){
23125             v.show();
23126             switch (this.viewMode) {
23127                 case 0:
23128
23129                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23130                         v.hide();
23131                     }
23132                     break;
23133                 case 1:
23134                 case 2:
23135                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23136                         v.hide();
23137                     }
23138                     break;
23139             }
23140         });
23141         
23142         Roo.each(this.picker().select('.next', true).elements, function(v){
23143             v.show();
23144             switch (this.viewMode) {
23145                 case 0:
23146
23147                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23148                         v.hide();
23149                     }
23150                     break;
23151                 case 1:
23152                 case 2:
23153                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23154                         v.hide();
23155                     }
23156                     break;
23157             }
23158         })
23159     },
23160     
23161     moveMonth: function(date, dir)
23162     {
23163         if (!dir) {
23164             return date;
23165         }
23166         var new_date = new Date(date.valueOf()),
23167         day = new_date.getUTCDate(),
23168         month = new_date.getUTCMonth(),
23169         mag = Math.abs(dir),
23170         new_month, test;
23171         dir = dir > 0 ? 1 : -1;
23172         if (mag == 1){
23173             test = dir == -1
23174             // If going back one month, make sure month is not current month
23175             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23176             ? function(){
23177                 return new_date.getUTCMonth() == month;
23178             }
23179             // If going forward one month, make sure month is as expected
23180             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23181             : function(){
23182                 return new_date.getUTCMonth() != new_month;
23183             };
23184             new_month = month + dir;
23185             new_date.setUTCMonth(new_month);
23186             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23187             if (new_month < 0 || new_month > 11) {
23188                 new_month = (new_month + 12) % 12;
23189             }
23190         } else {
23191             // For magnitudes >1, move one month at a time...
23192             for (var i=0; i<mag; i++) {
23193                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23194                 new_date = this.moveMonth(new_date, dir);
23195             }
23196             // ...then reset the day, keeping it in the new month
23197             new_month = new_date.getUTCMonth();
23198             new_date.setUTCDate(day);
23199             test = function(){
23200                 return new_month != new_date.getUTCMonth();
23201             };
23202         }
23203         // Common date-resetting loop -- if date is beyond end of month, make it
23204         // end of month
23205         while (test()){
23206             new_date.setUTCDate(--day);
23207             new_date.setUTCMonth(new_month);
23208         }
23209         return new_date;
23210     },
23211
23212     moveYear: function(date, dir)
23213     {
23214         return this.moveMonth(date, dir*12);
23215     },
23216
23217     dateWithinRange: function(date)
23218     {
23219         return date >= this.startDate && date <= this.endDate;
23220     },
23221
23222     
23223     remove: function() 
23224     {
23225         this.picker().remove();
23226     },
23227     
23228     validateValue : function(value)
23229     {
23230         if(this.getVisibilityEl().hasClass('hidden')){
23231             return true;
23232         }
23233         
23234         if(value.length < 1)  {
23235             if(this.allowBlank){
23236                 return true;
23237             }
23238             return false;
23239         }
23240         
23241         if(value.length < this.minLength){
23242             return false;
23243         }
23244         if(value.length > this.maxLength){
23245             return false;
23246         }
23247         if(this.vtype){
23248             var vt = Roo.form.VTypes;
23249             if(!vt[this.vtype](value, this)){
23250                 return false;
23251             }
23252         }
23253         if(typeof this.validator == "function"){
23254             var msg = this.validator(value);
23255             if(msg !== true){
23256                 return false;
23257             }
23258         }
23259         
23260         if(this.regex && !this.regex.test(value)){
23261             return false;
23262         }
23263         
23264         if(typeof(this.parseDate(value)) == 'undefined'){
23265             return false;
23266         }
23267         
23268         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23269             return false;
23270         }      
23271         
23272         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23273             return false;
23274         } 
23275         
23276         
23277         return true;
23278     },
23279     
23280     reset : function()
23281     {
23282         this.date = this.viewDate = '';
23283         
23284         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23285     }
23286    
23287 });
23288
23289 Roo.apply(Roo.bootstrap.DateField,  {
23290     
23291     head : {
23292         tag: 'thead',
23293         cn: [
23294         {
23295             tag: 'tr',
23296             cn: [
23297             {
23298                 tag: 'th',
23299                 cls: 'prev',
23300                 html: '<i class="fa fa-arrow-left"/>'
23301             },
23302             {
23303                 tag: 'th',
23304                 cls: 'switch',
23305                 colspan: '5'
23306             },
23307             {
23308                 tag: 'th',
23309                 cls: 'next',
23310                 html: '<i class="fa fa-arrow-right"/>'
23311             }
23312
23313             ]
23314         }
23315         ]
23316     },
23317     
23318     content : {
23319         tag: 'tbody',
23320         cn: [
23321         {
23322             tag: 'tr',
23323             cn: [
23324             {
23325                 tag: 'td',
23326                 colspan: '7'
23327             }
23328             ]
23329         }
23330         ]
23331     },
23332     
23333     footer : {
23334         tag: 'tfoot',
23335         cn: [
23336         {
23337             tag: 'tr',
23338             cn: [
23339             {
23340                 tag: 'th',
23341                 colspan: '7',
23342                 cls: 'today'
23343             }
23344                     
23345             ]
23346         }
23347         ]
23348     },
23349     
23350     dates:{
23351         en: {
23352             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23353             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23354             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23355             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23356             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23357             today: "Today"
23358         }
23359     },
23360     
23361     modes: [
23362     {
23363         clsName: 'days',
23364         navFnc: 'Month',
23365         navStep: 1
23366     },
23367     {
23368         clsName: 'months',
23369         navFnc: 'FullYear',
23370         navStep: 1
23371     },
23372     {
23373         clsName: 'years',
23374         navFnc: 'FullYear',
23375         navStep: 10
23376     }]
23377 });
23378
23379 Roo.apply(Roo.bootstrap.DateField,  {
23380   
23381     template : {
23382         tag: 'div',
23383         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23384         cn: [
23385         {
23386             tag: 'div',
23387             cls: 'datepicker-days',
23388             cn: [
23389             {
23390                 tag: 'table',
23391                 cls: 'table-condensed',
23392                 cn:[
23393                 Roo.bootstrap.DateField.head,
23394                 {
23395                     tag: 'tbody'
23396                 },
23397                 Roo.bootstrap.DateField.footer
23398                 ]
23399             }
23400             ]
23401         },
23402         {
23403             tag: 'div',
23404             cls: 'datepicker-months',
23405             cn: [
23406             {
23407                 tag: 'table',
23408                 cls: 'table-condensed',
23409                 cn:[
23410                 Roo.bootstrap.DateField.head,
23411                 Roo.bootstrap.DateField.content,
23412                 Roo.bootstrap.DateField.footer
23413                 ]
23414             }
23415             ]
23416         },
23417         {
23418             tag: 'div',
23419             cls: 'datepicker-years',
23420             cn: [
23421             {
23422                 tag: 'table',
23423                 cls: 'table-condensed',
23424                 cn:[
23425                 Roo.bootstrap.DateField.head,
23426                 Roo.bootstrap.DateField.content,
23427                 Roo.bootstrap.DateField.footer
23428                 ]
23429             }
23430             ]
23431         }
23432         ]
23433     }
23434 });
23435
23436  
23437
23438  /*
23439  * - LGPL
23440  *
23441  * TimeField
23442  * 
23443  */
23444
23445 /**
23446  * @class Roo.bootstrap.TimeField
23447  * @extends Roo.bootstrap.Input
23448  * Bootstrap DateField class
23449  * 
23450  * 
23451  * @constructor
23452  * Create a new TimeField
23453  * @param {Object} config The config object
23454  */
23455
23456 Roo.bootstrap.TimeField = function(config){
23457     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23458     this.addEvents({
23459             /**
23460              * @event show
23461              * Fires when this field show.
23462              * @param {Roo.bootstrap.DateField} thisthis
23463              * @param {Mixed} date The date value
23464              */
23465             show : true,
23466             /**
23467              * @event show
23468              * Fires when this field hide.
23469              * @param {Roo.bootstrap.DateField} this
23470              * @param {Mixed} date The date value
23471              */
23472             hide : true,
23473             /**
23474              * @event select
23475              * Fires when select a date.
23476              * @param {Roo.bootstrap.DateField} this
23477              * @param {Mixed} date The date value
23478              */
23479             select : true
23480         });
23481 };
23482
23483 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23484     
23485     /**
23486      * @cfg {String} format
23487      * The default time format string which can be overriden for localization support.  The format must be
23488      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23489      */
23490     format : "H:i",
23491
23492     getAutoCreate : function()
23493     {
23494         this.after = '<i class="fa far fa-clock"></i>';
23495         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23496         
23497          
23498     },
23499     onRender: function(ct, position)
23500     {
23501         
23502         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23503                 
23504         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23505         
23506         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23507         
23508         this.pop = this.picker().select('>.datepicker-time',true).first();
23509         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23510         
23511         this.picker().on('mousedown', this.onMousedown, this);
23512         this.picker().on('click', this.onClick, this);
23513         
23514         this.picker().addClass('datepicker-dropdown');
23515     
23516         this.fillTime();
23517         this.update();
23518             
23519         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23520         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23521         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23522         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23523         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23524         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23525
23526     },
23527     
23528     fireKey: function(e){
23529         if (!this.picker().isVisible()){
23530             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23531                 this.show();
23532             }
23533             return;
23534         }
23535
23536         e.preventDefault();
23537         
23538         switch(e.keyCode){
23539             case 27: // escape
23540                 this.hide();
23541                 break;
23542             case 37: // left
23543             case 39: // right
23544                 this.onTogglePeriod();
23545                 break;
23546             case 38: // up
23547                 this.onIncrementMinutes();
23548                 break;
23549             case 40: // down
23550                 this.onDecrementMinutes();
23551                 break;
23552             case 13: // enter
23553             case 9: // tab
23554                 this.setTime();
23555                 break;
23556         }
23557     },
23558     
23559     onClick: function(e) {
23560         e.stopPropagation();
23561         e.preventDefault();
23562     },
23563     
23564     picker : function()
23565     {
23566         return this.pickerEl;
23567     },
23568     
23569     fillTime: function()
23570     {    
23571         var time = this.pop.select('tbody', true).first();
23572         
23573         time.dom.innerHTML = '';
23574         
23575         time.createChild({
23576             tag: 'tr',
23577             cn: [
23578                 {
23579                     tag: 'td',
23580                     cn: [
23581                         {
23582                             tag: 'a',
23583                             href: '#',
23584                             cls: 'btn',
23585                             cn: [
23586                                 {
23587                                     tag: 'i',
23588                                     cls: 'hours-up fa fas fa-chevron-up'
23589                                 }
23590                             ]
23591                         } 
23592                     ]
23593                 },
23594                 {
23595                     tag: 'td',
23596                     cls: 'separator'
23597                 },
23598                 {
23599                     tag: 'td',
23600                     cn: [
23601                         {
23602                             tag: 'a',
23603                             href: '#',
23604                             cls: 'btn',
23605                             cn: [
23606                                 {
23607                                     tag: 'i',
23608                                     cls: 'minutes-up fa fas fa-chevron-up'
23609                                 }
23610                             ]
23611                         }
23612                     ]
23613                 },
23614                 {
23615                     tag: 'td',
23616                     cls: 'separator'
23617                 }
23618             ]
23619         });
23620         
23621         time.createChild({
23622             tag: 'tr',
23623             cn: [
23624                 {
23625                     tag: 'td',
23626                     cn: [
23627                         {
23628                             tag: 'span',
23629                             cls: 'timepicker-hour',
23630                             html: '00'
23631                         }  
23632                     ]
23633                 },
23634                 {
23635                     tag: 'td',
23636                     cls: 'separator',
23637                     html: ':'
23638                 },
23639                 {
23640                     tag: 'td',
23641                     cn: [
23642                         {
23643                             tag: 'span',
23644                             cls: 'timepicker-minute',
23645                             html: '00'
23646                         }  
23647                     ]
23648                 },
23649                 {
23650                     tag: 'td',
23651                     cls: 'separator'
23652                 },
23653                 {
23654                     tag: 'td',
23655                     cn: [
23656                         {
23657                             tag: 'button',
23658                             type: 'button',
23659                             cls: 'btn btn-primary period',
23660                             html: 'AM'
23661                             
23662                         }
23663                     ]
23664                 }
23665             ]
23666         });
23667         
23668         time.createChild({
23669             tag: 'tr',
23670             cn: [
23671                 {
23672                     tag: 'td',
23673                     cn: [
23674                         {
23675                             tag: 'a',
23676                             href: '#',
23677                             cls: 'btn',
23678                             cn: [
23679                                 {
23680                                     tag: 'span',
23681                                     cls: 'hours-down fa fas fa-chevron-down'
23682                                 }
23683                             ]
23684                         }
23685                     ]
23686                 },
23687                 {
23688                     tag: 'td',
23689                     cls: 'separator'
23690                 },
23691                 {
23692                     tag: 'td',
23693                     cn: [
23694                         {
23695                             tag: 'a',
23696                             href: '#',
23697                             cls: 'btn',
23698                             cn: [
23699                                 {
23700                                     tag: 'span',
23701                                     cls: 'minutes-down fa fas fa-chevron-down'
23702                                 }
23703                             ]
23704                         }
23705                     ]
23706                 },
23707                 {
23708                     tag: 'td',
23709                     cls: 'separator'
23710                 }
23711             ]
23712         });
23713         
23714     },
23715     
23716     update: function()
23717     {
23718         
23719         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23720         
23721         this.fill();
23722     },
23723     
23724     fill: function() 
23725     {
23726         var hours = this.time.getHours();
23727         var minutes = this.time.getMinutes();
23728         var period = 'AM';
23729         
23730         if(hours > 11){
23731             period = 'PM';
23732         }
23733         
23734         if(hours == 0){
23735             hours = 12;
23736         }
23737         
23738         
23739         if(hours > 12){
23740             hours = hours - 12;
23741         }
23742         
23743         if(hours < 10){
23744             hours = '0' + hours;
23745         }
23746         
23747         if(minutes < 10){
23748             minutes = '0' + minutes;
23749         }
23750         
23751         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23752         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23753         this.pop.select('button', true).first().dom.innerHTML = period;
23754         
23755     },
23756     
23757     place: function()
23758     {   
23759         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23760         
23761         var cls = ['bottom'];
23762         
23763         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23764             cls.pop();
23765             cls.push('top');
23766         }
23767         
23768         cls.push('right');
23769         
23770         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23771             cls.pop();
23772             cls.push('left');
23773         }
23774         //this.picker().setXY(20000,20000);
23775         this.picker().addClass(cls.join('-'));
23776         
23777         var _this = this;
23778         
23779         Roo.each(cls, function(c){
23780             if(c == 'bottom'){
23781                 (function() {
23782                  //  
23783                 }).defer(200);
23784                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23785                 //_this.picker().setTop(_this.inputEl().getHeight());
23786                 return;
23787             }
23788             if(c == 'top'){
23789                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23790                 
23791                 //_this.picker().setTop(0 - _this.picker().getHeight());
23792                 return;
23793             }
23794             /*
23795             if(c == 'left'){
23796                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23797                 return;
23798             }
23799             if(c == 'right'){
23800                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23801                 return;
23802             }
23803             */
23804         });
23805         
23806     },
23807   
23808     onFocus : function()
23809     {
23810         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23811         this.show();
23812     },
23813     
23814     onBlur : function()
23815     {
23816         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23817         this.hide();
23818     },
23819     
23820     show : function()
23821     {
23822         this.picker().show();
23823         this.pop.show();
23824         this.update();
23825         this.place();
23826         
23827         this.fireEvent('show', this, this.date);
23828     },
23829     
23830     hide : function()
23831     {
23832         this.picker().hide();
23833         this.pop.hide();
23834         
23835         this.fireEvent('hide', this, this.date);
23836     },
23837     
23838     setTime : function()
23839     {
23840         this.hide();
23841         this.setValue(this.time.format(this.format));
23842         
23843         this.fireEvent('select', this, this.date);
23844         
23845         
23846     },
23847     
23848     onMousedown: function(e){
23849         e.stopPropagation();
23850         e.preventDefault();
23851     },
23852     
23853     onIncrementHours: function()
23854     {
23855         Roo.log('onIncrementHours');
23856         this.time = this.time.add(Date.HOUR, 1);
23857         this.update();
23858         
23859     },
23860     
23861     onDecrementHours: function()
23862     {
23863         Roo.log('onDecrementHours');
23864         this.time = this.time.add(Date.HOUR, -1);
23865         this.update();
23866     },
23867     
23868     onIncrementMinutes: function()
23869     {
23870         Roo.log('onIncrementMinutes');
23871         this.time = this.time.add(Date.MINUTE, 1);
23872         this.update();
23873     },
23874     
23875     onDecrementMinutes: function()
23876     {
23877         Roo.log('onDecrementMinutes');
23878         this.time = this.time.add(Date.MINUTE, -1);
23879         this.update();
23880     },
23881     
23882     onTogglePeriod: function()
23883     {
23884         Roo.log('onTogglePeriod');
23885         this.time = this.time.add(Date.HOUR, 12);
23886         this.update();
23887     }
23888     
23889    
23890 });
23891  
23892
23893 Roo.apply(Roo.bootstrap.TimeField,  {
23894   
23895     template : {
23896         tag: 'div',
23897         cls: 'datepicker dropdown-menu',
23898         cn: [
23899             {
23900                 tag: 'div',
23901                 cls: 'datepicker-time',
23902                 cn: [
23903                 {
23904                     tag: 'table',
23905                     cls: 'table-condensed',
23906                     cn:[
23907                         {
23908                             tag: 'tbody',
23909                             cn: [
23910                                 {
23911                                     tag: 'tr',
23912                                     cn: [
23913                                     {
23914                                         tag: 'td',
23915                                         colspan: '7'
23916                                     }
23917                                     ]
23918                                 }
23919                             ]
23920                         },
23921                         {
23922                             tag: 'tfoot',
23923                             cn: [
23924                                 {
23925                                     tag: 'tr',
23926                                     cn: [
23927                                     {
23928                                         tag: 'th',
23929                                         colspan: '7',
23930                                         cls: '',
23931                                         cn: [
23932                                             {
23933                                                 tag: 'button',
23934                                                 cls: 'btn btn-info ok',
23935                                                 html: 'OK'
23936                                             }
23937                                         ]
23938                                     }
23939                     
23940                                     ]
23941                                 }
23942                             ]
23943                         }
23944                     ]
23945                 }
23946                 ]
23947             }
23948         ]
23949     }
23950 });
23951
23952  
23953
23954  /*
23955  * - LGPL
23956  *
23957  * MonthField
23958  * 
23959  */
23960
23961 /**
23962  * @class Roo.bootstrap.MonthField
23963  * @extends Roo.bootstrap.Input
23964  * Bootstrap MonthField class
23965  * 
23966  * @cfg {String} language default en
23967  * 
23968  * @constructor
23969  * Create a new MonthField
23970  * @param {Object} config The config object
23971  */
23972
23973 Roo.bootstrap.MonthField = function(config){
23974     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23975     
23976     this.addEvents({
23977         /**
23978          * @event show
23979          * Fires when this field show.
23980          * @param {Roo.bootstrap.MonthField} this
23981          * @param {Mixed} date The date value
23982          */
23983         show : true,
23984         /**
23985          * @event show
23986          * Fires when this field hide.
23987          * @param {Roo.bootstrap.MonthField} this
23988          * @param {Mixed} date The date value
23989          */
23990         hide : true,
23991         /**
23992          * @event select
23993          * Fires when select a date.
23994          * @param {Roo.bootstrap.MonthField} this
23995          * @param {String} oldvalue The old value
23996          * @param {String} newvalue The new value
23997          */
23998         select : true
23999     });
24000 };
24001
24002 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24003     
24004     onRender: function(ct, position)
24005     {
24006         
24007         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24008         
24009         this.language = this.language || 'en';
24010         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24011         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24012         
24013         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24014         this.isInline = false;
24015         this.isInput = true;
24016         this.component = this.el.select('.add-on', true).first() || false;
24017         this.component = (this.component && this.component.length === 0) ? false : this.component;
24018         this.hasInput = this.component && this.inputEL().length;
24019         
24020         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24021         
24022         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24023         
24024         this.picker().on('mousedown', this.onMousedown, this);
24025         this.picker().on('click', this.onClick, this);
24026         
24027         this.picker().addClass('datepicker-dropdown');
24028         
24029         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24030             v.setStyle('width', '189px');
24031         });
24032         
24033         this.fillMonths();
24034         
24035         this.update();
24036         
24037         if(this.isInline) {
24038             this.show();
24039         }
24040         
24041     },
24042     
24043     setValue: function(v, suppressEvent)
24044     {   
24045         var o = this.getValue();
24046         
24047         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24048         
24049         this.update();
24050
24051         if(suppressEvent !== true){
24052             this.fireEvent('select', this, o, v);
24053         }
24054         
24055     },
24056     
24057     getValue: function()
24058     {
24059         return this.value;
24060     },
24061     
24062     onClick: function(e) 
24063     {
24064         e.stopPropagation();
24065         e.preventDefault();
24066         
24067         var target = e.getTarget();
24068         
24069         if(target.nodeName.toLowerCase() === 'i'){
24070             target = Roo.get(target).dom.parentNode;
24071         }
24072         
24073         var nodeName = target.nodeName;
24074         var className = target.className;
24075         var html = target.innerHTML;
24076         
24077         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24078             return;
24079         }
24080         
24081         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24082         
24083         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24084         
24085         this.hide();
24086                         
24087     },
24088     
24089     picker : function()
24090     {
24091         return this.pickerEl;
24092     },
24093     
24094     fillMonths: function()
24095     {    
24096         var i = 0;
24097         var months = this.picker().select('>.datepicker-months td', true).first();
24098         
24099         months.dom.innerHTML = '';
24100         
24101         while (i < 12) {
24102             var month = {
24103                 tag: 'span',
24104                 cls: 'month',
24105                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24106             };
24107             
24108             months.createChild(month);
24109         }
24110         
24111     },
24112     
24113     update: function()
24114     {
24115         var _this = this;
24116         
24117         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24118             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24119         }
24120         
24121         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24122             e.removeClass('active');
24123             
24124             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24125                 e.addClass('active');
24126             }
24127         })
24128     },
24129     
24130     place: function()
24131     {
24132         if(this.isInline) {
24133             return;
24134         }
24135         
24136         this.picker().removeClass(['bottom', 'top']);
24137         
24138         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24139             /*
24140              * place to the top of element!
24141              *
24142              */
24143             
24144             this.picker().addClass('top');
24145             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24146             
24147             return;
24148         }
24149         
24150         this.picker().addClass('bottom');
24151         
24152         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24153     },
24154     
24155     onFocus : function()
24156     {
24157         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24158         this.show();
24159     },
24160     
24161     onBlur : function()
24162     {
24163         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24164         
24165         var d = this.inputEl().getValue();
24166         
24167         this.setValue(d);
24168                 
24169         this.hide();
24170     },
24171     
24172     show : function()
24173     {
24174         this.picker().show();
24175         this.picker().select('>.datepicker-months', true).first().show();
24176         this.update();
24177         this.place();
24178         
24179         this.fireEvent('show', this, this.date);
24180     },
24181     
24182     hide : function()
24183     {
24184         if(this.isInline) {
24185             return;
24186         }
24187         this.picker().hide();
24188         this.fireEvent('hide', this, this.date);
24189         
24190     },
24191     
24192     onMousedown: function(e)
24193     {
24194         e.stopPropagation();
24195         e.preventDefault();
24196     },
24197     
24198     keyup: function(e)
24199     {
24200         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24201         this.update();
24202     },
24203
24204     fireKey: function(e)
24205     {
24206         if (!this.picker().isVisible()){
24207             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24208                 this.show();
24209             }
24210             return;
24211         }
24212         
24213         var dir;
24214         
24215         switch(e.keyCode){
24216             case 27: // escape
24217                 this.hide();
24218                 e.preventDefault();
24219                 break;
24220             case 37: // left
24221             case 39: // right
24222                 dir = e.keyCode == 37 ? -1 : 1;
24223                 
24224                 this.vIndex = this.vIndex + dir;
24225                 
24226                 if(this.vIndex < 0){
24227                     this.vIndex = 0;
24228                 }
24229                 
24230                 if(this.vIndex > 11){
24231                     this.vIndex = 11;
24232                 }
24233                 
24234                 if(isNaN(this.vIndex)){
24235                     this.vIndex = 0;
24236                 }
24237                 
24238                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24239                 
24240                 break;
24241             case 38: // up
24242             case 40: // down
24243                 
24244                 dir = e.keyCode == 38 ? -1 : 1;
24245                 
24246                 this.vIndex = this.vIndex + dir * 4;
24247                 
24248                 if(this.vIndex < 0){
24249                     this.vIndex = 0;
24250                 }
24251                 
24252                 if(this.vIndex > 11){
24253                     this.vIndex = 11;
24254                 }
24255                 
24256                 if(isNaN(this.vIndex)){
24257                     this.vIndex = 0;
24258                 }
24259                 
24260                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24261                 break;
24262                 
24263             case 13: // enter
24264                 
24265                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24266                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24267                 }
24268                 
24269                 this.hide();
24270                 e.preventDefault();
24271                 break;
24272             case 9: // tab
24273                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24274                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24275                 }
24276                 this.hide();
24277                 break;
24278             case 16: // shift
24279             case 17: // ctrl
24280             case 18: // alt
24281                 break;
24282             default :
24283                 this.hide();
24284                 
24285         }
24286     },
24287     
24288     remove: function() 
24289     {
24290         this.picker().remove();
24291     }
24292    
24293 });
24294
24295 Roo.apply(Roo.bootstrap.MonthField,  {
24296     
24297     content : {
24298         tag: 'tbody',
24299         cn: [
24300         {
24301             tag: 'tr',
24302             cn: [
24303             {
24304                 tag: 'td',
24305                 colspan: '7'
24306             }
24307             ]
24308         }
24309         ]
24310     },
24311     
24312     dates:{
24313         en: {
24314             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24315             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24316         }
24317     }
24318 });
24319
24320 Roo.apply(Roo.bootstrap.MonthField,  {
24321   
24322     template : {
24323         tag: 'div',
24324         cls: 'datepicker dropdown-menu roo-dynamic',
24325         cn: [
24326             {
24327                 tag: 'div',
24328                 cls: 'datepicker-months',
24329                 cn: [
24330                 {
24331                     tag: 'table',
24332                     cls: 'table-condensed',
24333                     cn:[
24334                         Roo.bootstrap.DateField.content
24335                     ]
24336                 }
24337                 ]
24338             }
24339         ]
24340     }
24341 });
24342
24343  
24344
24345  
24346  /*
24347  * - LGPL
24348  *
24349  * CheckBox
24350  * 
24351  */
24352
24353 /**
24354  * @class Roo.bootstrap.CheckBox
24355  * @extends Roo.bootstrap.Input
24356  * Bootstrap CheckBox class
24357  * 
24358  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24359  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24360  * @cfg {String} boxLabel The text that appears beside the checkbox
24361  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24362  * @cfg {Boolean} checked initnal the element
24363  * @cfg {Boolean} inline inline the element (default false)
24364  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24365  * @cfg {String} tooltip label tooltip
24366  * 
24367  * @constructor
24368  * Create a new CheckBox
24369  * @param {Object} config The config object
24370  */
24371
24372 Roo.bootstrap.CheckBox = function(config){
24373     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24374    
24375     this.addEvents({
24376         /**
24377         * @event check
24378         * Fires when the element is checked or unchecked.
24379         * @param {Roo.bootstrap.CheckBox} this This input
24380         * @param {Boolean} checked The new checked value
24381         */
24382        check : true,
24383        /**
24384         * @event click
24385         * Fires when the element is click.
24386         * @param {Roo.bootstrap.CheckBox} this This input
24387         */
24388        click : true
24389     });
24390     
24391 };
24392
24393 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24394   
24395     inputType: 'checkbox',
24396     inputValue: 1,
24397     valueOff: 0,
24398     boxLabel: false,
24399     checked: false,
24400     weight : false,
24401     inline: false,
24402     tooltip : '',
24403     
24404     // checkbox success does not make any sense really.. 
24405     invalidClass : "",
24406     validClass : "",
24407     
24408     
24409     getAutoCreate : function()
24410     {
24411         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24412         
24413         var id = Roo.id();
24414         
24415         var cfg = {};
24416         
24417         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24418         
24419         if(this.inline){
24420             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24421         }
24422         
24423         var input =  {
24424             tag: 'input',
24425             id : id,
24426             type : this.inputType,
24427             value : this.inputValue,
24428             cls : 'roo-' + this.inputType, //'form-box',
24429             placeholder : this.placeholder || ''
24430             
24431         };
24432         
24433         if(this.inputType != 'radio'){
24434             var hidden =  {
24435                 tag: 'input',
24436                 type : 'hidden',
24437                 cls : 'roo-hidden-value',
24438                 value : this.checked ? this.inputValue : this.valueOff
24439             };
24440         }
24441         
24442             
24443         if (this.weight) { // Validity check?
24444             cfg.cls += " " + this.inputType + "-" + this.weight;
24445         }
24446         
24447         if (this.disabled) {
24448             input.disabled=true;
24449         }
24450         
24451         if(this.checked){
24452             input.checked = this.checked;
24453         }
24454         
24455         if (this.name) {
24456             
24457             input.name = this.name;
24458             
24459             if(this.inputType != 'radio'){
24460                 hidden.name = this.name;
24461                 input.name = '_hidden_' + this.name;
24462             }
24463         }
24464         
24465         if (this.size) {
24466             input.cls += ' input-' + this.size;
24467         }
24468         
24469         var settings=this;
24470         
24471         ['xs','sm','md','lg'].map(function(size){
24472             if (settings[size]) {
24473                 cfg.cls += ' col-' + size + '-' + settings[size];
24474             }
24475         });
24476         
24477         var inputblock = input;
24478          
24479         if (this.before || this.after) {
24480             
24481             inputblock = {
24482                 cls : 'input-group',
24483                 cn :  [] 
24484             };
24485             
24486             if (this.before) {
24487                 inputblock.cn.push({
24488                     tag :'span',
24489                     cls : 'input-group-addon',
24490                     html : this.before
24491                 });
24492             }
24493             
24494             inputblock.cn.push(input);
24495             
24496             if(this.inputType != 'radio'){
24497                 inputblock.cn.push(hidden);
24498             }
24499             
24500             if (this.after) {
24501                 inputblock.cn.push({
24502                     tag :'span',
24503                     cls : 'input-group-addon',
24504                     html : this.after
24505                 });
24506             }
24507             
24508         }
24509         var boxLabelCfg = false;
24510         
24511         if(this.boxLabel){
24512            
24513             boxLabelCfg = {
24514                 tag: 'label',
24515                 //'for': id, // box label is handled by onclick - so no for...
24516                 cls: 'box-label',
24517                 html: this.boxLabel
24518             };
24519             if(this.tooltip){
24520                 boxLabelCfg.tooltip = this.tooltip;
24521             }
24522              
24523         }
24524         
24525         
24526         if (align ==='left' && this.fieldLabel.length) {
24527 //                Roo.log("left and has label");
24528             cfg.cn = [
24529                 {
24530                     tag: 'label',
24531                     'for' :  id,
24532                     cls : 'control-label',
24533                     html : this.fieldLabel
24534                 },
24535                 {
24536                     cls : "", 
24537                     cn: [
24538                         inputblock
24539                     ]
24540                 }
24541             ];
24542             
24543             if (boxLabelCfg) {
24544                 cfg.cn[1].cn.push(boxLabelCfg);
24545             }
24546             
24547             if(this.labelWidth > 12){
24548                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24549             }
24550             
24551             if(this.labelWidth < 13 && this.labelmd == 0){
24552                 this.labelmd = this.labelWidth;
24553             }
24554             
24555             if(this.labellg > 0){
24556                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24557                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24558             }
24559             
24560             if(this.labelmd > 0){
24561                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24562                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24563             }
24564             
24565             if(this.labelsm > 0){
24566                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24567                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24568             }
24569             
24570             if(this.labelxs > 0){
24571                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24572                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24573             }
24574             
24575         } else if ( this.fieldLabel.length) {
24576 //                Roo.log(" label");
24577                 cfg.cn = [
24578                    
24579                     {
24580                         tag: this.boxLabel ? 'span' : 'label',
24581                         'for': id,
24582                         cls: 'control-label box-input-label',
24583                         //cls : 'input-group-addon',
24584                         html : this.fieldLabel
24585                     },
24586                     
24587                     inputblock
24588                     
24589                 ];
24590                 if (boxLabelCfg) {
24591                     cfg.cn.push(boxLabelCfg);
24592                 }
24593
24594         } else {
24595             
24596 //                Roo.log(" no label && no align");
24597                 cfg.cn = [  inputblock ] ;
24598                 if (boxLabelCfg) {
24599                     cfg.cn.push(boxLabelCfg);
24600                 }
24601
24602                 
24603         }
24604         
24605        
24606         
24607         if(this.inputType != 'radio'){
24608             cfg.cn.push(hidden);
24609         }
24610         
24611         return cfg;
24612         
24613     },
24614     
24615     /**
24616      * return the real input element.
24617      */
24618     inputEl: function ()
24619     {
24620         return this.el.select('input.roo-' + this.inputType,true).first();
24621     },
24622     hiddenEl: function ()
24623     {
24624         return this.el.select('input.roo-hidden-value',true).first();
24625     },
24626     
24627     labelEl: function()
24628     {
24629         return this.el.select('label.control-label',true).first();
24630     },
24631     /* depricated... */
24632     
24633     label: function()
24634     {
24635         return this.labelEl();
24636     },
24637     
24638     boxLabelEl: function()
24639     {
24640         return this.el.select('label.box-label',true).first();
24641     },
24642     
24643     initEvents : function()
24644     {
24645 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24646         
24647         this.inputEl().on('click', this.onClick,  this);
24648         
24649         if (this.boxLabel) { 
24650             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24651         }
24652         
24653         this.startValue = this.getValue();
24654         
24655         if(this.groupId){
24656             Roo.bootstrap.CheckBox.register(this);
24657         }
24658     },
24659     
24660     onClick : function(e)
24661     {   
24662         if(this.fireEvent('click', this, e) !== false){
24663             this.setChecked(!this.checked);
24664         }
24665         
24666     },
24667     
24668     setChecked : function(state,suppressEvent)
24669     {
24670         this.startValue = this.getValue();
24671
24672         if(this.inputType == 'radio'){
24673             
24674             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24675                 e.dom.checked = false;
24676             });
24677             
24678             this.inputEl().dom.checked = true;
24679             
24680             this.inputEl().dom.value = this.inputValue;
24681             
24682             if(suppressEvent !== true){
24683                 this.fireEvent('check', this, true);
24684             }
24685             
24686             this.validate();
24687             
24688             return;
24689         }
24690         
24691         this.checked = state;
24692         
24693         this.inputEl().dom.checked = state;
24694         
24695         
24696         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24697         
24698         if(suppressEvent !== true){
24699             this.fireEvent('check', this, state);
24700         }
24701         
24702         this.validate();
24703     },
24704     
24705     getValue : function()
24706     {
24707         if(this.inputType == 'radio'){
24708             return this.getGroupValue();
24709         }
24710         
24711         return this.hiddenEl().dom.value;
24712         
24713     },
24714     
24715     getGroupValue : function()
24716     {
24717         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24718             return '';
24719         }
24720         
24721         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24722     },
24723     
24724     setValue : function(v,suppressEvent)
24725     {
24726         if(this.inputType == 'radio'){
24727             this.setGroupValue(v, suppressEvent);
24728             return;
24729         }
24730         
24731         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24732         
24733         this.validate();
24734     },
24735     
24736     setGroupValue : function(v, suppressEvent)
24737     {
24738         this.startValue = this.getValue();
24739         
24740         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24741             e.dom.checked = false;
24742             
24743             if(e.dom.value == v){
24744                 e.dom.checked = true;
24745             }
24746         });
24747         
24748         if(suppressEvent !== true){
24749             this.fireEvent('check', this, true);
24750         }
24751
24752         this.validate();
24753         
24754         return;
24755     },
24756     
24757     validate : function()
24758     {
24759         if(this.getVisibilityEl().hasClass('hidden')){
24760             return true;
24761         }
24762         
24763         if(
24764                 this.disabled || 
24765                 (this.inputType == 'radio' && this.validateRadio()) ||
24766                 (this.inputType == 'checkbox' && this.validateCheckbox())
24767         ){
24768             this.markValid();
24769             return true;
24770         }
24771         
24772         this.markInvalid();
24773         return false;
24774     },
24775     
24776     validateRadio : function()
24777     {
24778         if(this.getVisibilityEl().hasClass('hidden')){
24779             return true;
24780         }
24781         
24782         if(this.allowBlank){
24783             return true;
24784         }
24785         
24786         var valid = false;
24787         
24788         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24789             if(!e.dom.checked){
24790                 return;
24791             }
24792             
24793             valid = true;
24794             
24795             return false;
24796         });
24797         
24798         return valid;
24799     },
24800     
24801     validateCheckbox : function()
24802     {
24803         if(!this.groupId){
24804             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24805             //return (this.getValue() == this.inputValue) ? true : false;
24806         }
24807         
24808         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24809         
24810         if(!group){
24811             return false;
24812         }
24813         
24814         var r = false;
24815         
24816         for(var i in group){
24817             if(group[i].el.isVisible(true)){
24818                 r = false;
24819                 break;
24820             }
24821             
24822             r = true;
24823         }
24824         
24825         for(var i in group){
24826             if(r){
24827                 break;
24828             }
24829             
24830             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24831         }
24832         
24833         return r;
24834     },
24835     
24836     /**
24837      * Mark this field as valid
24838      */
24839     markValid : function()
24840     {
24841         var _this = this;
24842         
24843         this.fireEvent('valid', this);
24844         
24845         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24846         
24847         if(this.groupId){
24848             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24849         }
24850         
24851         if(label){
24852             label.markValid();
24853         }
24854
24855         if(this.inputType == 'radio'){
24856             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24857                 var fg = e.findParent('.form-group', false, true);
24858                 if (Roo.bootstrap.version == 3) {
24859                     fg.removeClass([_this.invalidClass, _this.validClass]);
24860                     fg.addClass(_this.validClass);
24861                 } else {
24862                     fg.removeClass(['is-valid', 'is-invalid']);
24863                     fg.addClass('is-valid');
24864                 }
24865             });
24866             
24867             return;
24868         }
24869
24870         if(!this.groupId){
24871             var fg = this.el.findParent('.form-group', false, true);
24872             if (Roo.bootstrap.version == 3) {
24873                 fg.removeClass([this.invalidClass, this.validClass]);
24874                 fg.addClass(this.validClass);
24875             } else {
24876                 fg.removeClass(['is-valid', 'is-invalid']);
24877                 fg.addClass('is-valid');
24878             }
24879             return;
24880         }
24881         
24882         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24883         
24884         if(!group){
24885             return;
24886         }
24887         
24888         for(var i in group){
24889             var fg = group[i].el.findParent('.form-group', false, true);
24890             if (Roo.bootstrap.version == 3) {
24891                 fg.removeClass([this.invalidClass, this.validClass]);
24892                 fg.addClass(this.validClass);
24893             } else {
24894                 fg.removeClass(['is-valid', 'is-invalid']);
24895                 fg.addClass('is-valid');
24896             }
24897         }
24898     },
24899     
24900      /**
24901      * Mark this field as invalid
24902      * @param {String} msg The validation message
24903      */
24904     markInvalid : function(msg)
24905     {
24906         if(this.allowBlank){
24907             return;
24908         }
24909         
24910         var _this = this;
24911         
24912         this.fireEvent('invalid', this, msg);
24913         
24914         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24915         
24916         if(this.groupId){
24917             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24918         }
24919         
24920         if(label){
24921             label.markInvalid();
24922         }
24923             
24924         if(this.inputType == 'radio'){
24925             
24926             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24927                 var fg = e.findParent('.form-group', false, true);
24928                 if (Roo.bootstrap.version == 3) {
24929                     fg.removeClass([_this.invalidClass, _this.validClass]);
24930                     fg.addClass(_this.invalidClass);
24931                 } else {
24932                     fg.removeClass(['is-invalid', 'is-valid']);
24933                     fg.addClass('is-invalid');
24934                 }
24935             });
24936             
24937             return;
24938         }
24939         
24940         if(!this.groupId){
24941             var fg = this.el.findParent('.form-group', false, true);
24942             if (Roo.bootstrap.version == 3) {
24943                 fg.removeClass([_this.invalidClass, _this.validClass]);
24944                 fg.addClass(_this.invalidClass);
24945             } else {
24946                 fg.removeClass(['is-invalid', 'is-valid']);
24947                 fg.addClass('is-invalid');
24948             }
24949             return;
24950         }
24951         
24952         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24953         
24954         if(!group){
24955             return;
24956         }
24957         
24958         for(var i in group){
24959             var fg = group[i].el.findParent('.form-group', false, true);
24960             if (Roo.bootstrap.version == 3) {
24961                 fg.removeClass([_this.invalidClass, _this.validClass]);
24962                 fg.addClass(_this.invalidClass);
24963             } else {
24964                 fg.removeClass(['is-invalid', 'is-valid']);
24965                 fg.addClass('is-invalid');
24966             }
24967         }
24968         
24969     },
24970     
24971     clearInvalid : function()
24972     {
24973         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24974         
24975         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24976         
24977         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24978         
24979         if (label && label.iconEl) {
24980             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24981             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24982         }
24983     },
24984     
24985     disable : function()
24986     {
24987         if(this.inputType != 'radio'){
24988             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24989             return;
24990         }
24991         
24992         var _this = this;
24993         
24994         if(this.rendered){
24995             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24996                 _this.getActionEl().addClass(this.disabledClass);
24997                 e.dom.disabled = true;
24998             });
24999         }
25000         
25001         this.disabled = true;
25002         this.fireEvent("disable", this);
25003         return this;
25004     },
25005
25006     enable : function()
25007     {
25008         if(this.inputType != 'radio'){
25009             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25010             return;
25011         }
25012         
25013         var _this = this;
25014         
25015         if(this.rendered){
25016             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25017                 _this.getActionEl().removeClass(this.disabledClass);
25018                 e.dom.disabled = false;
25019             });
25020         }
25021         
25022         this.disabled = false;
25023         this.fireEvent("enable", this);
25024         return this;
25025     },
25026     
25027     setBoxLabel : function(v)
25028     {
25029         this.boxLabel = v;
25030         
25031         if(this.rendered){
25032             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25033         }
25034     }
25035
25036 });
25037
25038 Roo.apply(Roo.bootstrap.CheckBox, {
25039     
25040     groups: {},
25041     
25042      /**
25043     * register a CheckBox Group
25044     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25045     */
25046     register : function(checkbox)
25047     {
25048         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25049             this.groups[checkbox.groupId] = {};
25050         }
25051         
25052         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25053             return;
25054         }
25055         
25056         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25057         
25058     },
25059     /**
25060     * fetch a CheckBox Group based on the group ID
25061     * @param {string} the group ID
25062     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25063     */
25064     get: function(groupId) {
25065         if (typeof(this.groups[groupId]) == 'undefined') {
25066             return false;
25067         }
25068         
25069         return this.groups[groupId] ;
25070     }
25071     
25072     
25073 });
25074 /*
25075  * - LGPL
25076  *
25077  * RadioItem
25078  * 
25079  */
25080
25081 /**
25082  * @class Roo.bootstrap.Radio
25083  * @extends Roo.bootstrap.Component
25084  * Bootstrap Radio class
25085  * @cfg {String} boxLabel - the label associated
25086  * @cfg {String} value - the value of radio
25087  * 
25088  * @constructor
25089  * Create a new Radio
25090  * @param {Object} config The config object
25091  */
25092 Roo.bootstrap.Radio = function(config){
25093     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25094     
25095 };
25096
25097 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25098     
25099     boxLabel : '',
25100     
25101     value : '',
25102     
25103     getAutoCreate : function()
25104     {
25105         var cfg = {
25106             tag : 'div',
25107             cls : 'form-group radio',
25108             cn : [
25109                 {
25110                     tag : 'label',
25111                     cls : 'box-label',
25112                     html : this.boxLabel
25113                 }
25114             ]
25115         };
25116         
25117         return cfg;
25118     },
25119     
25120     initEvents : function() 
25121     {
25122         this.parent().register(this);
25123         
25124         this.el.on('click', this.onClick, this);
25125         
25126     },
25127     
25128     onClick : function(e)
25129     {
25130         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25131             this.setChecked(true);
25132         }
25133     },
25134     
25135     setChecked : function(state, suppressEvent)
25136     {
25137         this.parent().setValue(this.value, suppressEvent);
25138         
25139     },
25140     
25141     setBoxLabel : function(v)
25142     {
25143         this.boxLabel = v;
25144         
25145         if(this.rendered){
25146             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25147         }
25148     }
25149     
25150 });
25151  
25152
25153  /*
25154  * - LGPL
25155  *
25156  * Input
25157  * 
25158  */
25159
25160 /**
25161  * @class Roo.bootstrap.SecurePass
25162  * @extends Roo.bootstrap.Input
25163  * Bootstrap SecurePass class
25164  *
25165  * 
25166  * @constructor
25167  * Create a new SecurePass
25168  * @param {Object} config The config object
25169  */
25170  
25171 Roo.bootstrap.SecurePass = function (config) {
25172     // these go here, so the translation tool can replace them..
25173     this.errors = {
25174         PwdEmpty: "Please type a password, and then retype it to confirm.",
25175         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25176         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25177         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25178         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25179         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25180         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25181         TooWeak: "Your password is Too Weak."
25182     },
25183     this.meterLabel = "Password strength:";
25184     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25185     this.meterClass = [
25186         "roo-password-meter-tooweak", 
25187         "roo-password-meter-weak", 
25188         "roo-password-meter-medium", 
25189         "roo-password-meter-strong", 
25190         "roo-password-meter-grey"
25191     ];
25192     
25193     this.errors = {};
25194     
25195     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25196 }
25197
25198 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25199     /**
25200      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25201      * {
25202      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25203      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25204      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25205      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25206      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25207      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25208      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25209      * })
25210      */
25211     // private
25212     
25213     meterWidth: 300,
25214     errorMsg :'',    
25215     errors: false,
25216     imageRoot: '/',
25217     /**
25218      * @cfg {String/Object} Label for the strength meter (defaults to
25219      * 'Password strength:')
25220      */
25221     // private
25222     meterLabel: '',
25223     /**
25224      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25225      * ['Weak', 'Medium', 'Strong'])
25226      */
25227     // private    
25228     pwdStrengths: false,    
25229     // private
25230     strength: 0,
25231     // private
25232     _lastPwd: null,
25233     // private
25234     kCapitalLetter: 0,
25235     kSmallLetter: 1,
25236     kDigit: 2,
25237     kPunctuation: 3,
25238     
25239     insecure: false,
25240     // private
25241     initEvents: function ()
25242     {
25243         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25244
25245         if (this.el.is('input[type=password]') && Roo.isSafari) {
25246             this.el.on('keydown', this.SafariOnKeyDown, this);
25247         }
25248
25249         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25250     },
25251     // private
25252     onRender: function (ct, position)
25253     {
25254         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25255         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25256         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25257
25258         this.trigger.createChild({
25259                    cn: [
25260                     {
25261                     //id: 'PwdMeter',
25262                     tag: 'div',
25263                     cls: 'roo-password-meter-grey col-xs-12',
25264                     style: {
25265                         //width: 0,
25266                         //width: this.meterWidth + 'px'                                                
25267                         }
25268                     },
25269                     {                            
25270                          cls: 'roo-password-meter-text'                          
25271                     }
25272                 ]            
25273         });
25274
25275          
25276         if (this.hideTrigger) {
25277             this.trigger.setDisplayed(false);
25278         }
25279         this.setSize(this.width || '', this.height || '');
25280     },
25281     // private
25282     onDestroy: function ()
25283     {
25284         if (this.trigger) {
25285             this.trigger.removeAllListeners();
25286             this.trigger.remove();
25287         }
25288         if (this.wrap) {
25289             this.wrap.remove();
25290         }
25291         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25292     },
25293     // private
25294     checkStrength: function ()
25295     {
25296         var pwd = this.inputEl().getValue();
25297         if (pwd == this._lastPwd) {
25298             return;
25299         }
25300
25301         var strength;
25302         if (this.ClientSideStrongPassword(pwd)) {
25303             strength = 3;
25304         } else if (this.ClientSideMediumPassword(pwd)) {
25305             strength = 2;
25306         } else if (this.ClientSideWeakPassword(pwd)) {
25307             strength = 1;
25308         } else {
25309             strength = 0;
25310         }
25311         
25312         Roo.log('strength1: ' + strength);
25313         
25314         //var pm = this.trigger.child('div/div/div').dom;
25315         var pm = this.trigger.child('div/div');
25316         pm.removeClass(this.meterClass);
25317         pm.addClass(this.meterClass[strength]);
25318                 
25319         
25320         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25321                 
25322         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25323         
25324         this._lastPwd = pwd;
25325     },
25326     reset: function ()
25327     {
25328         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25329         
25330         this._lastPwd = '';
25331         
25332         var pm = this.trigger.child('div/div');
25333         pm.removeClass(this.meterClass);
25334         pm.addClass('roo-password-meter-grey');        
25335         
25336         
25337         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25338         
25339         pt.innerHTML = '';
25340         this.inputEl().dom.type='password';
25341     },
25342     // private
25343     validateValue: function (value)
25344     {
25345         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25346             return false;
25347         }
25348         if (value.length == 0) {
25349             if (this.allowBlank) {
25350                 this.clearInvalid();
25351                 return true;
25352             }
25353
25354             this.markInvalid(this.errors.PwdEmpty);
25355             this.errorMsg = this.errors.PwdEmpty;
25356             return false;
25357         }
25358         
25359         if(this.insecure){
25360             return true;
25361         }
25362         
25363         if (!value.match(/[\x21-\x7e]+/)) {
25364             this.markInvalid(this.errors.PwdBadChar);
25365             this.errorMsg = this.errors.PwdBadChar;
25366             return false;
25367         }
25368         if (value.length < 6) {
25369             this.markInvalid(this.errors.PwdShort);
25370             this.errorMsg = this.errors.PwdShort;
25371             return false;
25372         }
25373         if (value.length > 16) {
25374             this.markInvalid(this.errors.PwdLong);
25375             this.errorMsg = this.errors.PwdLong;
25376             return false;
25377         }
25378         var strength;
25379         if (this.ClientSideStrongPassword(value)) {
25380             strength = 3;
25381         } else if (this.ClientSideMediumPassword(value)) {
25382             strength = 2;
25383         } else if (this.ClientSideWeakPassword(value)) {
25384             strength = 1;
25385         } else {
25386             strength = 0;
25387         }
25388
25389         
25390         if (strength < 2) {
25391             //this.markInvalid(this.errors.TooWeak);
25392             this.errorMsg = this.errors.TooWeak;
25393             //return false;
25394         }
25395         
25396         
25397         console.log('strength2: ' + strength);
25398         
25399         //var pm = this.trigger.child('div/div/div').dom;
25400         
25401         var pm = this.trigger.child('div/div');
25402         pm.removeClass(this.meterClass);
25403         pm.addClass(this.meterClass[strength]);
25404                 
25405         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25406                 
25407         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25408         
25409         this.errorMsg = ''; 
25410         return true;
25411     },
25412     // private
25413     CharacterSetChecks: function (type)
25414     {
25415         this.type = type;
25416         this.fResult = false;
25417     },
25418     // private
25419     isctype: function (character, type)
25420     {
25421         switch (type) {  
25422             case this.kCapitalLetter:
25423                 if (character >= 'A' && character <= 'Z') {
25424                     return true;
25425                 }
25426                 break;
25427             
25428             case this.kSmallLetter:
25429                 if (character >= 'a' && character <= 'z') {
25430                     return true;
25431                 }
25432                 break;
25433             
25434             case this.kDigit:
25435                 if (character >= '0' && character <= '9') {
25436                     return true;
25437                 }
25438                 break;
25439             
25440             case this.kPunctuation:
25441                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25442                     return true;
25443                 }
25444                 break;
25445             
25446             default:
25447                 return false;
25448         }
25449
25450     },
25451     // private
25452     IsLongEnough: function (pwd, size)
25453     {
25454         return !(pwd == null || isNaN(size) || pwd.length < size);
25455     },
25456     // private
25457     SpansEnoughCharacterSets: function (word, nb)
25458     {
25459         if (!this.IsLongEnough(word, nb))
25460         {
25461             return false;
25462         }
25463
25464         var characterSetChecks = new Array(
25465             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25466             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25467         );
25468         
25469         for (var index = 0; index < word.length; ++index) {
25470             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25471                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25472                     characterSetChecks[nCharSet].fResult = true;
25473                     break;
25474                 }
25475             }
25476         }
25477
25478         var nCharSets = 0;
25479         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25480             if (characterSetChecks[nCharSet].fResult) {
25481                 ++nCharSets;
25482             }
25483         }
25484
25485         if (nCharSets < nb) {
25486             return false;
25487         }
25488         return true;
25489     },
25490     // private
25491     ClientSideStrongPassword: function (pwd)
25492     {
25493         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25494     },
25495     // private
25496     ClientSideMediumPassword: function (pwd)
25497     {
25498         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25499     },
25500     // private
25501     ClientSideWeakPassword: function (pwd)
25502     {
25503         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25504     }
25505           
25506 })//<script type="text/javascript">
25507
25508 /*
25509  * Based  Ext JS Library 1.1.1
25510  * Copyright(c) 2006-2007, Ext JS, LLC.
25511  * LGPL
25512  *
25513  */
25514  
25515 /**
25516  * @class Roo.HtmlEditorCore
25517  * @extends Roo.Component
25518  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25519  *
25520  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25521  */
25522
25523 Roo.HtmlEditorCore = function(config){
25524     
25525     
25526     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25527     
25528     
25529     this.addEvents({
25530         /**
25531          * @event initialize
25532          * Fires when the editor is fully initialized (including the iframe)
25533          * @param {Roo.HtmlEditorCore} this
25534          */
25535         initialize: true,
25536         /**
25537          * @event activate
25538          * Fires when the editor is first receives the focus. Any insertion must wait
25539          * until after this event.
25540          * @param {Roo.HtmlEditorCore} this
25541          */
25542         activate: true,
25543          /**
25544          * @event beforesync
25545          * Fires before the textarea is updated with content from the editor iframe. Return false
25546          * to cancel the sync.
25547          * @param {Roo.HtmlEditorCore} this
25548          * @param {String} html
25549          */
25550         beforesync: true,
25551          /**
25552          * @event beforepush
25553          * Fires before the iframe editor is updated with content from the textarea. Return false
25554          * to cancel the push.
25555          * @param {Roo.HtmlEditorCore} this
25556          * @param {String} html
25557          */
25558         beforepush: true,
25559          /**
25560          * @event sync
25561          * Fires when the textarea is updated with content from the editor iframe.
25562          * @param {Roo.HtmlEditorCore} this
25563          * @param {String} html
25564          */
25565         sync: true,
25566          /**
25567          * @event push
25568          * Fires when the iframe editor is updated with content from the textarea.
25569          * @param {Roo.HtmlEditorCore} this
25570          * @param {String} html
25571          */
25572         push: true,
25573         
25574         /**
25575          * @event editorevent
25576          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25577          * @param {Roo.HtmlEditorCore} this
25578          */
25579         editorevent: true
25580         
25581     });
25582     
25583     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25584     
25585     // defaults : white / black...
25586     this.applyBlacklists();
25587     
25588     
25589     
25590 };
25591
25592
25593 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25594
25595
25596      /**
25597      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25598      */
25599     
25600     owner : false,
25601     
25602      /**
25603      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25604      *                        Roo.resizable.
25605      */
25606     resizable : false,
25607      /**
25608      * @cfg {Number} height (in pixels)
25609      */   
25610     height: 300,
25611    /**
25612      * @cfg {Number} width (in pixels)
25613      */   
25614     width: 500,
25615     
25616     /**
25617      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25618      * 
25619      */
25620     stylesheets: false,
25621     
25622     /**
25623      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25624      */
25625     allowComments: false,
25626     // id of frame..
25627     frameId: false,
25628     
25629     // private properties
25630     validationEvent : false,
25631     deferHeight: true,
25632     initialized : false,
25633     activated : false,
25634     sourceEditMode : false,
25635     onFocus : Roo.emptyFn,
25636     iframePad:3,
25637     hideMode:'offsets',
25638     
25639     clearUp: true,
25640     
25641     // blacklist + whitelisted elements..
25642     black: false,
25643     white: false,
25644      
25645     bodyCls : '',
25646
25647     /**
25648      * Protected method that will not generally be called directly. It
25649      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25650      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25651      */
25652     getDocMarkup : function(){
25653         // body styles..
25654         var st = '';
25655         
25656         // inherit styels from page...?? 
25657         if (this.stylesheets === false) {
25658             
25659             Roo.get(document.head).select('style').each(function(node) {
25660                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25661             });
25662             
25663             Roo.get(document.head).select('link').each(function(node) { 
25664                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25665             });
25666             
25667         } else if (!this.stylesheets.length) {
25668                 // simple..
25669                 st = '<style type="text/css">' +
25670                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25671                    '</style>';
25672         } else {
25673             for (var i in this.stylesheets) { 
25674                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25675             }
25676             
25677         }
25678         
25679         st +=  '<style type="text/css">' +
25680             'IMG { cursor: pointer } ' +
25681         '</style>';
25682
25683         var cls = 'roo-htmleditor-body';
25684         
25685         if(this.bodyCls.length){
25686             cls += ' ' + this.bodyCls;
25687         }
25688         
25689         return '<html><head>' + st  +
25690             //<style type="text/css">' +
25691             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25692             //'</style>' +
25693             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25694     },
25695
25696     // private
25697     onRender : function(ct, position)
25698     {
25699         var _t = this;
25700         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25701         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25702         
25703         
25704         this.el.dom.style.border = '0 none';
25705         this.el.dom.setAttribute('tabIndex', -1);
25706         this.el.addClass('x-hidden hide');
25707         
25708         
25709         
25710         if(Roo.isIE){ // fix IE 1px bogus margin
25711             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25712         }
25713        
25714         
25715         this.frameId = Roo.id();
25716         
25717          
25718         
25719         var iframe = this.owner.wrap.createChild({
25720             tag: 'iframe',
25721             cls: 'form-control', // bootstrap..
25722             id: this.frameId,
25723             name: this.frameId,
25724             frameBorder : 'no',
25725             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25726         }, this.el
25727         );
25728         
25729         
25730         this.iframe = iframe.dom;
25731
25732          this.assignDocWin();
25733         
25734         this.doc.designMode = 'on';
25735        
25736         this.doc.open();
25737         this.doc.write(this.getDocMarkup());
25738         this.doc.close();
25739
25740         
25741         var task = { // must defer to wait for browser to be ready
25742             run : function(){
25743                 //console.log("run task?" + this.doc.readyState);
25744                 this.assignDocWin();
25745                 if(this.doc.body || this.doc.readyState == 'complete'){
25746                     try {
25747                         this.doc.designMode="on";
25748                     } catch (e) {
25749                         return;
25750                     }
25751                     Roo.TaskMgr.stop(task);
25752                     this.initEditor.defer(10, this);
25753                 }
25754             },
25755             interval : 10,
25756             duration: 10000,
25757             scope: this
25758         };
25759         Roo.TaskMgr.start(task);
25760
25761     },
25762
25763     // private
25764     onResize : function(w, h)
25765     {
25766          Roo.log('resize: ' +w + ',' + h );
25767         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25768         if(!this.iframe){
25769             return;
25770         }
25771         if(typeof w == 'number'){
25772             
25773             this.iframe.style.width = w + 'px';
25774         }
25775         if(typeof h == 'number'){
25776             
25777             this.iframe.style.height = h + 'px';
25778             if(this.doc){
25779                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25780             }
25781         }
25782         
25783     },
25784
25785     /**
25786      * Toggles the editor between standard and source edit mode.
25787      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25788      */
25789     toggleSourceEdit : function(sourceEditMode){
25790         
25791         this.sourceEditMode = sourceEditMode === true;
25792         
25793         if(this.sourceEditMode){
25794  
25795             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25796             
25797         }else{
25798             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25799             //this.iframe.className = '';
25800             this.deferFocus();
25801         }
25802         //this.setSize(this.owner.wrap.getSize());
25803         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25804     },
25805
25806     
25807   
25808
25809     /**
25810      * Protected method that will not generally be called directly. If you need/want
25811      * custom HTML cleanup, this is the method you should override.
25812      * @param {String} html The HTML to be cleaned
25813      * return {String} The cleaned HTML
25814      */
25815     cleanHtml : function(html){
25816         html = String(html);
25817         if(html.length > 5){
25818             if(Roo.isSafari){ // strip safari nonsense
25819                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25820             }
25821         }
25822         if(html == '&nbsp;'){
25823             html = '';
25824         }
25825         return html;
25826     },
25827
25828     /**
25829      * HTML Editor -> Textarea
25830      * Protected method that will not generally be called directly. Syncs the contents
25831      * of the editor iframe with the textarea.
25832      */
25833     syncValue : function(){
25834         if(this.initialized){
25835             var bd = (this.doc.body || this.doc.documentElement);
25836             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25837             var html = bd.innerHTML;
25838             if(Roo.isSafari){
25839                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25840                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25841                 if(m && m[1]){
25842                     html = '<div style="'+m[0]+'">' + html + '</div>';
25843                 }
25844             }
25845             html = this.cleanHtml(html);
25846             // fix up the special chars.. normaly like back quotes in word...
25847             // however we do not want to do this with chinese..
25848             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25849                 
25850                 var cc = match.charCodeAt();
25851
25852                 // Get the character value, handling surrogate pairs
25853                 if (match.length == 2) {
25854                     // It's a surrogate pair, calculate the Unicode code point
25855                     var high = match.charCodeAt(0) - 0xD800;
25856                     var low  = match.charCodeAt(1) - 0xDC00;
25857                     cc = (high * 0x400) + low + 0x10000;
25858                 }  else if (
25859                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25860                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25861                     (cc >= 0xf900 && cc < 0xfb00 )
25862                 ) {
25863                         return match;
25864                 }  
25865          
25866                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25867                 return "&#" + cc + ";";
25868                 
25869                 
25870             });
25871             
25872             
25873              
25874             if(this.owner.fireEvent('beforesync', this, html) !== false){
25875                 this.el.dom.value = html;
25876                 this.owner.fireEvent('sync', this, html);
25877             }
25878         }
25879     },
25880
25881     /**
25882      * Protected method that will not generally be called directly. Pushes the value of the textarea
25883      * into the iframe editor.
25884      */
25885     pushValue : function(){
25886         if(this.initialized){
25887             var v = this.el.dom.value.trim();
25888             
25889 //            if(v.length < 1){
25890 //                v = '&#160;';
25891 //            }
25892             
25893             if(this.owner.fireEvent('beforepush', this, v) !== false){
25894                 var d = (this.doc.body || this.doc.documentElement);
25895                 d.innerHTML = v;
25896                 this.cleanUpPaste();
25897                 this.el.dom.value = d.innerHTML;
25898                 this.owner.fireEvent('push', this, v);
25899             }
25900         }
25901     },
25902
25903     // private
25904     deferFocus : function(){
25905         this.focus.defer(10, this);
25906     },
25907
25908     // doc'ed in Field
25909     focus : function(){
25910         if(this.win && !this.sourceEditMode){
25911             this.win.focus();
25912         }else{
25913             this.el.focus();
25914         }
25915     },
25916     
25917     assignDocWin: function()
25918     {
25919         var iframe = this.iframe;
25920         
25921          if(Roo.isIE){
25922             this.doc = iframe.contentWindow.document;
25923             this.win = iframe.contentWindow;
25924         } else {
25925 //            if (!Roo.get(this.frameId)) {
25926 //                return;
25927 //            }
25928 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25929 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25930             
25931             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25932                 return;
25933             }
25934             
25935             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25936             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25937         }
25938     },
25939     
25940     // private
25941     initEditor : function(){
25942         //console.log("INIT EDITOR");
25943         this.assignDocWin();
25944         
25945         
25946         
25947         this.doc.designMode="on";
25948         this.doc.open();
25949         this.doc.write(this.getDocMarkup());
25950         this.doc.close();
25951         
25952         var dbody = (this.doc.body || this.doc.documentElement);
25953         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25954         // this copies styles from the containing element into thsi one..
25955         // not sure why we need all of this..
25956         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25957         
25958         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25959         //ss['background-attachment'] = 'fixed'; // w3c
25960         dbody.bgProperties = 'fixed'; // ie
25961         //Roo.DomHelper.applyStyles(dbody, ss);
25962         Roo.EventManager.on(this.doc, {
25963             //'mousedown': this.onEditorEvent,
25964             'mouseup': this.onEditorEvent,
25965             'dblclick': this.onEditorEvent,
25966             'click': this.onEditorEvent,
25967             'keyup': this.onEditorEvent,
25968             buffer:100,
25969             scope: this
25970         });
25971         if(Roo.isGecko){
25972             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25973         }
25974         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25975             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25976         }
25977         this.initialized = true;
25978
25979         this.owner.fireEvent('initialize', this);
25980         this.pushValue();
25981     },
25982
25983     // private
25984     onDestroy : function(){
25985         
25986         
25987         
25988         if(this.rendered){
25989             
25990             //for (var i =0; i < this.toolbars.length;i++) {
25991             //    // fixme - ask toolbars for heights?
25992             //    this.toolbars[i].onDestroy();
25993            // }
25994             
25995             //this.wrap.dom.innerHTML = '';
25996             //this.wrap.remove();
25997         }
25998     },
25999
26000     // private
26001     onFirstFocus : function(){
26002         
26003         this.assignDocWin();
26004         
26005         
26006         this.activated = true;
26007          
26008     
26009         if(Roo.isGecko){ // prevent silly gecko errors
26010             this.win.focus();
26011             var s = this.win.getSelection();
26012             if(!s.focusNode || s.focusNode.nodeType != 3){
26013                 var r = s.getRangeAt(0);
26014                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26015                 r.collapse(true);
26016                 this.deferFocus();
26017             }
26018             try{
26019                 this.execCmd('useCSS', true);
26020                 this.execCmd('styleWithCSS', false);
26021             }catch(e){}
26022         }
26023         this.owner.fireEvent('activate', this);
26024     },
26025
26026     // private
26027     adjustFont: function(btn){
26028         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26029         //if(Roo.isSafari){ // safari
26030         //    adjust *= 2;
26031        // }
26032         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26033         if(Roo.isSafari){ // safari
26034             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26035             v =  (v < 10) ? 10 : v;
26036             v =  (v > 48) ? 48 : v;
26037             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26038             
26039         }
26040         
26041         
26042         v = Math.max(1, v+adjust);
26043         
26044         this.execCmd('FontSize', v  );
26045     },
26046
26047     onEditorEvent : function(e)
26048     {
26049         this.owner.fireEvent('editorevent', this, e);
26050       //  this.updateToolbar();
26051         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26052     },
26053
26054     insertTag : function(tg)
26055     {
26056         // could be a bit smarter... -> wrap the current selected tRoo..
26057         if (tg.toLowerCase() == 'span' ||
26058             tg.toLowerCase() == 'code' ||
26059             tg.toLowerCase() == 'sup' ||
26060             tg.toLowerCase() == 'sub' 
26061             ) {
26062             
26063             range = this.createRange(this.getSelection());
26064             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26065             wrappingNode.appendChild(range.extractContents());
26066             range.insertNode(wrappingNode);
26067
26068             return;
26069             
26070             
26071             
26072         }
26073         this.execCmd("formatblock",   tg);
26074         
26075     },
26076     
26077     insertText : function(txt)
26078     {
26079         
26080         
26081         var range = this.createRange();
26082         range.deleteContents();
26083                //alert(Sender.getAttribute('label'));
26084                
26085         range.insertNode(this.doc.createTextNode(txt));
26086     } ,
26087     
26088      
26089
26090     /**
26091      * Executes a Midas editor command on the editor document and performs necessary focus and
26092      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26093      * @param {String} cmd The Midas command
26094      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26095      */
26096     relayCmd : function(cmd, value){
26097         this.win.focus();
26098         this.execCmd(cmd, value);
26099         this.owner.fireEvent('editorevent', this);
26100         //this.updateToolbar();
26101         this.owner.deferFocus();
26102     },
26103
26104     /**
26105      * Executes a Midas editor command directly on the editor document.
26106      * For visual commands, you should use {@link #relayCmd} instead.
26107      * <b>This should only be called after the editor is initialized.</b>
26108      * @param {String} cmd The Midas command
26109      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26110      */
26111     execCmd : function(cmd, value){
26112         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26113         this.syncValue();
26114     },
26115  
26116  
26117    
26118     /**
26119      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26120      * to insert tRoo.
26121      * @param {String} text | dom node.. 
26122      */
26123     insertAtCursor : function(text)
26124     {
26125         
26126         if(!this.activated){
26127             return;
26128         }
26129         /*
26130         if(Roo.isIE){
26131             this.win.focus();
26132             var r = this.doc.selection.createRange();
26133             if(r){
26134                 r.collapse(true);
26135                 r.pasteHTML(text);
26136                 this.syncValue();
26137                 this.deferFocus();
26138             
26139             }
26140             return;
26141         }
26142         */
26143         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26144             this.win.focus();
26145             
26146             
26147             // from jquery ui (MIT licenced)
26148             var range, node;
26149             var win = this.win;
26150             
26151             if (win.getSelection && win.getSelection().getRangeAt) {
26152                 range = win.getSelection().getRangeAt(0);
26153                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26154                 range.insertNode(node);
26155             } else if (win.document.selection && win.document.selection.createRange) {
26156                 // no firefox support
26157                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26158                 win.document.selection.createRange().pasteHTML(txt);
26159             } else {
26160                 // no firefox support
26161                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26162                 this.execCmd('InsertHTML', txt);
26163             } 
26164             
26165             this.syncValue();
26166             
26167             this.deferFocus();
26168         }
26169     },
26170  // private
26171     mozKeyPress : function(e){
26172         if(e.ctrlKey){
26173             var c = e.getCharCode(), cmd;
26174           
26175             if(c > 0){
26176                 c = String.fromCharCode(c).toLowerCase();
26177                 switch(c){
26178                     case 'b':
26179                         cmd = 'bold';
26180                         break;
26181                     case 'i':
26182                         cmd = 'italic';
26183                         break;
26184                     
26185                     case 'u':
26186                         cmd = 'underline';
26187                         break;
26188                     
26189                     case 'v':
26190                         this.cleanUpPaste.defer(100, this);
26191                         return;
26192                         
26193                 }
26194                 if(cmd){
26195                     this.win.focus();
26196                     this.execCmd(cmd);
26197                     this.deferFocus();
26198                     e.preventDefault();
26199                 }
26200                 
26201             }
26202         }
26203     },
26204
26205     // private
26206     fixKeys : function(){ // load time branching for fastest keydown performance
26207         if(Roo.isIE){
26208             return function(e){
26209                 var k = e.getKey(), r;
26210                 if(k == e.TAB){
26211                     e.stopEvent();
26212                     r = this.doc.selection.createRange();
26213                     if(r){
26214                         r.collapse(true);
26215                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26216                         this.deferFocus();
26217                     }
26218                     return;
26219                 }
26220                 
26221                 if(k == e.ENTER){
26222                     r = this.doc.selection.createRange();
26223                     if(r){
26224                         var target = r.parentElement();
26225                         if(!target || target.tagName.toLowerCase() != 'li'){
26226                             e.stopEvent();
26227                             r.pasteHTML('<br />');
26228                             r.collapse(false);
26229                             r.select();
26230                         }
26231                     }
26232                 }
26233                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26234                     this.cleanUpPaste.defer(100, this);
26235                     return;
26236                 }
26237                 
26238                 
26239             };
26240         }else if(Roo.isOpera){
26241             return function(e){
26242                 var k = e.getKey();
26243                 if(k == e.TAB){
26244                     e.stopEvent();
26245                     this.win.focus();
26246                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26247                     this.deferFocus();
26248                 }
26249                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26250                     this.cleanUpPaste.defer(100, this);
26251                     return;
26252                 }
26253                 
26254             };
26255         }else if(Roo.isSafari){
26256             return function(e){
26257                 var k = e.getKey();
26258                 
26259                 if(k == e.TAB){
26260                     e.stopEvent();
26261                     this.execCmd('InsertText','\t');
26262                     this.deferFocus();
26263                     return;
26264                 }
26265                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26266                     this.cleanUpPaste.defer(100, this);
26267                     return;
26268                 }
26269                 
26270              };
26271         }
26272     }(),
26273     
26274     getAllAncestors: function()
26275     {
26276         var p = this.getSelectedNode();
26277         var a = [];
26278         if (!p) {
26279             a.push(p); // push blank onto stack..
26280             p = this.getParentElement();
26281         }
26282         
26283         
26284         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26285             a.push(p);
26286             p = p.parentNode;
26287         }
26288         a.push(this.doc.body);
26289         return a;
26290     },
26291     lastSel : false,
26292     lastSelNode : false,
26293     
26294     
26295     getSelection : function() 
26296     {
26297         this.assignDocWin();
26298         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26299     },
26300     
26301     getSelectedNode: function() 
26302     {
26303         // this may only work on Gecko!!!
26304         
26305         // should we cache this!!!!
26306         
26307         
26308         
26309          
26310         var range = this.createRange(this.getSelection()).cloneRange();
26311         
26312         if (Roo.isIE) {
26313             var parent = range.parentElement();
26314             while (true) {
26315                 var testRange = range.duplicate();
26316                 testRange.moveToElementText(parent);
26317                 if (testRange.inRange(range)) {
26318                     break;
26319                 }
26320                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26321                     break;
26322                 }
26323                 parent = parent.parentElement;
26324             }
26325             return parent;
26326         }
26327         
26328         // is ancestor a text element.
26329         var ac =  range.commonAncestorContainer;
26330         if (ac.nodeType == 3) {
26331             ac = ac.parentNode;
26332         }
26333         
26334         var ar = ac.childNodes;
26335          
26336         var nodes = [];
26337         var other_nodes = [];
26338         var has_other_nodes = false;
26339         for (var i=0;i<ar.length;i++) {
26340             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26341                 continue;
26342             }
26343             // fullly contained node.
26344             
26345             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26346                 nodes.push(ar[i]);
26347                 continue;
26348             }
26349             
26350             // probably selected..
26351             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26352                 other_nodes.push(ar[i]);
26353                 continue;
26354             }
26355             // outer..
26356             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26357                 continue;
26358             }
26359             
26360             
26361             has_other_nodes = true;
26362         }
26363         if (!nodes.length && other_nodes.length) {
26364             nodes= other_nodes;
26365         }
26366         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26367             return false;
26368         }
26369         
26370         return nodes[0];
26371     },
26372     createRange: function(sel)
26373     {
26374         // this has strange effects when using with 
26375         // top toolbar - not sure if it's a great idea.
26376         //this.editor.contentWindow.focus();
26377         if (typeof sel != "undefined") {
26378             try {
26379                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26380             } catch(e) {
26381                 return this.doc.createRange();
26382             }
26383         } else {
26384             return this.doc.createRange();
26385         }
26386     },
26387     getParentElement: function()
26388     {
26389         
26390         this.assignDocWin();
26391         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26392         
26393         var range = this.createRange(sel);
26394          
26395         try {
26396             var p = range.commonAncestorContainer;
26397             while (p.nodeType == 3) { // text node
26398                 p = p.parentNode;
26399             }
26400             return p;
26401         } catch (e) {
26402             return null;
26403         }
26404     
26405     },
26406     /***
26407      *
26408      * Range intersection.. the hard stuff...
26409      *  '-1' = before
26410      *  '0' = hits..
26411      *  '1' = after.
26412      *         [ -- selected range --- ]
26413      *   [fail]                        [fail]
26414      *
26415      *    basically..
26416      *      if end is before start or  hits it. fail.
26417      *      if start is after end or hits it fail.
26418      *
26419      *   if either hits (but other is outside. - then it's not 
26420      *   
26421      *    
26422      **/
26423     
26424     
26425     // @see http://www.thismuchiknow.co.uk/?p=64.
26426     rangeIntersectsNode : function(range, node)
26427     {
26428         var nodeRange = node.ownerDocument.createRange();
26429         try {
26430             nodeRange.selectNode(node);
26431         } catch (e) {
26432             nodeRange.selectNodeContents(node);
26433         }
26434     
26435         var rangeStartRange = range.cloneRange();
26436         rangeStartRange.collapse(true);
26437     
26438         var rangeEndRange = range.cloneRange();
26439         rangeEndRange.collapse(false);
26440     
26441         var nodeStartRange = nodeRange.cloneRange();
26442         nodeStartRange.collapse(true);
26443     
26444         var nodeEndRange = nodeRange.cloneRange();
26445         nodeEndRange.collapse(false);
26446     
26447         return rangeStartRange.compareBoundaryPoints(
26448                  Range.START_TO_START, nodeEndRange) == -1 &&
26449                rangeEndRange.compareBoundaryPoints(
26450                  Range.START_TO_START, nodeStartRange) == 1;
26451         
26452          
26453     },
26454     rangeCompareNode : function(range, node)
26455     {
26456         var nodeRange = node.ownerDocument.createRange();
26457         try {
26458             nodeRange.selectNode(node);
26459         } catch (e) {
26460             nodeRange.selectNodeContents(node);
26461         }
26462         
26463         
26464         range.collapse(true);
26465     
26466         nodeRange.collapse(true);
26467      
26468         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26469         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26470          
26471         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26472         
26473         var nodeIsBefore   =  ss == 1;
26474         var nodeIsAfter    = ee == -1;
26475         
26476         if (nodeIsBefore && nodeIsAfter) {
26477             return 0; // outer
26478         }
26479         if (!nodeIsBefore && nodeIsAfter) {
26480             return 1; //right trailed.
26481         }
26482         
26483         if (nodeIsBefore && !nodeIsAfter) {
26484             return 2;  // left trailed.
26485         }
26486         // fully contined.
26487         return 3;
26488     },
26489
26490     // private? - in a new class?
26491     cleanUpPaste :  function()
26492     {
26493         // cleans up the whole document..
26494         Roo.log('cleanuppaste');
26495         
26496         this.cleanUpChildren(this.doc.body);
26497         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26498         if (clean != this.doc.body.innerHTML) {
26499             this.doc.body.innerHTML = clean;
26500         }
26501         
26502     },
26503     
26504     cleanWordChars : function(input) {// change the chars to hex code
26505         var he = Roo.HtmlEditorCore;
26506         
26507         var output = input;
26508         Roo.each(he.swapCodes, function(sw) { 
26509             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26510             
26511             output = output.replace(swapper, sw[1]);
26512         });
26513         
26514         return output;
26515     },
26516     
26517     
26518     cleanUpChildren : function (n)
26519     {
26520         if (!n.childNodes.length) {
26521             return;
26522         }
26523         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26524            this.cleanUpChild(n.childNodes[i]);
26525         }
26526     },
26527     
26528     
26529         
26530     
26531     cleanUpChild : function (node)
26532     {
26533         var ed = this;
26534         //console.log(node);
26535         if (node.nodeName == "#text") {
26536             // clean up silly Windows -- stuff?
26537             return; 
26538         }
26539         if (node.nodeName == "#comment") {
26540             if (!this.allowComments) {
26541                 node.parentNode.removeChild(node);
26542             }
26543             // clean up silly Windows -- stuff?
26544             return; 
26545         }
26546         var lcname = node.tagName.toLowerCase();
26547         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26548         // whitelist of tags..
26549         
26550         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26551             // remove node.
26552             node.parentNode.removeChild(node);
26553             return;
26554             
26555         }
26556         
26557         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26558         
26559         // spans with no attributes - just remove them..
26560         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26561             remove_keep_children = true;
26562         }
26563         
26564         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26565         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26566         
26567         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26568         //    remove_keep_children = true;
26569         //}
26570         
26571         if (remove_keep_children) {
26572             this.cleanUpChildren(node);
26573             // inserts everything just before this node...
26574             while (node.childNodes.length) {
26575                 var cn = node.childNodes[0];
26576                 node.removeChild(cn);
26577                 node.parentNode.insertBefore(cn, node);
26578             }
26579             node.parentNode.removeChild(node);
26580             return;
26581         }
26582         
26583         if (!node.attributes || !node.attributes.length) {
26584             
26585           
26586             
26587             
26588             this.cleanUpChildren(node);
26589             return;
26590         }
26591         
26592         function cleanAttr(n,v)
26593         {
26594             
26595             if (v.match(/^\./) || v.match(/^\//)) {
26596                 return;
26597             }
26598             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26599                 return;
26600             }
26601             if (v.match(/^#/)) {
26602                 return;
26603             }
26604             if (v.match(/^\{/)) { // allow template editing.
26605                 return;
26606             }
26607 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26608             node.removeAttribute(n);
26609             
26610         }
26611         
26612         var cwhite = this.cwhite;
26613         var cblack = this.cblack;
26614             
26615         function cleanStyle(n,v)
26616         {
26617             if (v.match(/expression/)) { //XSS?? should we even bother..
26618                 node.removeAttribute(n);
26619                 return;
26620             }
26621             
26622             var parts = v.split(/;/);
26623             var clean = [];
26624             
26625             Roo.each(parts, function(p) {
26626                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26627                 if (!p.length) {
26628                     return true;
26629                 }
26630                 var l = p.split(':').shift().replace(/\s+/g,'');
26631                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26632                 
26633                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26634 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26635                     //node.removeAttribute(n);
26636                     return true;
26637                 }
26638                 //Roo.log()
26639                 // only allow 'c whitelisted system attributes'
26640                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26641 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26642                     //node.removeAttribute(n);
26643                     return true;
26644                 }
26645                 
26646                 
26647                  
26648                 
26649                 clean.push(p);
26650                 return true;
26651             });
26652             if (clean.length) { 
26653                 node.setAttribute(n, clean.join(';'));
26654             } else {
26655                 node.removeAttribute(n);
26656             }
26657             
26658         }
26659         
26660         
26661         for (var i = node.attributes.length-1; i > -1 ; i--) {
26662             var a = node.attributes[i];
26663             //console.log(a);
26664             
26665             if (a.name.toLowerCase().substr(0,2)=='on')  {
26666                 node.removeAttribute(a.name);
26667                 continue;
26668             }
26669             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26670                 node.removeAttribute(a.name);
26671                 continue;
26672             }
26673             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26674                 cleanAttr(a.name,a.value); // fixme..
26675                 continue;
26676             }
26677             if (a.name == 'style') {
26678                 cleanStyle(a.name,a.value);
26679                 continue;
26680             }
26681             /// clean up MS crap..
26682             // tecnically this should be a list of valid class'es..
26683             
26684             
26685             if (a.name == 'class') {
26686                 if (a.value.match(/^Mso/)) {
26687                     node.removeAttribute('class');
26688                 }
26689                 
26690                 if (a.value.match(/^body$/)) {
26691                     node.removeAttribute('class');
26692                 }
26693                 continue;
26694             }
26695             
26696             // style cleanup!?
26697             // class cleanup?
26698             
26699         }
26700         
26701         
26702         this.cleanUpChildren(node);
26703         
26704         
26705     },
26706     
26707     /**
26708      * Clean up MS wordisms...
26709      */
26710     cleanWord : function(node)
26711     {
26712         if (!node) {
26713             this.cleanWord(this.doc.body);
26714             return;
26715         }
26716         
26717         if(
26718                 node.nodeName == 'SPAN' &&
26719                 !node.hasAttributes() &&
26720                 node.childNodes.length == 1 &&
26721                 node.firstChild.nodeName == "#text"  
26722         ) {
26723             var textNode = node.firstChild;
26724             node.removeChild(textNode);
26725             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26726                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26727             }
26728             node.parentNode.insertBefore(textNode, node);
26729             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26730                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26731             }
26732             node.parentNode.removeChild(node);
26733         }
26734         
26735         if (node.nodeName == "#text") {
26736             // clean up silly Windows -- stuff?
26737             return; 
26738         }
26739         if (node.nodeName == "#comment") {
26740             node.parentNode.removeChild(node);
26741             // clean up silly Windows -- stuff?
26742             return; 
26743         }
26744         
26745         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26746             node.parentNode.removeChild(node);
26747             return;
26748         }
26749         //Roo.log(node.tagName);
26750         // remove - but keep children..
26751         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26752             //Roo.log('-- removed');
26753             while (node.childNodes.length) {
26754                 var cn = node.childNodes[0];
26755                 node.removeChild(cn);
26756                 node.parentNode.insertBefore(cn, node);
26757                 // move node to parent - and clean it..
26758                 this.cleanWord(cn);
26759             }
26760             node.parentNode.removeChild(node);
26761             /// no need to iterate chidlren = it's got none..
26762             //this.iterateChildren(node, this.cleanWord);
26763             return;
26764         }
26765         // clean styles
26766         if (node.className.length) {
26767             
26768             var cn = node.className.split(/\W+/);
26769             var cna = [];
26770             Roo.each(cn, function(cls) {
26771                 if (cls.match(/Mso[a-zA-Z]+/)) {
26772                     return;
26773                 }
26774                 cna.push(cls);
26775             });
26776             node.className = cna.length ? cna.join(' ') : '';
26777             if (!cna.length) {
26778                 node.removeAttribute("class");
26779             }
26780         }
26781         
26782         if (node.hasAttribute("lang")) {
26783             node.removeAttribute("lang");
26784         }
26785         
26786         if (node.hasAttribute("style")) {
26787             
26788             var styles = node.getAttribute("style").split(";");
26789             var nstyle = [];
26790             Roo.each(styles, function(s) {
26791                 if (!s.match(/:/)) {
26792                     return;
26793                 }
26794                 var kv = s.split(":");
26795                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26796                     return;
26797                 }
26798                 // what ever is left... we allow.
26799                 nstyle.push(s);
26800             });
26801             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26802             if (!nstyle.length) {
26803                 node.removeAttribute('style');
26804             }
26805         }
26806         this.iterateChildren(node, this.cleanWord);
26807         
26808         
26809         
26810     },
26811     /**
26812      * iterateChildren of a Node, calling fn each time, using this as the scole..
26813      * @param {DomNode} node node to iterate children of.
26814      * @param {Function} fn method of this class to call on each item.
26815      */
26816     iterateChildren : function(node, fn)
26817     {
26818         if (!node.childNodes.length) {
26819                 return;
26820         }
26821         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26822            fn.call(this, node.childNodes[i])
26823         }
26824     },
26825     
26826     
26827     /**
26828      * cleanTableWidths.
26829      *
26830      * Quite often pasting from word etc.. results in tables with column and widths.
26831      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26832      *
26833      */
26834     cleanTableWidths : function(node)
26835     {
26836          
26837          
26838         if (!node) {
26839             this.cleanTableWidths(this.doc.body);
26840             return;
26841         }
26842         
26843         // ignore list...
26844         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26845             return; 
26846         }
26847         Roo.log(node.tagName);
26848         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26849             this.iterateChildren(node, this.cleanTableWidths);
26850             return;
26851         }
26852         if (node.hasAttribute('width')) {
26853             node.removeAttribute('width');
26854         }
26855         
26856          
26857         if (node.hasAttribute("style")) {
26858             // pretty basic...
26859             
26860             var styles = node.getAttribute("style").split(";");
26861             var nstyle = [];
26862             Roo.each(styles, function(s) {
26863                 if (!s.match(/:/)) {
26864                     return;
26865                 }
26866                 var kv = s.split(":");
26867                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26868                     return;
26869                 }
26870                 // what ever is left... we allow.
26871                 nstyle.push(s);
26872             });
26873             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26874             if (!nstyle.length) {
26875                 node.removeAttribute('style');
26876             }
26877         }
26878         
26879         this.iterateChildren(node, this.cleanTableWidths);
26880         
26881         
26882     },
26883     
26884     
26885     
26886     
26887     domToHTML : function(currentElement, depth, nopadtext) {
26888         
26889         depth = depth || 0;
26890         nopadtext = nopadtext || false;
26891     
26892         if (!currentElement) {
26893             return this.domToHTML(this.doc.body);
26894         }
26895         
26896         //Roo.log(currentElement);
26897         var j;
26898         var allText = false;
26899         var nodeName = currentElement.nodeName;
26900         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26901         
26902         if  (nodeName == '#text') {
26903             
26904             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26905         }
26906         
26907         
26908         var ret = '';
26909         if (nodeName != 'BODY') {
26910              
26911             var i = 0;
26912             // Prints the node tagName, such as <A>, <IMG>, etc
26913             if (tagName) {
26914                 var attr = [];
26915                 for(i = 0; i < currentElement.attributes.length;i++) {
26916                     // quoting?
26917                     var aname = currentElement.attributes.item(i).name;
26918                     if (!currentElement.attributes.item(i).value.length) {
26919                         continue;
26920                     }
26921                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26922                 }
26923                 
26924                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26925             } 
26926             else {
26927                 
26928                 // eack
26929             }
26930         } else {
26931             tagName = false;
26932         }
26933         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26934             return ret;
26935         }
26936         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26937             nopadtext = true;
26938         }
26939         
26940         
26941         // Traverse the tree
26942         i = 0;
26943         var currentElementChild = currentElement.childNodes.item(i);
26944         var allText = true;
26945         var innerHTML  = '';
26946         lastnode = '';
26947         while (currentElementChild) {
26948             // Formatting code (indent the tree so it looks nice on the screen)
26949             var nopad = nopadtext;
26950             if (lastnode == 'SPAN') {
26951                 nopad  = true;
26952             }
26953             // text
26954             if  (currentElementChild.nodeName == '#text') {
26955                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26956                 toadd = nopadtext ? toadd : toadd.trim();
26957                 if (!nopad && toadd.length > 80) {
26958                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26959                 }
26960                 innerHTML  += toadd;
26961                 
26962                 i++;
26963                 currentElementChild = currentElement.childNodes.item(i);
26964                 lastNode = '';
26965                 continue;
26966             }
26967             allText = false;
26968             
26969             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26970                 
26971             // Recursively traverse the tree structure of the child node
26972             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26973             lastnode = currentElementChild.nodeName;
26974             i++;
26975             currentElementChild=currentElement.childNodes.item(i);
26976         }
26977         
26978         ret += innerHTML;
26979         
26980         if (!allText) {
26981                 // The remaining code is mostly for formatting the tree
26982             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26983         }
26984         
26985         
26986         if (tagName) {
26987             ret+= "</"+tagName+">";
26988         }
26989         return ret;
26990         
26991     },
26992         
26993     applyBlacklists : function()
26994     {
26995         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26996         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26997         
26998         this.white = [];
26999         this.black = [];
27000         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27001             if (b.indexOf(tag) > -1) {
27002                 return;
27003             }
27004             this.white.push(tag);
27005             
27006         }, this);
27007         
27008         Roo.each(w, function(tag) {
27009             if (b.indexOf(tag) > -1) {
27010                 return;
27011             }
27012             if (this.white.indexOf(tag) > -1) {
27013                 return;
27014             }
27015             this.white.push(tag);
27016             
27017         }, this);
27018         
27019         
27020         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27021             if (w.indexOf(tag) > -1) {
27022                 return;
27023             }
27024             this.black.push(tag);
27025             
27026         }, this);
27027         
27028         Roo.each(b, function(tag) {
27029             if (w.indexOf(tag) > -1) {
27030                 return;
27031             }
27032             if (this.black.indexOf(tag) > -1) {
27033                 return;
27034             }
27035             this.black.push(tag);
27036             
27037         }, this);
27038         
27039         
27040         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27041         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27042         
27043         this.cwhite = [];
27044         this.cblack = [];
27045         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27046             if (b.indexOf(tag) > -1) {
27047                 return;
27048             }
27049             this.cwhite.push(tag);
27050             
27051         }, this);
27052         
27053         Roo.each(w, function(tag) {
27054             if (b.indexOf(tag) > -1) {
27055                 return;
27056             }
27057             if (this.cwhite.indexOf(tag) > -1) {
27058                 return;
27059             }
27060             this.cwhite.push(tag);
27061             
27062         }, this);
27063         
27064         
27065         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27066             if (w.indexOf(tag) > -1) {
27067                 return;
27068             }
27069             this.cblack.push(tag);
27070             
27071         }, this);
27072         
27073         Roo.each(b, function(tag) {
27074             if (w.indexOf(tag) > -1) {
27075                 return;
27076             }
27077             if (this.cblack.indexOf(tag) > -1) {
27078                 return;
27079             }
27080             this.cblack.push(tag);
27081             
27082         }, this);
27083     },
27084     
27085     setStylesheets : function(stylesheets)
27086     {
27087         if(typeof(stylesheets) == 'string'){
27088             Roo.get(this.iframe.contentDocument.head).createChild({
27089                 tag : 'link',
27090                 rel : 'stylesheet',
27091                 type : 'text/css',
27092                 href : stylesheets
27093             });
27094             
27095             return;
27096         }
27097         var _this = this;
27098      
27099         Roo.each(stylesheets, function(s) {
27100             if(!s.length){
27101                 return;
27102             }
27103             
27104             Roo.get(_this.iframe.contentDocument.head).createChild({
27105                 tag : 'link',
27106                 rel : 'stylesheet',
27107                 type : 'text/css',
27108                 href : s
27109             });
27110         });
27111
27112         
27113     },
27114     
27115     removeStylesheets : function()
27116     {
27117         var _this = this;
27118         
27119         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27120             s.remove();
27121         });
27122     },
27123     
27124     setStyle : function(style)
27125     {
27126         Roo.get(this.iframe.contentDocument.head).createChild({
27127             tag : 'style',
27128             type : 'text/css',
27129             html : style
27130         });
27131
27132         return;
27133     }
27134     
27135     // hide stuff that is not compatible
27136     /**
27137      * @event blur
27138      * @hide
27139      */
27140     /**
27141      * @event change
27142      * @hide
27143      */
27144     /**
27145      * @event focus
27146      * @hide
27147      */
27148     /**
27149      * @event specialkey
27150      * @hide
27151      */
27152     /**
27153      * @cfg {String} fieldClass @hide
27154      */
27155     /**
27156      * @cfg {String} focusClass @hide
27157      */
27158     /**
27159      * @cfg {String} autoCreate @hide
27160      */
27161     /**
27162      * @cfg {String} inputType @hide
27163      */
27164     /**
27165      * @cfg {String} invalidClass @hide
27166      */
27167     /**
27168      * @cfg {String} invalidText @hide
27169      */
27170     /**
27171      * @cfg {String} msgFx @hide
27172      */
27173     /**
27174      * @cfg {String} validateOnBlur @hide
27175      */
27176 });
27177
27178 Roo.HtmlEditorCore.white = [
27179         'area', 'br', 'img', 'input', 'hr', 'wbr',
27180         
27181        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27182        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27183        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27184        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27185        'table',   'ul',         'xmp', 
27186        
27187        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27188       'thead',   'tr', 
27189      
27190       'dir', 'menu', 'ol', 'ul', 'dl',
27191        
27192       'embed',  'object'
27193 ];
27194
27195
27196 Roo.HtmlEditorCore.black = [
27197     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27198         'applet', // 
27199         'base',   'basefont', 'bgsound', 'blink',  'body', 
27200         'frame',  'frameset', 'head',    'html',   'ilayer', 
27201         'iframe', 'layer',  'link',     'meta',    'object',   
27202         'script', 'style' ,'title',  'xml' // clean later..
27203 ];
27204 Roo.HtmlEditorCore.clean = [
27205     'script', 'style', 'title', 'xml'
27206 ];
27207 Roo.HtmlEditorCore.remove = [
27208     'font'
27209 ];
27210 // attributes..
27211
27212 Roo.HtmlEditorCore.ablack = [
27213     'on'
27214 ];
27215     
27216 Roo.HtmlEditorCore.aclean = [ 
27217     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27218 ];
27219
27220 // protocols..
27221 Roo.HtmlEditorCore.pwhite= [
27222         'http',  'https',  'mailto'
27223 ];
27224
27225 // white listed style attributes.
27226 Roo.HtmlEditorCore.cwhite= [
27227       //  'text-align', /// default is to allow most things..
27228       
27229          
27230 //        'font-size'//??
27231 ];
27232
27233 // black listed style attributes.
27234 Roo.HtmlEditorCore.cblack= [
27235       //  'font-size' -- this can be set by the project 
27236 ];
27237
27238
27239 Roo.HtmlEditorCore.swapCodes   =[ 
27240     [    8211, "&#8211;" ], 
27241     [    8212, "&#8212;" ], 
27242     [    8216,  "'" ],  
27243     [    8217, "'" ],  
27244     [    8220, '"' ],  
27245     [    8221, '"' ],  
27246     [    8226, "*" ],  
27247     [    8230, "..." ]
27248 ]; 
27249
27250     /*
27251  * - LGPL
27252  *
27253  * HtmlEditor
27254  * 
27255  */
27256
27257 /**
27258  * @class Roo.bootstrap.HtmlEditor
27259  * @extends Roo.bootstrap.TextArea
27260  * Bootstrap HtmlEditor class
27261
27262  * @constructor
27263  * Create a new HtmlEditor
27264  * @param {Object} config The config object
27265  */
27266
27267 Roo.bootstrap.HtmlEditor = function(config){
27268     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27269     if (!this.toolbars) {
27270         this.toolbars = [];
27271     }
27272     
27273     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27274     this.addEvents({
27275             /**
27276              * @event initialize
27277              * Fires when the editor is fully initialized (including the iframe)
27278              * @param {HtmlEditor} this
27279              */
27280             initialize: true,
27281             /**
27282              * @event activate
27283              * Fires when the editor is first receives the focus. Any insertion must wait
27284              * until after this event.
27285              * @param {HtmlEditor} this
27286              */
27287             activate: true,
27288              /**
27289              * @event beforesync
27290              * Fires before the textarea is updated with content from the editor iframe. Return false
27291              * to cancel the sync.
27292              * @param {HtmlEditor} this
27293              * @param {String} html
27294              */
27295             beforesync: true,
27296              /**
27297              * @event beforepush
27298              * Fires before the iframe editor is updated with content from the textarea. Return false
27299              * to cancel the push.
27300              * @param {HtmlEditor} this
27301              * @param {String} html
27302              */
27303             beforepush: true,
27304              /**
27305              * @event sync
27306              * Fires when the textarea is updated with content from the editor iframe.
27307              * @param {HtmlEditor} this
27308              * @param {String} html
27309              */
27310             sync: true,
27311              /**
27312              * @event push
27313              * Fires when the iframe editor is updated with content from the textarea.
27314              * @param {HtmlEditor} this
27315              * @param {String} html
27316              */
27317             push: true,
27318              /**
27319              * @event editmodechange
27320              * Fires when the editor switches edit modes
27321              * @param {HtmlEditor} this
27322              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27323              */
27324             editmodechange: true,
27325             /**
27326              * @event editorevent
27327              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27328              * @param {HtmlEditor} this
27329              */
27330             editorevent: true,
27331             /**
27332              * @event firstfocus
27333              * Fires when on first focus - needed by toolbars..
27334              * @param {HtmlEditor} this
27335              */
27336             firstfocus: true,
27337             /**
27338              * @event autosave
27339              * Auto save the htmlEditor value as a file into Events
27340              * @param {HtmlEditor} this
27341              */
27342             autosave: true,
27343             /**
27344              * @event savedpreview
27345              * preview the saved version of htmlEditor
27346              * @param {HtmlEditor} this
27347              */
27348             savedpreview: true
27349         });
27350 };
27351
27352
27353 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27354     
27355     
27356       /**
27357      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27358      */
27359     toolbars : false,
27360     
27361      /**
27362     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27363     */
27364     btns : [],
27365    
27366      /**
27367      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27368      *                        Roo.resizable.
27369      */
27370     resizable : false,
27371      /**
27372      * @cfg {Number} height (in pixels)
27373      */   
27374     height: 300,
27375    /**
27376      * @cfg {Number} width (in pixels)
27377      */   
27378     width: false,
27379     
27380     /**
27381      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27382      * 
27383      */
27384     stylesheets: false,
27385     
27386     // id of frame..
27387     frameId: false,
27388     
27389     // private properties
27390     validationEvent : false,
27391     deferHeight: true,
27392     initialized : false,
27393     activated : false,
27394     
27395     onFocus : Roo.emptyFn,
27396     iframePad:3,
27397     hideMode:'offsets',
27398     
27399     tbContainer : false,
27400     
27401     bodyCls : '',
27402     
27403     toolbarContainer :function() {
27404         return this.wrap.select('.x-html-editor-tb',true).first();
27405     },
27406
27407     /**
27408      * Protected method that will not generally be called directly. It
27409      * is called when the editor creates its toolbar. Override this method if you need to
27410      * add custom toolbar buttons.
27411      * @param {HtmlEditor} editor
27412      */
27413     createToolbar : function(){
27414         Roo.log('renewing');
27415         Roo.log("create toolbars");
27416         
27417         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27418         this.toolbars[0].render(this.toolbarContainer());
27419         
27420         return;
27421         
27422 //        if (!editor.toolbars || !editor.toolbars.length) {
27423 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27424 //        }
27425 //        
27426 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27427 //            editor.toolbars[i] = Roo.factory(
27428 //                    typeof(editor.toolbars[i]) == 'string' ?
27429 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27430 //                Roo.bootstrap.HtmlEditor);
27431 //            editor.toolbars[i].init(editor);
27432 //        }
27433     },
27434
27435      
27436     // private
27437     onRender : function(ct, position)
27438     {
27439        // Roo.log("Call onRender: " + this.xtype);
27440         var _t = this;
27441         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27442       
27443         this.wrap = this.inputEl().wrap({
27444             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27445         });
27446         
27447         this.editorcore.onRender(ct, position);
27448          
27449         if (this.resizable) {
27450             this.resizeEl = new Roo.Resizable(this.wrap, {
27451                 pinned : true,
27452                 wrap: true,
27453                 dynamic : true,
27454                 minHeight : this.height,
27455                 height: this.height,
27456                 handles : this.resizable,
27457                 width: this.width,
27458                 listeners : {
27459                     resize : function(r, w, h) {
27460                         _t.onResize(w,h); // -something
27461                     }
27462                 }
27463             });
27464             
27465         }
27466         this.createToolbar(this);
27467        
27468         
27469         if(!this.width && this.resizable){
27470             this.setSize(this.wrap.getSize());
27471         }
27472         if (this.resizeEl) {
27473             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27474             // should trigger onReize..
27475         }
27476         
27477     },
27478
27479     // private
27480     onResize : function(w, h)
27481     {
27482         Roo.log('resize: ' +w + ',' + h );
27483         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27484         var ew = false;
27485         var eh = false;
27486         
27487         if(this.inputEl() ){
27488             if(typeof w == 'number'){
27489                 var aw = w - this.wrap.getFrameWidth('lr');
27490                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27491                 ew = aw;
27492             }
27493             if(typeof h == 'number'){
27494                  var tbh = -11;  // fixme it needs to tool bar size!
27495                 for (var i =0; i < this.toolbars.length;i++) {
27496                     // fixme - ask toolbars for heights?
27497                     tbh += this.toolbars[i].el.getHeight();
27498                     //if (this.toolbars[i].footer) {
27499                     //    tbh += this.toolbars[i].footer.el.getHeight();
27500                     //}
27501                 }
27502               
27503                 
27504                 
27505                 
27506                 
27507                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27508                 ah -= 5; // knock a few pixes off for look..
27509                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27510                 var eh = ah;
27511             }
27512         }
27513         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27514         this.editorcore.onResize(ew,eh);
27515         
27516     },
27517
27518     /**
27519      * Toggles the editor between standard and source edit mode.
27520      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27521      */
27522     toggleSourceEdit : function(sourceEditMode)
27523     {
27524         this.editorcore.toggleSourceEdit(sourceEditMode);
27525         
27526         if(this.editorcore.sourceEditMode){
27527             Roo.log('editor - showing textarea');
27528             
27529 //            Roo.log('in');
27530 //            Roo.log(this.syncValue());
27531             this.syncValue();
27532             this.inputEl().removeClass(['hide', 'x-hidden']);
27533             this.inputEl().dom.removeAttribute('tabIndex');
27534             this.inputEl().focus();
27535         }else{
27536             Roo.log('editor - hiding textarea');
27537 //            Roo.log('out')
27538 //            Roo.log(this.pushValue()); 
27539             this.pushValue();
27540             
27541             this.inputEl().addClass(['hide', 'x-hidden']);
27542             this.inputEl().dom.setAttribute('tabIndex', -1);
27543             //this.deferFocus();
27544         }
27545          
27546         if(this.resizable){
27547             this.setSize(this.wrap.getSize());
27548         }
27549         
27550         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27551     },
27552  
27553     // private (for BoxComponent)
27554     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27555
27556     // private (for BoxComponent)
27557     getResizeEl : function(){
27558         return this.wrap;
27559     },
27560
27561     // private (for BoxComponent)
27562     getPositionEl : function(){
27563         return this.wrap;
27564     },
27565
27566     // private
27567     initEvents : function(){
27568         this.originalValue = this.getValue();
27569     },
27570
27571 //    /**
27572 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27573 //     * @method
27574 //     */
27575 //    markInvalid : Roo.emptyFn,
27576 //    /**
27577 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27578 //     * @method
27579 //     */
27580 //    clearInvalid : Roo.emptyFn,
27581
27582     setValue : function(v){
27583         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27584         this.editorcore.pushValue();
27585     },
27586
27587      
27588     // private
27589     deferFocus : function(){
27590         this.focus.defer(10, this);
27591     },
27592
27593     // doc'ed in Field
27594     focus : function(){
27595         this.editorcore.focus();
27596         
27597     },
27598       
27599
27600     // private
27601     onDestroy : function(){
27602         
27603         
27604         
27605         if(this.rendered){
27606             
27607             for (var i =0; i < this.toolbars.length;i++) {
27608                 // fixme - ask toolbars for heights?
27609                 this.toolbars[i].onDestroy();
27610             }
27611             
27612             this.wrap.dom.innerHTML = '';
27613             this.wrap.remove();
27614         }
27615     },
27616
27617     // private
27618     onFirstFocus : function(){
27619         //Roo.log("onFirstFocus");
27620         this.editorcore.onFirstFocus();
27621          for (var i =0; i < this.toolbars.length;i++) {
27622             this.toolbars[i].onFirstFocus();
27623         }
27624         
27625     },
27626     
27627     // private
27628     syncValue : function()
27629     {   
27630         this.editorcore.syncValue();
27631     },
27632     
27633     pushValue : function()
27634     {   
27635         this.editorcore.pushValue();
27636     }
27637      
27638     
27639     // hide stuff that is not compatible
27640     /**
27641      * @event blur
27642      * @hide
27643      */
27644     /**
27645      * @event change
27646      * @hide
27647      */
27648     /**
27649      * @event focus
27650      * @hide
27651      */
27652     /**
27653      * @event specialkey
27654      * @hide
27655      */
27656     /**
27657      * @cfg {String} fieldClass @hide
27658      */
27659     /**
27660      * @cfg {String} focusClass @hide
27661      */
27662     /**
27663      * @cfg {String} autoCreate @hide
27664      */
27665     /**
27666      * @cfg {String} inputType @hide
27667      */
27668      
27669     /**
27670      * @cfg {String} invalidText @hide
27671      */
27672     /**
27673      * @cfg {String} msgFx @hide
27674      */
27675     /**
27676      * @cfg {String} validateOnBlur @hide
27677      */
27678 });
27679  
27680     
27681    
27682    
27683    
27684       
27685 Roo.namespace('Roo.bootstrap.htmleditor');
27686 /**
27687  * @class Roo.bootstrap.HtmlEditorToolbar1
27688  * Basic Toolbar
27689  * 
27690  * @example
27691  * Usage:
27692  *
27693  new Roo.bootstrap.HtmlEditor({
27694     ....
27695     toolbars : [
27696         new Roo.bootstrap.HtmlEditorToolbar1({
27697             disable : { fonts: 1 , format: 1, ..., ... , ...],
27698             btns : [ .... ]
27699         })
27700     }
27701      
27702  * 
27703  * @cfg {Object} disable List of elements to disable..
27704  * @cfg {Array} btns List of additional buttons.
27705  * 
27706  * 
27707  * NEEDS Extra CSS? 
27708  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27709  */
27710  
27711 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27712 {
27713     
27714     Roo.apply(this, config);
27715     
27716     // default disabled, based on 'good practice'..
27717     this.disable = this.disable || {};
27718     Roo.applyIf(this.disable, {
27719         fontSize : true,
27720         colors : true,
27721         specialElements : true
27722     });
27723     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27724     
27725     this.editor = config.editor;
27726     this.editorcore = config.editor.editorcore;
27727     
27728     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27729     
27730     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27731     // dont call parent... till later.
27732 }
27733 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27734      
27735     bar : true,
27736     
27737     editor : false,
27738     editorcore : false,
27739     
27740     
27741     formats : [
27742         "p" ,  
27743         "h1","h2","h3","h4","h5","h6", 
27744         "pre", "code", 
27745         "abbr", "acronym", "address", "cite", "samp", "var",
27746         'div','span'
27747     ],
27748     
27749     onRender : function(ct, position)
27750     {
27751        // Roo.log("Call onRender: " + this.xtype);
27752         
27753        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27754        Roo.log(this.el);
27755        this.el.dom.style.marginBottom = '0';
27756        var _this = this;
27757        var editorcore = this.editorcore;
27758        var editor= this.editor;
27759        
27760        var children = [];
27761        var btn = function(id,cmd , toggle, handler, html){
27762        
27763             var  event = toggle ? 'toggle' : 'click';
27764        
27765             var a = {
27766                 size : 'sm',
27767                 xtype: 'Button',
27768                 xns: Roo.bootstrap,
27769                 //glyphicon : id,
27770                 fa: id,
27771                 cmd : id || cmd,
27772                 enableToggle:toggle !== false,
27773                 html : html || '',
27774                 pressed : toggle ? false : null,
27775                 listeners : {}
27776             };
27777             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27778                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27779             };
27780             children.push(a);
27781             return a;
27782        }
27783        
27784     //    var cb_box = function...
27785         
27786         var style = {
27787                 xtype: 'Button',
27788                 size : 'sm',
27789                 xns: Roo.bootstrap,
27790                 fa : 'font',
27791                 //html : 'submit'
27792                 menu : {
27793                     xtype: 'Menu',
27794                     xns: Roo.bootstrap,
27795                     items:  []
27796                 }
27797         };
27798         Roo.each(this.formats, function(f) {
27799             style.menu.items.push({
27800                 xtype :'MenuItem',
27801                 xns: Roo.bootstrap,
27802                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27803                 tagname : f,
27804                 listeners : {
27805                     click : function()
27806                     {
27807                         editorcore.insertTag(this.tagname);
27808                         editor.focus();
27809                     }
27810                 }
27811                 
27812             });
27813         });
27814         children.push(style);   
27815         
27816         btn('bold',false,true);
27817         btn('italic',false,true);
27818         btn('align-left', 'justifyleft',true);
27819         btn('align-center', 'justifycenter',true);
27820         btn('align-right' , 'justifyright',true);
27821         btn('link', false, false, function(btn) {
27822             //Roo.log("create link?");
27823             var url = prompt(this.createLinkText, this.defaultLinkValue);
27824             if(url && url != 'http:/'+'/'){
27825                 this.editorcore.relayCmd('createlink', url);
27826             }
27827         }),
27828         btn('list','insertunorderedlist',true);
27829         btn('pencil', false,true, function(btn){
27830                 Roo.log(this);
27831                 this.toggleSourceEdit(btn.pressed);
27832         });
27833         
27834         if (this.editor.btns.length > 0) {
27835             for (var i = 0; i<this.editor.btns.length; i++) {
27836                 children.push(this.editor.btns[i]);
27837             }
27838         }
27839         
27840         /*
27841         var cog = {
27842                 xtype: 'Button',
27843                 size : 'sm',
27844                 xns: Roo.bootstrap,
27845                 glyphicon : 'cog',
27846                 //html : 'submit'
27847                 menu : {
27848                     xtype: 'Menu',
27849                     xns: Roo.bootstrap,
27850                     items:  []
27851                 }
27852         };
27853         
27854         cog.menu.items.push({
27855             xtype :'MenuItem',
27856             xns: Roo.bootstrap,
27857             html : Clean styles,
27858             tagname : f,
27859             listeners : {
27860                 click : function()
27861                 {
27862                     editorcore.insertTag(this.tagname);
27863                     editor.focus();
27864                 }
27865             }
27866             
27867         });
27868        */
27869         
27870          
27871        this.xtype = 'NavSimplebar';
27872         
27873         for(var i=0;i< children.length;i++) {
27874             
27875             this.buttons.add(this.addxtypeChild(children[i]));
27876             
27877         }
27878         
27879         editor.on('editorevent', this.updateToolbar, this);
27880     },
27881     onBtnClick : function(id)
27882     {
27883        this.editorcore.relayCmd(id);
27884        this.editorcore.focus();
27885     },
27886     
27887     /**
27888      * Protected method that will not generally be called directly. It triggers
27889      * a toolbar update by reading the markup state of the current selection in the editor.
27890      */
27891     updateToolbar: function(){
27892
27893         if(!this.editorcore.activated){
27894             this.editor.onFirstFocus(); // is this neeed?
27895             return;
27896         }
27897
27898         var btns = this.buttons; 
27899         var doc = this.editorcore.doc;
27900         btns.get('bold').setActive(doc.queryCommandState('bold'));
27901         btns.get('italic').setActive(doc.queryCommandState('italic'));
27902         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27903         
27904         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27905         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27906         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27907         
27908         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27909         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27910          /*
27911         
27912         var ans = this.editorcore.getAllAncestors();
27913         if (this.formatCombo) {
27914             
27915             
27916             var store = this.formatCombo.store;
27917             this.formatCombo.setValue("");
27918             for (var i =0; i < ans.length;i++) {
27919                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27920                     // select it..
27921                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27922                     break;
27923                 }
27924             }
27925         }
27926         
27927         
27928         
27929         // hides menus... - so this cant be on a menu...
27930         Roo.bootstrap.MenuMgr.hideAll();
27931         */
27932         Roo.bootstrap.MenuMgr.hideAll();
27933         //this.editorsyncValue();
27934     },
27935     onFirstFocus: function() {
27936         this.buttons.each(function(item){
27937            item.enable();
27938         });
27939     },
27940     toggleSourceEdit : function(sourceEditMode){
27941         
27942           
27943         if(sourceEditMode){
27944             Roo.log("disabling buttons");
27945            this.buttons.each( function(item){
27946                 if(item.cmd != 'pencil'){
27947                     item.disable();
27948                 }
27949             });
27950           
27951         }else{
27952             Roo.log("enabling buttons");
27953             if(this.editorcore.initialized){
27954                 this.buttons.each( function(item){
27955                     item.enable();
27956                 });
27957             }
27958             
27959         }
27960         Roo.log("calling toggole on editor");
27961         // tell the editor that it's been pressed..
27962         this.editor.toggleSourceEdit(sourceEditMode);
27963        
27964     }
27965 });
27966
27967
27968
27969
27970  
27971 /*
27972  * - LGPL
27973  */
27974
27975 /**
27976  * @class Roo.bootstrap.Markdown
27977  * @extends Roo.bootstrap.TextArea
27978  * Bootstrap Showdown editable area
27979  * @cfg {string} content
27980  * 
27981  * @constructor
27982  * Create a new Showdown
27983  */
27984
27985 Roo.bootstrap.Markdown = function(config){
27986     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27987    
27988 };
27989
27990 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27991     
27992     editing :false,
27993     
27994     initEvents : function()
27995     {
27996         
27997         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27998         this.markdownEl = this.el.createChild({
27999             cls : 'roo-markdown-area'
28000         });
28001         this.inputEl().addClass('d-none');
28002         if (this.getValue() == '') {
28003             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28004             
28005         } else {
28006             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28007         }
28008         this.markdownEl.on('click', this.toggleTextEdit, this);
28009         this.on('blur', this.toggleTextEdit, this);
28010         this.on('specialkey', this.resizeTextArea, this);
28011     },
28012     
28013     toggleTextEdit : function()
28014     {
28015         var sh = this.markdownEl.getHeight();
28016         this.inputEl().addClass('d-none');
28017         this.markdownEl.addClass('d-none');
28018         if (!this.editing) {
28019             // show editor?
28020             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28021             this.inputEl().removeClass('d-none');
28022             this.inputEl().focus();
28023             this.editing = true;
28024             return;
28025         }
28026         // show showdown...
28027         this.updateMarkdown();
28028         this.markdownEl.removeClass('d-none');
28029         this.editing = false;
28030         return;
28031     },
28032     updateMarkdown : function()
28033     {
28034         if (this.getValue() == '') {
28035             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28036             return;
28037         }
28038  
28039         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28040     },
28041     
28042     resizeTextArea: function () {
28043         
28044         var sh = 100;
28045         Roo.log([sh, this.getValue().split("\n").length * 30]);
28046         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28047     },
28048     setValue : function(val)
28049     {
28050         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28051         if (!this.editing) {
28052             this.updateMarkdown();
28053         }
28054         
28055     },
28056     focus : function()
28057     {
28058         if (!this.editing) {
28059             this.toggleTextEdit();
28060         }
28061         
28062     }
28063
28064
28065 });/*
28066  * Based on:
28067  * Ext JS Library 1.1.1
28068  * Copyright(c) 2006-2007, Ext JS, LLC.
28069  *
28070  * Originally Released Under LGPL - original licence link has changed is not relivant.
28071  *
28072  * Fork - LGPL
28073  * <script type="text/javascript">
28074  */
28075  
28076 /**
28077  * @class Roo.bootstrap.PagingToolbar
28078  * @extends Roo.bootstrap.NavSimplebar
28079  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28080  * @constructor
28081  * Create a new PagingToolbar
28082  * @param {Object} config The config object
28083  * @param {Roo.data.Store} store
28084  */
28085 Roo.bootstrap.PagingToolbar = function(config)
28086 {
28087     // old args format still supported... - xtype is prefered..
28088         // created from xtype...
28089     
28090     this.ds = config.dataSource;
28091     
28092     if (config.store && !this.ds) {
28093         this.store= Roo.factory(config.store, Roo.data);
28094         this.ds = this.store;
28095         this.ds.xmodule = this.xmodule || false;
28096     }
28097     
28098     this.toolbarItems = [];
28099     if (config.items) {
28100         this.toolbarItems = config.items;
28101     }
28102     
28103     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28104     
28105     this.cursor = 0;
28106     
28107     if (this.ds) { 
28108         this.bind(this.ds);
28109     }
28110     
28111     if (Roo.bootstrap.version == 4) {
28112         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28113     } else {
28114         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28115     }
28116     
28117 };
28118
28119 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28120     /**
28121      * @cfg {Roo.data.Store} dataSource
28122      * The underlying data store providing the paged data
28123      */
28124     /**
28125      * @cfg {String/HTMLElement/Element} container
28126      * container The id or element that will contain the toolbar
28127      */
28128     /**
28129      * @cfg {Boolean} displayInfo
28130      * True to display the displayMsg (defaults to false)
28131      */
28132     /**
28133      * @cfg {Number} pageSize
28134      * The number of records to display per page (defaults to 20)
28135      */
28136     pageSize: 20,
28137     /**
28138      * @cfg {String} displayMsg
28139      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28140      */
28141     displayMsg : 'Displaying {0} - {1} of {2}',
28142     /**
28143      * @cfg {String} emptyMsg
28144      * The message to display when no records are found (defaults to "No data to display")
28145      */
28146     emptyMsg : 'No data to display',
28147     /**
28148      * Customizable piece of the default paging text (defaults to "Page")
28149      * @type String
28150      */
28151     beforePageText : "Page",
28152     /**
28153      * Customizable piece of the default paging text (defaults to "of %0")
28154      * @type String
28155      */
28156     afterPageText : "of {0}",
28157     /**
28158      * Customizable piece of the default paging text (defaults to "First Page")
28159      * @type String
28160      */
28161     firstText : "First Page",
28162     /**
28163      * Customizable piece of the default paging text (defaults to "Previous Page")
28164      * @type String
28165      */
28166     prevText : "Previous Page",
28167     /**
28168      * Customizable piece of the default paging text (defaults to "Next Page")
28169      * @type String
28170      */
28171     nextText : "Next Page",
28172     /**
28173      * Customizable piece of the default paging text (defaults to "Last Page")
28174      * @type String
28175      */
28176     lastText : "Last Page",
28177     /**
28178      * Customizable piece of the default paging text (defaults to "Refresh")
28179      * @type String
28180      */
28181     refreshText : "Refresh",
28182
28183     buttons : false,
28184     // private
28185     onRender : function(ct, position) 
28186     {
28187         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28188         this.navgroup.parentId = this.id;
28189         this.navgroup.onRender(this.el, null);
28190         // add the buttons to the navgroup
28191         
28192         if(this.displayInfo){
28193             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28194             this.displayEl = this.el.select('.x-paging-info', true).first();
28195 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28196 //            this.displayEl = navel.el.select('span',true).first();
28197         }
28198         
28199         var _this = this;
28200         
28201         if(this.buttons){
28202             Roo.each(_this.buttons, function(e){ // this might need to use render????
28203                Roo.factory(e).render(_this.el);
28204             });
28205         }
28206             
28207         Roo.each(_this.toolbarItems, function(e) {
28208             _this.navgroup.addItem(e);
28209         });
28210         
28211         
28212         this.first = this.navgroup.addItem({
28213             tooltip: this.firstText,
28214             cls: "prev btn-outline-secondary",
28215             html : ' <i class="fa fa-step-backward"></i>',
28216             disabled: true,
28217             preventDefault: true,
28218             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28219         });
28220         
28221         this.prev =  this.navgroup.addItem({
28222             tooltip: this.prevText,
28223             cls: "prev btn-outline-secondary",
28224             html : ' <i class="fa fa-backward"></i>',
28225             disabled: true,
28226             preventDefault: true,
28227             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28228         });
28229     //this.addSeparator();
28230         
28231         
28232         var field = this.navgroup.addItem( {
28233             tagtype : 'span',
28234             cls : 'x-paging-position  btn-outline-secondary',
28235              disabled: true,
28236             html : this.beforePageText  +
28237                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28238                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28239          } ); //?? escaped?
28240         
28241         this.field = field.el.select('input', true).first();
28242         this.field.on("keydown", this.onPagingKeydown, this);
28243         this.field.on("focus", function(){this.dom.select();});
28244     
28245     
28246         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28247         //this.field.setHeight(18);
28248         //this.addSeparator();
28249         this.next = this.navgroup.addItem({
28250             tooltip: this.nextText,
28251             cls: "next btn-outline-secondary",
28252             html : ' <i class="fa fa-forward"></i>',
28253             disabled: true,
28254             preventDefault: true,
28255             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28256         });
28257         this.last = this.navgroup.addItem({
28258             tooltip: this.lastText,
28259             html : ' <i class="fa fa-step-forward"></i>',
28260             cls: "next btn-outline-secondary",
28261             disabled: true,
28262             preventDefault: true,
28263             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28264         });
28265     //this.addSeparator();
28266         this.loading = this.navgroup.addItem({
28267             tooltip: this.refreshText,
28268             cls: "btn-outline-secondary",
28269             html : ' <i class="fa fa-refresh"></i>',
28270             preventDefault: true,
28271             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28272         });
28273         
28274     },
28275
28276     // private
28277     updateInfo : function(){
28278         if(this.displayEl){
28279             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28280             var msg = count == 0 ?
28281                 this.emptyMsg :
28282                 String.format(
28283                     this.displayMsg,
28284                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28285                 );
28286             this.displayEl.update(msg);
28287         }
28288     },
28289
28290     // private
28291     onLoad : function(ds, r, o)
28292     {
28293         this.cursor = o.params && o.params.start ? o.params.start : 0;
28294         
28295         var d = this.getPageData(),
28296             ap = d.activePage,
28297             ps = d.pages;
28298         
28299         
28300         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28301         this.field.dom.value = ap;
28302         this.first.setDisabled(ap == 1);
28303         this.prev.setDisabled(ap == 1);
28304         this.next.setDisabled(ap == ps);
28305         this.last.setDisabled(ap == ps);
28306         this.loading.enable();
28307         this.updateInfo();
28308     },
28309
28310     // private
28311     getPageData : function(){
28312         var total = this.ds.getTotalCount();
28313         return {
28314             total : total,
28315             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28316             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28317         };
28318     },
28319
28320     // private
28321     onLoadError : function(){
28322         this.loading.enable();
28323     },
28324
28325     // private
28326     onPagingKeydown : function(e){
28327         var k = e.getKey();
28328         var d = this.getPageData();
28329         if(k == e.RETURN){
28330             var v = this.field.dom.value, pageNum;
28331             if(!v || isNaN(pageNum = parseInt(v, 10))){
28332                 this.field.dom.value = d.activePage;
28333                 return;
28334             }
28335             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28336             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28337             e.stopEvent();
28338         }
28339         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))
28340         {
28341           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28342           this.field.dom.value = pageNum;
28343           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28344           e.stopEvent();
28345         }
28346         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28347         {
28348           var v = this.field.dom.value, pageNum; 
28349           var increment = (e.shiftKey) ? 10 : 1;
28350           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28351                 increment *= -1;
28352           }
28353           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28354             this.field.dom.value = d.activePage;
28355             return;
28356           }
28357           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28358           {
28359             this.field.dom.value = parseInt(v, 10) + increment;
28360             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28361             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28362           }
28363           e.stopEvent();
28364         }
28365     },
28366
28367     // private
28368     beforeLoad : function(){
28369         if(this.loading){
28370             this.loading.disable();
28371         }
28372     },
28373
28374     // private
28375     onClick : function(which){
28376         
28377         var ds = this.ds;
28378         if (!ds) {
28379             return;
28380         }
28381         
28382         switch(which){
28383             case "first":
28384                 ds.load({params:{start: 0, limit: this.pageSize}});
28385             break;
28386             case "prev":
28387                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28388             break;
28389             case "next":
28390                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28391             break;
28392             case "last":
28393                 var total = ds.getTotalCount();
28394                 var extra = total % this.pageSize;
28395                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28396                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28397             break;
28398             case "refresh":
28399                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28400             break;
28401         }
28402     },
28403
28404     /**
28405      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28406      * @param {Roo.data.Store} store The data store to unbind
28407      */
28408     unbind : function(ds){
28409         ds.un("beforeload", this.beforeLoad, this);
28410         ds.un("load", this.onLoad, this);
28411         ds.un("loadexception", this.onLoadError, this);
28412         ds.un("remove", this.updateInfo, this);
28413         ds.un("add", this.updateInfo, this);
28414         this.ds = undefined;
28415     },
28416
28417     /**
28418      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28419      * @param {Roo.data.Store} store The data store to bind
28420      */
28421     bind : function(ds){
28422         ds.on("beforeload", this.beforeLoad, this);
28423         ds.on("load", this.onLoad, this);
28424         ds.on("loadexception", this.onLoadError, this);
28425         ds.on("remove", this.updateInfo, this);
28426         ds.on("add", this.updateInfo, this);
28427         this.ds = ds;
28428     }
28429 });/*
28430  * - LGPL
28431  *
28432  * element
28433  * 
28434  */
28435
28436 /**
28437  * @class Roo.bootstrap.MessageBar
28438  * @extends Roo.bootstrap.Component
28439  * Bootstrap MessageBar class
28440  * @cfg {String} html contents of the MessageBar
28441  * @cfg {String} weight (info | success | warning | danger) default info
28442  * @cfg {String} beforeClass insert the bar before the given class
28443  * @cfg {Boolean} closable (true | false) default false
28444  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28445  * 
28446  * @constructor
28447  * Create a new Element
28448  * @param {Object} config The config object
28449  */
28450
28451 Roo.bootstrap.MessageBar = function(config){
28452     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28453 };
28454
28455 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28456     
28457     html: '',
28458     weight: 'info',
28459     closable: false,
28460     fixed: false,
28461     beforeClass: 'bootstrap-sticky-wrap',
28462     
28463     getAutoCreate : function(){
28464         
28465         var cfg = {
28466             tag: 'div',
28467             cls: 'alert alert-dismissable alert-' + this.weight,
28468             cn: [
28469                 {
28470                     tag: 'span',
28471                     cls: 'message',
28472                     html: this.html || ''
28473                 }
28474             ]
28475         };
28476         
28477         if(this.fixed){
28478             cfg.cls += ' alert-messages-fixed';
28479         }
28480         
28481         if(this.closable){
28482             cfg.cn.push({
28483                 tag: 'button',
28484                 cls: 'close',
28485                 html: 'x'
28486             });
28487         }
28488         
28489         return cfg;
28490     },
28491     
28492     onRender : function(ct, position)
28493     {
28494         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28495         
28496         if(!this.el){
28497             var cfg = Roo.apply({},  this.getAutoCreate());
28498             cfg.id = Roo.id();
28499             
28500             if (this.cls) {
28501                 cfg.cls += ' ' + this.cls;
28502             }
28503             if (this.style) {
28504                 cfg.style = this.style;
28505             }
28506             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28507             
28508             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28509         }
28510         
28511         this.el.select('>button.close').on('click', this.hide, this);
28512         
28513     },
28514     
28515     show : function()
28516     {
28517         if (!this.rendered) {
28518             this.render();
28519         }
28520         
28521         this.el.show();
28522         
28523         this.fireEvent('show', this);
28524         
28525     },
28526     
28527     hide : function()
28528     {
28529         if (!this.rendered) {
28530             this.render();
28531         }
28532         
28533         this.el.hide();
28534         
28535         this.fireEvent('hide', this);
28536     },
28537     
28538     update : function()
28539     {
28540 //        var e = this.el.dom.firstChild;
28541 //        
28542 //        if(this.closable){
28543 //            e = e.nextSibling;
28544 //        }
28545 //        
28546 //        e.data = this.html || '';
28547
28548         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28549     }
28550    
28551 });
28552
28553  
28554
28555      /*
28556  * - LGPL
28557  *
28558  * Graph
28559  * 
28560  */
28561
28562
28563 /**
28564  * @class Roo.bootstrap.Graph
28565  * @extends Roo.bootstrap.Component
28566  * Bootstrap Graph class
28567 > Prameters
28568  -sm {number} sm 4
28569  -md {number} md 5
28570  @cfg {String} graphtype  bar | vbar | pie
28571  @cfg {number} g_x coodinator | centre x (pie)
28572  @cfg {number} g_y coodinator | centre y (pie)
28573  @cfg {number} g_r radius (pie)
28574  @cfg {number} g_height height of the chart (respected by all elements in the set)
28575  @cfg {number} g_width width of the chart (respected by all elements in the set)
28576  @cfg {Object} title The title of the chart
28577     
28578  -{Array}  values
28579  -opts (object) options for the chart 
28580      o {
28581      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28582      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28583      o vgutter (number)
28584      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.
28585      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28586      o to
28587      o stretch (boolean)
28588      o }
28589  -opts (object) options for the pie
28590      o{
28591      o cut
28592      o startAngle (number)
28593      o endAngle (number)
28594      } 
28595  *
28596  * @constructor
28597  * Create a new Input
28598  * @param {Object} config The config object
28599  */
28600
28601 Roo.bootstrap.Graph = function(config){
28602     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28603     
28604     this.addEvents({
28605         // img events
28606         /**
28607          * @event click
28608          * The img click event for the img.
28609          * @param {Roo.EventObject} e
28610          */
28611         "click" : true
28612     });
28613 };
28614
28615 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28616     
28617     sm: 4,
28618     md: 5,
28619     graphtype: 'bar',
28620     g_height: 250,
28621     g_width: 400,
28622     g_x: 50,
28623     g_y: 50,
28624     g_r: 30,
28625     opts:{
28626         //g_colors: this.colors,
28627         g_type: 'soft',
28628         g_gutter: '20%'
28629
28630     },
28631     title : false,
28632
28633     getAutoCreate : function(){
28634         
28635         var cfg = {
28636             tag: 'div',
28637             html : null
28638         };
28639         
28640         
28641         return  cfg;
28642     },
28643
28644     onRender : function(ct,position){
28645         
28646         
28647         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28648         
28649         if (typeof(Raphael) == 'undefined') {
28650             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28651             return;
28652         }
28653         
28654         this.raphael = Raphael(this.el.dom);
28655         
28656                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28657                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28658                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28659                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28660                 /*
28661                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28662                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28663                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28664                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28665                 
28666                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28667                 r.barchart(330, 10, 300, 220, data1);
28668                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28669                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28670                 */
28671                 
28672                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28673                 // r.barchart(30, 30, 560, 250,  xdata, {
28674                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28675                 //     axis : "0 0 1 1",
28676                 //     axisxlabels :  xdata
28677                 //     //yvalues : cols,
28678                    
28679                 // });
28680 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28681 //        
28682 //        this.load(null,xdata,{
28683 //                axis : "0 0 1 1",
28684 //                axisxlabels :  xdata
28685 //                });
28686
28687     },
28688
28689     load : function(graphtype,xdata,opts)
28690     {
28691         this.raphael.clear();
28692         if(!graphtype) {
28693             graphtype = this.graphtype;
28694         }
28695         if(!opts){
28696             opts = this.opts;
28697         }
28698         var r = this.raphael,
28699             fin = function () {
28700                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28701             },
28702             fout = function () {
28703                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28704             },
28705             pfin = function() {
28706                 this.sector.stop();
28707                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28708
28709                 if (this.label) {
28710                     this.label[0].stop();
28711                     this.label[0].attr({ r: 7.5 });
28712                     this.label[1].attr({ "font-weight": 800 });
28713                 }
28714             },
28715             pfout = function() {
28716                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28717
28718                 if (this.label) {
28719                     this.label[0].animate({ r: 5 }, 500, "bounce");
28720                     this.label[1].attr({ "font-weight": 400 });
28721                 }
28722             };
28723
28724         switch(graphtype){
28725             case 'bar':
28726                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28727                 break;
28728             case 'hbar':
28729                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28730                 break;
28731             case 'pie':
28732 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28733 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28734 //            
28735                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28736                 
28737                 break;
28738
28739         }
28740         
28741         if(this.title){
28742             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28743         }
28744         
28745     },
28746     
28747     setTitle: function(o)
28748     {
28749         this.title = o;
28750     },
28751     
28752     initEvents: function() {
28753         
28754         if(!this.href){
28755             this.el.on('click', this.onClick, this);
28756         }
28757     },
28758     
28759     onClick : function(e)
28760     {
28761         Roo.log('img onclick');
28762         this.fireEvent('click', this, e);
28763     }
28764    
28765 });
28766
28767  
28768 /*
28769  * - LGPL
28770  *
28771  * numberBox
28772  * 
28773  */
28774 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28775
28776 /**
28777  * @class Roo.bootstrap.dash.NumberBox
28778  * @extends Roo.bootstrap.Component
28779  * Bootstrap NumberBox class
28780  * @cfg {String} headline Box headline
28781  * @cfg {String} content Box content
28782  * @cfg {String} icon Box icon
28783  * @cfg {String} footer Footer text
28784  * @cfg {String} fhref Footer href
28785  * 
28786  * @constructor
28787  * Create a new NumberBox
28788  * @param {Object} config The config object
28789  */
28790
28791
28792 Roo.bootstrap.dash.NumberBox = function(config){
28793     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28794     
28795 };
28796
28797 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28798     
28799     headline : '',
28800     content : '',
28801     icon : '',
28802     footer : '',
28803     fhref : '',
28804     ficon : '',
28805     
28806     getAutoCreate : function(){
28807         
28808         var cfg = {
28809             tag : 'div',
28810             cls : 'small-box ',
28811             cn : [
28812                 {
28813                     tag : 'div',
28814                     cls : 'inner',
28815                     cn :[
28816                         {
28817                             tag : 'h3',
28818                             cls : 'roo-headline',
28819                             html : this.headline
28820                         },
28821                         {
28822                             tag : 'p',
28823                             cls : 'roo-content',
28824                             html : this.content
28825                         }
28826                     ]
28827                 }
28828             ]
28829         };
28830         
28831         if(this.icon){
28832             cfg.cn.push({
28833                 tag : 'div',
28834                 cls : 'icon',
28835                 cn :[
28836                     {
28837                         tag : 'i',
28838                         cls : 'ion ' + this.icon
28839                     }
28840                 ]
28841             });
28842         }
28843         
28844         if(this.footer){
28845             var footer = {
28846                 tag : 'a',
28847                 cls : 'small-box-footer',
28848                 href : this.fhref || '#',
28849                 html : this.footer
28850             };
28851             
28852             cfg.cn.push(footer);
28853             
28854         }
28855         
28856         return  cfg;
28857     },
28858
28859     onRender : function(ct,position){
28860         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28861
28862
28863        
28864                 
28865     },
28866
28867     setHeadline: function (value)
28868     {
28869         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28870     },
28871     
28872     setFooter: function (value, href)
28873     {
28874         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28875         
28876         if(href){
28877             this.el.select('a.small-box-footer',true).first().attr('href', href);
28878         }
28879         
28880     },
28881
28882     setContent: function (value)
28883     {
28884         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28885     },
28886
28887     initEvents: function() 
28888     {   
28889         
28890     }
28891     
28892 });
28893
28894  
28895 /*
28896  * - LGPL
28897  *
28898  * TabBox
28899  * 
28900  */
28901 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28902
28903 /**
28904  * @class Roo.bootstrap.dash.TabBox
28905  * @extends Roo.bootstrap.Component
28906  * Bootstrap TabBox class
28907  * @cfg {String} title Title of the TabBox
28908  * @cfg {String} icon Icon of the TabBox
28909  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28910  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28911  * 
28912  * @constructor
28913  * Create a new TabBox
28914  * @param {Object} config The config object
28915  */
28916
28917
28918 Roo.bootstrap.dash.TabBox = function(config){
28919     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28920     this.addEvents({
28921         // raw events
28922         /**
28923          * @event addpane
28924          * When a pane is added
28925          * @param {Roo.bootstrap.dash.TabPane} pane
28926          */
28927         "addpane" : true,
28928         /**
28929          * @event activatepane
28930          * When a pane is activated
28931          * @param {Roo.bootstrap.dash.TabPane} pane
28932          */
28933         "activatepane" : true
28934         
28935          
28936     });
28937     
28938     this.panes = [];
28939 };
28940
28941 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28942
28943     title : '',
28944     icon : false,
28945     showtabs : true,
28946     tabScrollable : false,
28947     
28948     getChildContainer : function()
28949     {
28950         return this.el.select('.tab-content', true).first();
28951     },
28952     
28953     getAutoCreate : function(){
28954         
28955         var header = {
28956             tag: 'li',
28957             cls: 'pull-left header',
28958             html: this.title,
28959             cn : []
28960         };
28961         
28962         if(this.icon){
28963             header.cn.push({
28964                 tag: 'i',
28965                 cls: 'fa ' + this.icon
28966             });
28967         }
28968         
28969         var h = {
28970             tag: 'ul',
28971             cls: 'nav nav-tabs pull-right',
28972             cn: [
28973                 header
28974             ]
28975         };
28976         
28977         if(this.tabScrollable){
28978             h = {
28979                 tag: 'div',
28980                 cls: 'tab-header',
28981                 cn: [
28982                     {
28983                         tag: 'ul',
28984                         cls: 'nav nav-tabs pull-right',
28985                         cn: [
28986                             header
28987                         ]
28988                     }
28989                 ]
28990             };
28991         }
28992         
28993         var cfg = {
28994             tag: 'div',
28995             cls: 'nav-tabs-custom',
28996             cn: [
28997                 h,
28998                 {
28999                     tag: 'div',
29000                     cls: 'tab-content no-padding',
29001                     cn: []
29002                 }
29003             ]
29004         };
29005
29006         return  cfg;
29007     },
29008     initEvents : function()
29009     {
29010         //Roo.log('add add pane handler');
29011         this.on('addpane', this.onAddPane, this);
29012     },
29013      /**
29014      * Updates the box title
29015      * @param {String} html to set the title to.
29016      */
29017     setTitle : function(value)
29018     {
29019         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29020     },
29021     onAddPane : function(pane)
29022     {
29023         this.panes.push(pane);
29024         //Roo.log('addpane');
29025         //Roo.log(pane);
29026         // tabs are rendere left to right..
29027         if(!this.showtabs){
29028             return;
29029         }
29030         
29031         var ctr = this.el.select('.nav-tabs', true).first();
29032          
29033          
29034         var existing = ctr.select('.nav-tab',true);
29035         var qty = existing.getCount();;
29036         
29037         
29038         var tab = ctr.createChild({
29039             tag : 'li',
29040             cls : 'nav-tab' + (qty ? '' : ' active'),
29041             cn : [
29042                 {
29043                     tag : 'a',
29044                     href:'#',
29045                     html : pane.title
29046                 }
29047             ]
29048         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29049         pane.tab = tab;
29050         
29051         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29052         if (!qty) {
29053             pane.el.addClass('active');
29054         }
29055         
29056                 
29057     },
29058     onTabClick : function(ev,un,ob,pane)
29059     {
29060         //Roo.log('tab - prev default');
29061         ev.preventDefault();
29062         
29063         
29064         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29065         pane.tab.addClass('active');
29066         //Roo.log(pane.title);
29067         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29068         // technically we should have a deactivate event.. but maybe add later.
29069         // and it should not de-activate the selected tab...
29070         this.fireEvent('activatepane', pane);
29071         pane.el.addClass('active');
29072         pane.fireEvent('activate');
29073         
29074         
29075     },
29076     
29077     getActivePane : function()
29078     {
29079         var r = false;
29080         Roo.each(this.panes, function(p) {
29081             if(p.el.hasClass('active')){
29082                 r = p;
29083                 return false;
29084             }
29085             
29086             return;
29087         });
29088         
29089         return r;
29090     }
29091     
29092     
29093 });
29094
29095  
29096 /*
29097  * - LGPL
29098  *
29099  * Tab pane
29100  * 
29101  */
29102 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29103 /**
29104  * @class Roo.bootstrap.TabPane
29105  * @extends Roo.bootstrap.Component
29106  * Bootstrap TabPane class
29107  * @cfg {Boolean} active (false | true) Default false
29108  * @cfg {String} title title of panel
29109
29110  * 
29111  * @constructor
29112  * Create a new TabPane
29113  * @param {Object} config The config object
29114  */
29115
29116 Roo.bootstrap.dash.TabPane = function(config){
29117     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29118     
29119     this.addEvents({
29120         // raw events
29121         /**
29122          * @event activate
29123          * When a pane is activated
29124          * @param {Roo.bootstrap.dash.TabPane} pane
29125          */
29126         "activate" : true
29127          
29128     });
29129 };
29130
29131 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29132     
29133     active : false,
29134     title : '',
29135     
29136     // the tabBox that this is attached to.
29137     tab : false,
29138      
29139     getAutoCreate : function() 
29140     {
29141         var cfg = {
29142             tag: 'div',
29143             cls: 'tab-pane'
29144         };
29145         
29146         if(this.active){
29147             cfg.cls += ' active';
29148         }
29149         
29150         return cfg;
29151     },
29152     initEvents  : function()
29153     {
29154         //Roo.log('trigger add pane handler');
29155         this.parent().fireEvent('addpane', this)
29156     },
29157     
29158      /**
29159      * Updates the tab title 
29160      * @param {String} html to set the title to.
29161      */
29162     setTitle: function(str)
29163     {
29164         if (!this.tab) {
29165             return;
29166         }
29167         this.title = str;
29168         this.tab.select('a', true).first().dom.innerHTML = str;
29169         
29170     }
29171     
29172     
29173     
29174 });
29175
29176  
29177
29178
29179  /*
29180  * - LGPL
29181  *
29182  * menu
29183  * 
29184  */
29185 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29186
29187 /**
29188  * @class Roo.bootstrap.menu.Menu
29189  * @extends Roo.bootstrap.Component
29190  * Bootstrap Menu class - container for Menu
29191  * @cfg {String} html Text of the menu
29192  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29193  * @cfg {String} icon Font awesome icon
29194  * @cfg {String} pos Menu align to (top | bottom) default bottom
29195  * 
29196  * 
29197  * @constructor
29198  * Create a new Menu
29199  * @param {Object} config The config object
29200  */
29201
29202
29203 Roo.bootstrap.menu.Menu = function(config){
29204     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29205     
29206     this.addEvents({
29207         /**
29208          * @event beforeshow
29209          * Fires before this menu is displayed
29210          * @param {Roo.bootstrap.menu.Menu} this
29211          */
29212         beforeshow : true,
29213         /**
29214          * @event beforehide
29215          * Fires before this menu is hidden
29216          * @param {Roo.bootstrap.menu.Menu} this
29217          */
29218         beforehide : true,
29219         /**
29220          * @event show
29221          * Fires after this menu is displayed
29222          * @param {Roo.bootstrap.menu.Menu} this
29223          */
29224         show : true,
29225         /**
29226          * @event hide
29227          * Fires after this menu is hidden
29228          * @param {Roo.bootstrap.menu.Menu} this
29229          */
29230         hide : true,
29231         /**
29232          * @event click
29233          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29234          * @param {Roo.bootstrap.menu.Menu} this
29235          * @param {Roo.EventObject} e
29236          */
29237         click : true
29238     });
29239     
29240 };
29241
29242 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29243     
29244     submenu : false,
29245     html : '',
29246     weight : 'default',
29247     icon : false,
29248     pos : 'bottom',
29249     
29250     
29251     getChildContainer : function() {
29252         if(this.isSubMenu){
29253             return this.el;
29254         }
29255         
29256         return this.el.select('ul.dropdown-menu', true).first();  
29257     },
29258     
29259     getAutoCreate : function()
29260     {
29261         var text = [
29262             {
29263                 tag : 'span',
29264                 cls : 'roo-menu-text',
29265                 html : this.html
29266             }
29267         ];
29268         
29269         if(this.icon){
29270             text.unshift({
29271                 tag : 'i',
29272                 cls : 'fa ' + this.icon
29273             })
29274         }
29275         
29276         
29277         var cfg = {
29278             tag : 'div',
29279             cls : 'btn-group',
29280             cn : [
29281                 {
29282                     tag : 'button',
29283                     cls : 'dropdown-button btn btn-' + this.weight,
29284                     cn : text
29285                 },
29286                 {
29287                     tag : 'button',
29288                     cls : 'dropdown-toggle btn btn-' + this.weight,
29289                     cn : [
29290                         {
29291                             tag : 'span',
29292                             cls : 'caret'
29293                         }
29294                     ]
29295                 },
29296                 {
29297                     tag : 'ul',
29298                     cls : 'dropdown-menu'
29299                 }
29300             ]
29301             
29302         };
29303         
29304         if(this.pos == 'top'){
29305             cfg.cls += ' dropup';
29306         }
29307         
29308         if(this.isSubMenu){
29309             cfg = {
29310                 tag : 'ul',
29311                 cls : 'dropdown-menu'
29312             }
29313         }
29314         
29315         return cfg;
29316     },
29317     
29318     onRender : function(ct, position)
29319     {
29320         this.isSubMenu = ct.hasClass('dropdown-submenu');
29321         
29322         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29323     },
29324     
29325     initEvents : function() 
29326     {
29327         if(this.isSubMenu){
29328             return;
29329         }
29330         
29331         this.hidden = true;
29332         
29333         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29334         this.triggerEl.on('click', this.onTriggerPress, this);
29335         
29336         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29337         this.buttonEl.on('click', this.onClick, this);
29338         
29339     },
29340     
29341     list : function()
29342     {
29343         if(this.isSubMenu){
29344             return this.el;
29345         }
29346         
29347         return this.el.select('ul.dropdown-menu', true).first();
29348     },
29349     
29350     onClick : function(e)
29351     {
29352         this.fireEvent("click", this, e);
29353     },
29354     
29355     onTriggerPress  : function(e)
29356     {   
29357         if (this.isVisible()) {
29358             this.hide();
29359         } else {
29360             this.show();
29361         }
29362     },
29363     
29364     isVisible : function(){
29365         return !this.hidden;
29366     },
29367     
29368     show : function()
29369     {
29370         this.fireEvent("beforeshow", this);
29371         
29372         this.hidden = false;
29373         this.el.addClass('open');
29374         
29375         Roo.get(document).on("mouseup", this.onMouseUp, this);
29376         
29377         this.fireEvent("show", this);
29378         
29379         
29380     },
29381     
29382     hide : function()
29383     {
29384         this.fireEvent("beforehide", this);
29385         
29386         this.hidden = true;
29387         this.el.removeClass('open');
29388         
29389         Roo.get(document).un("mouseup", this.onMouseUp);
29390         
29391         this.fireEvent("hide", this);
29392     },
29393     
29394     onMouseUp : function()
29395     {
29396         this.hide();
29397     }
29398     
29399 });
29400
29401  
29402  /*
29403  * - LGPL
29404  *
29405  * menu item
29406  * 
29407  */
29408 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29409
29410 /**
29411  * @class Roo.bootstrap.menu.Item
29412  * @extends Roo.bootstrap.Component
29413  * Bootstrap MenuItem class
29414  * @cfg {Boolean} submenu (true | false) default false
29415  * @cfg {String} html text of the item
29416  * @cfg {String} href the link
29417  * @cfg {Boolean} disable (true | false) default false
29418  * @cfg {Boolean} preventDefault (true | false) default true
29419  * @cfg {String} icon Font awesome icon
29420  * @cfg {String} pos Submenu align to (left | right) default right 
29421  * 
29422  * 
29423  * @constructor
29424  * Create a new Item
29425  * @param {Object} config The config object
29426  */
29427
29428
29429 Roo.bootstrap.menu.Item = function(config){
29430     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29431     this.addEvents({
29432         /**
29433          * @event mouseover
29434          * Fires when the mouse is hovering over this menu
29435          * @param {Roo.bootstrap.menu.Item} this
29436          * @param {Roo.EventObject} e
29437          */
29438         mouseover : true,
29439         /**
29440          * @event mouseout
29441          * Fires when the mouse exits this menu
29442          * @param {Roo.bootstrap.menu.Item} this
29443          * @param {Roo.EventObject} e
29444          */
29445         mouseout : true,
29446         // raw events
29447         /**
29448          * @event click
29449          * The raw click event for the entire grid.
29450          * @param {Roo.EventObject} e
29451          */
29452         click : true
29453     });
29454 };
29455
29456 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29457     
29458     submenu : false,
29459     href : '',
29460     html : '',
29461     preventDefault: true,
29462     disable : false,
29463     icon : false,
29464     pos : 'right',
29465     
29466     getAutoCreate : function()
29467     {
29468         var text = [
29469             {
29470                 tag : 'span',
29471                 cls : 'roo-menu-item-text',
29472                 html : this.html
29473             }
29474         ];
29475         
29476         if(this.icon){
29477             text.unshift({
29478                 tag : 'i',
29479                 cls : 'fa ' + this.icon
29480             })
29481         }
29482         
29483         var cfg = {
29484             tag : 'li',
29485             cn : [
29486                 {
29487                     tag : 'a',
29488                     href : this.href || '#',
29489                     cn : text
29490                 }
29491             ]
29492         };
29493         
29494         if(this.disable){
29495             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29496         }
29497         
29498         if(this.submenu){
29499             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29500             
29501             if(this.pos == 'left'){
29502                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29503             }
29504         }
29505         
29506         return cfg;
29507     },
29508     
29509     initEvents : function() 
29510     {
29511         this.el.on('mouseover', this.onMouseOver, this);
29512         this.el.on('mouseout', this.onMouseOut, this);
29513         
29514         this.el.select('a', true).first().on('click', this.onClick, this);
29515         
29516     },
29517     
29518     onClick : function(e)
29519     {
29520         if(this.preventDefault){
29521             e.preventDefault();
29522         }
29523         
29524         this.fireEvent("click", this, e);
29525     },
29526     
29527     onMouseOver : function(e)
29528     {
29529         if(this.submenu && this.pos == 'left'){
29530             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29531         }
29532         
29533         this.fireEvent("mouseover", this, e);
29534     },
29535     
29536     onMouseOut : function(e)
29537     {
29538         this.fireEvent("mouseout", this, e);
29539     }
29540 });
29541
29542  
29543
29544  /*
29545  * - LGPL
29546  *
29547  * menu separator
29548  * 
29549  */
29550 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29551
29552 /**
29553  * @class Roo.bootstrap.menu.Separator
29554  * @extends Roo.bootstrap.Component
29555  * Bootstrap Separator class
29556  * 
29557  * @constructor
29558  * Create a new Separator
29559  * @param {Object} config The config object
29560  */
29561
29562
29563 Roo.bootstrap.menu.Separator = function(config){
29564     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29565 };
29566
29567 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29568     
29569     getAutoCreate : function(){
29570         var cfg = {
29571             tag : 'li',
29572             cls: 'dropdown-divider divider'
29573         };
29574         
29575         return cfg;
29576     }
29577    
29578 });
29579
29580  
29581
29582  /*
29583  * - LGPL
29584  *
29585  * Tooltip
29586  * 
29587  */
29588
29589 /**
29590  * @class Roo.bootstrap.Tooltip
29591  * Bootstrap Tooltip class
29592  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29593  * to determine which dom element triggers the tooltip.
29594  * 
29595  * It needs to add support for additional attributes like tooltip-position
29596  * 
29597  * @constructor
29598  * Create a new Toolti
29599  * @param {Object} config The config object
29600  */
29601
29602 Roo.bootstrap.Tooltip = function(config){
29603     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29604     
29605     this.alignment = Roo.bootstrap.Tooltip.alignment;
29606     
29607     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29608         this.alignment = config.alignment;
29609     }
29610     
29611 };
29612
29613 Roo.apply(Roo.bootstrap.Tooltip, {
29614     /**
29615      * @function init initialize tooltip monitoring.
29616      * @static
29617      */
29618     currentEl : false,
29619     currentTip : false,
29620     currentRegion : false,
29621     
29622     //  init : delay?
29623     
29624     init : function()
29625     {
29626         Roo.get(document).on('mouseover', this.enter ,this);
29627         Roo.get(document).on('mouseout', this.leave, this);
29628          
29629         
29630         this.currentTip = new Roo.bootstrap.Tooltip();
29631     },
29632     
29633     enter : function(ev)
29634     {
29635         var dom = ev.getTarget();
29636         
29637         //Roo.log(['enter',dom]);
29638         var el = Roo.fly(dom);
29639         if (this.currentEl) {
29640             //Roo.log(dom);
29641             //Roo.log(this.currentEl);
29642             //Roo.log(this.currentEl.contains(dom));
29643             if (this.currentEl == el) {
29644                 return;
29645             }
29646             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29647                 return;
29648             }
29649
29650         }
29651         
29652         if (this.currentTip.el) {
29653             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29654         }    
29655         //Roo.log(ev);
29656         
29657         if(!el || el.dom == document){
29658             return;
29659         }
29660         
29661         var bindEl = el; 
29662         var pel = false;
29663         if (!el.attr('tooltip')) {
29664             pel = el.findParent("[tooltip]");
29665             if (pel) {
29666                 bindEl = Roo.get(pel);
29667             }
29668         }
29669         
29670        
29671         
29672         // you can not look for children, as if el is the body.. then everythign is the child..
29673         if (!pel && !el.attr('tooltip')) { //
29674             if (!el.select("[tooltip]").elements.length) {
29675                 return;
29676             }
29677             // is the mouse over this child...?
29678             bindEl = el.select("[tooltip]").first();
29679             var xy = ev.getXY();
29680             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29681                 //Roo.log("not in region.");
29682                 return;
29683             }
29684             //Roo.log("child element over..");
29685             
29686         }
29687         this.currentEl = el;
29688         this.currentTip.bind(bindEl);
29689         this.currentRegion = Roo.lib.Region.getRegion(dom);
29690         this.currentTip.enter();
29691         
29692     },
29693     leave : function(ev)
29694     {
29695         var dom = ev.getTarget();
29696         //Roo.log(['leave',dom]);
29697         if (!this.currentEl) {
29698             return;
29699         }
29700         
29701         
29702         if (dom != this.currentEl.dom) {
29703             return;
29704         }
29705         var xy = ev.getXY();
29706         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29707             return;
29708         }
29709         // only activate leave if mouse cursor is outside... bounding box..
29710         
29711         
29712         
29713         
29714         if (this.currentTip) {
29715             this.currentTip.leave();
29716         }
29717         //Roo.log('clear currentEl');
29718         this.currentEl = false;
29719         
29720         
29721     },
29722     alignment : {
29723         'left' : ['r-l', [-2,0], 'right'],
29724         'right' : ['l-r', [2,0], 'left'],
29725         'bottom' : ['t-b', [0,2], 'top'],
29726         'top' : [ 'b-t', [0,-2], 'bottom']
29727     }
29728     
29729 });
29730
29731
29732 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29733     
29734     
29735     bindEl : false,
29736     
29737     delay : null, // can be { show : 300 , hide: 500}
29738     
29739     timeout : null,
29740     
29741     hoverState : null, //???
29742     
29743     placement : 'bottom', 
29744     
29745     alignment : false,
29746     
29747     getAutoCreate : function(){
29748     
29749         var cfg = {
29750            cls : 'tooltip',   
29751            role : 'tooltip',
29752            cn : [
29753                 {
29754                     cls : 'tooltip-arrow arrow'
29755                 },
29756                 {
29757                     cls : 'tooltip-inner'
29758                 }
29759            ]
29760         };
29761         
29762         return cfg;
29763     },
29764     bind : function(el)
29765     {
29766         this.bindEl = el;
29767     },
29768     
29769     initEvents : function()
29770     {
29771         this.arrowEl = this.el.select('.arrow', true).first();
29772         this.innerEl = this.el.select('.tooltip-inner', true).first();
29773     },
29774     
29775     enter : function () {
29776        
29777         if (this.timeout != null) {
29778             clearTimeout(this.timeout);
29779         }
29780         
29781         this.hoverState = 'in';
29782          //Roo.log("enter - show");
29783         if (!this.delay || !this.delay.show) {
29784             this.show();
29785             return;
29786         }
29787         var _t = this;
29788         this.timeout = setTimeout(function () {
29789             if (_t.hoverState == 'in') {
29790                 _t.show();
29791             }
29792         }, this.delay.show);
29793     },
29794     leave : function()
29795     {
29796         clearTimeout(this.timeout);
29797     
29798         this.hoverState = 'out';
29799          if (!this.delay || !this.delay.hide) {
29800             this.hide();
29801             return;
29802         }
29803        
29804         var _t = this;
29805         this.timeout = setTimeout(function () {
29806             //Roo.log("leave - timeout");
29807             
29808             if (_t.hoverState == 'out') {
29809                 _t.hide();
29810                 Roo.bootstrap.Tooltip.currentEl = false;
29811             }
29812         }, delay);
29813     },
29814     
29815     show : function (msg)
29816     {
29817         if (!this.el) {
29818             this.render(document.body);
29819         }
29820         // set content.
29821         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29822         
29823         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29824         
29825         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29826         
29827         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29828                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29829         
29830         var placement = typeof this.placement == 'function' ?
29831             this.placement.call(this, this.el, on_el) :
29832             this.placement;
29833             
29834         var autoToken = /\s?auto?\s?/i;
29835         var autoPlace = autoToken.test(placement);
29836         if (autoPlace) {
29837             placement = placement.replace(autoToken, '') || 'top';
29838         }
29839         
29840         //this.el.detach()
29841         //this.el.setXY([0,0]);
29842         this.el.show();
29843         //this.el.dom.style.display='block';
29844         
29845         //this.el.appendTo(on_el);
29846         
29847         var p = this.getPosition();
29848         var box = this.el.getBox();
29849         
29850         if (autoPlace) {
29851             // fixme..
29852         }
29853         
29854         var align = this.alignment[placement];
29855         
29856         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29857         
29858         if(placement == 'top' || placement == 'bottom'){
29859             if(xy[0] < 0){
29860                 placement = 'right';
29861             }
29862             
29863             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29864                 placement = 'left';
29865             }
29866             
29867             var scroll = Roo.select('body', true).first().getScroll();
29868             
29869             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29870                 placement = 'top';
29871             }
29872             
29873             align = this.alignment[placement];
29874             
29875             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29876             
29877         }
29878         
29879         var elems = document.getElementsByTagName('div');
29880         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29881         for (var i = 0; i < elems.length; i++) {
29882           var zindex = Number.parseInt(
29883                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29884                 10
29885           );
29886           if (zindex > highest) {
29887             highest = zindex;
29888           }
29889         }
29890         
29891         
29892         
29893         this.el.dom.style.zIndex = highest;
29894         
29895         this.el.alignTo(this.bindEl, align[0],align[1]);
29896         //var arrow = this.el.select('.arrow',true).first();
29897         //arrow.set(align[2], 
29898         
29899         this.el.addClass(placement);
29900         this.el.addClass("bs-tooltip-"+ placement);
29901         
29902         this.el.addClass('in fade show');
29903         
29904         this.hoverState = null;
29905         
29906         if (this.el.hasClass('fade')) {
29907             // fade it?
29908         }
29909         
29910         
29911         
29912         
29913         
29914     },
29915     hide : function()
29916     {
29917          
29918         if (!this.el) {
29919             return;
29920         }
29921         //this.el.setXY([0,0]);
29922         this.el.removeClass(['show', 'in']);
29923         //this.el.hide();
29924         
29925     }
29926     
29927 });
29928  
29929
29930  /*
29931  * - LGPL
29932  *
29933  * Location Picker
29934  * 
29935  */
29936
29937 /**
29938  * @class Roo.bootstrap.LocationPicker
29939  * @extends Roo.bootstrap.Component
29940  * Bootstrap LocationPicker class
29941  * @cfg {Number} latitude Position when init default 0
29942  * @cfg {Number} longitude Position when init default 0
29943  * @cfg {Number} zoom default 15
29944  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29945  * @cfg {Boolean} mapTypeControl default false
29946  * @cfg {Boolean} disableDoubleClickZoom default false
29947  * @cfg {Boolean} scrollwheel default true
29948  * @cfg {Boolean} streetViewControl default false
29949  * @cfg {Number} radius default 0
29950  * @cfg {String} locationName
29951  * @cfg {Boolean} draggable default true
29952  * @cfg {Boolean} enableAutocomplete default false
29953  * @cfg {Boolean} enableReverseGeocode default true
29954  * @cfg {String} markerTitle
29955  * 
29956  * @constructor
29957  * Create a new LocationPicker
29958  * @param {Object} config The config object
29959  */
29960
29961
29962 Roo.bootstrap.LocationPicker = function(config){
29963     
29964     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29965     
29966     this.addEvents({
29967         /**
29968          * @event initial
29969          * Fires when the picker initialized.
29970          * @param {Roo.bootstrap.LocationPicker} this
29971          * @param {Google Location} location
29972          */
29973         initial : true,
29974         /**
29975          * @event positionchanged
29976          * Fires when the picker position changed.
29977          * @param {Roo.bootstrap.LocationPicker} this
29978          * @param {Google Location} location
29979          */
29980         positionchanged : true,
29981         /**
29982          * @event resize
29983          * Fires when the map resize.
29984          * @param {Roo.bootstrap.LocationPicker} this
29985          */
29986         resize : true,
29987         /**
29988          * @event show
29989          * Fires when the map show.
29990          * @param {Roo.bootstrap.LocationPicker} this
29991          */
29992         show : true,
29993         /**
29994          * @event hide
29995          * Fires when the map hide.
29996          * @param {Roo.bootstrap.LocationPicker} this
29997          */
29998         hide : true,
29999         /**
30000          * @event mapClick
30001          * Fires when click the map.
30002          * @param {Roo.bootstrap.LocationPicker} this
30003          * @param {Map event} e
30004          */
30005         mapClick : true,
30006         /**
30007          * @event mapRightClick
30008          * Fires when right click the map.
30009          * @param {Roo.bootstrap.LocationPicker} this
30010          * @param {Map event} e
30011          */
30012         mapRightClick : true,
30013         /**
30014          * @event markerClick
30015          * Fires when click the marker.
30016          * @param {Roo.bootstrap.LocationPicker} this
30017          * @param {Map event} e
30018          */
30019         markerClick : true,
30020         /**
30021          * @event markerRightClick
30022          * Fires when right click the marker.
30023          * @param {Roo.bootstrap.LocationPicker} this
30024          * @param {Map event} e
30025          */
30026         markerRightClick : true,
30027         /**
30028          * @event OverlayViewDraw
30029          * Fires when OverlayView Draw
30030          * @param {Roo.bootstrap.LocationPicker} this
30031          */
30032         OverlayViewDraw : true,
30033         /**
30034          * @event OverlayViewOnAdd
30035          * Fires when OverlayView Draw
30036          * @param {Roo.bootstrap.LocationPicker} this
30037          */
30038         OverlayViewOnAdd : true,
30039         /**
30040          * @event OverlayViewOnRemove
30041          * Fires when OverlayView Draw
30042          * @param {Roo.bootstrap.LocationPicker} this
30043          */
30044         OverlayViewOnRemove : true,
30045         /**
30046          * @event OverlayViewShow
30047          * Fires when OverlayView Draw
30048          * @param {Roo.bootstrap.LocationPicker} this
30049          * @param {Pixel} cpx
30050          */
30051         OverlayViewShow : true,
30052         /**
30053          * @event OverlayViewHide
30054          * Fires when OverlayView Draw
30055          * @param {Roo.bootstrap.LocationPicker} this
30056          */
30057         OverlayViewHide : true,
30058         /**
30059          * @event loadexception
30060          * Fires when load google lib failed.
30061          * @param {Roo.bootstrap.LocationPicker} this
30062          */
30063         loadexception : true
30064     });
30065         
30066 };
30067
30068 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30069     
30070     gMapContext: false,
30071     
30072     latitude: 0,
30073     longitude: 0,
30074     zoom: 15,
30075     mapTypeId: false,
30076     mapTypeControl: false,
30077     disableDoubleClickZoom: false,
30078     scrollwheel: true,
30079     streetViewControl: false,
30080     radius: 0,
30081     locationName: '',
30082     draggable: true,
30083     enableAutocomplete: false,
30084     enableReverseGeocode: true,
30085     markerTitle: '',
30086     
30087     getAutoCreate: function()
30088     {
30089
30090         var cfg = {
30091             tag: 'div',
30092             cls: 'roo-location-picker'
30093         };
30094         
30095         return cfg
30096     },
30097     
30098     initEvents: function(ct, position)
30099     {       
30100         if(!this.el.getWidth() || this.isApplied()){
30101             return;
30102         }
30103         
30104         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30105         
30106         this.initial();
30107     },
30108     
30109     initial: function()
30110     {
30111         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30112             this.fireEvent('loadexception', this);
30113             return;
30114         }
30115         
30116         if(!this.mapTypeId){
30117             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30118         }
30119         
30120         this.gMapContext = this.GMapContext();
30121         
30122         this.initOverlayView();
30123         
30124         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30125         
30126         var _this = this;
30127                 
30128         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30129             _this.setPosition(_this.gMapContext.marker.position);
30130         });
30131         
30132         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30133             _this.fireEvent('mapClick', this, event);
30134             
30135         });
30136
30137         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30138             _this.fireEvent('mapRightClick', this, event);
30139             
30140         });
30141         
30142         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30143             _this.fireEvent('markerClick', this, event);
30144             
30145         });
30146
30147         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30148             _this.fireEvent('markerRightClick', this, event);
30149             
30150         });
30151         
30152         this.setPosition(this.gMapContext.location);
30153         
30154         this.fireEvent('initial', this, this.gMapContext.location);
30155     },
30156     
30157     initOverlayView: function()
30158     {
30159         var _this = this;
30160         
30161         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30162             
30163             draw: function()
30164             {
30165                 _this.fireEvent('OverlayViewDraw', _this);
30166             },
30167             
30168             onAdd: function()
30169             {
30170                 _this.fireEvent('OverlayViewOnAdd', _this);
30171             },
30172             
30173             onRemove: function()
30174             {
30175                 _this.fireEvent('OverlayViewOnRemove', _this);
30176             },
30177             
30178             show: function(cpx)
30179             {
30180                 _this.fireEvent('OverlayViewShow', _this, cpx);
30181             },
30182             
30183             hide: function()
30184             {
30185                 _this.fireEvent('OverlayViewHide', _this);
30186             }
30187             
30188         });
30189     },
30190     
30191     fromLatLngToContainerPixel: function(event)
30192     {
30193         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30194     },
30195     
30196     isApplied: function() 
30197     {
30198         return this.getGmapContext() == false ? false : true;
30199     },
30200     
30201     getGmapContext: function() 
30202     {
30203         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30204     },
30205     
30206     GMapContext: function() 
30207     {
30208         var position = new google.maps.LatLng(this.latitude, this.longitude);
30209         
30210         var _map = new google.maps.Map(this.el.dom, {
30211             center: position,
30212             zoom: this.zoom,
30213             mapTypeId: this.mapTypeId,
30214             mapTypeControl: this.mapTypeControl,
30215             disableDoubleClickZoom: this.disableDoubleClickZoom,
30216             scrollwheel: this.scrollwheel,
30217             streetViewControl: this.streetViewControl,
30218             locationName: this.locationName,
30219             draggable: this.draggable,
30220             enableAutocomplete: this.enableAutocomplete,
30221             enableReverseGeocode: this.enableReverseGeocode
30222         });
30223         
30224         var _marker = new google.maps.Marker({
30225             position: position,
30226             map: _map,
30227             title: this.markerTitle,
30228             draggable: this.draggable
30229         });
30230         
30231         return {
30232             map: _map,
30233             marker: _marker,
30234             circle: null,
30235             location: position,
30236             radius: this.radius,
30237             locationName: this.locationName,
30238             addressComponents: {
30239                 formatted_address: null,
30240                 addressLine1: null,
30241                 addressLine2: null,
30242                 streetName: null,
30243                 streetNumber: null,
30244                 city: null,
30245                 district: null,
30246                 state: null,
30247                 stateOrProvince: null
30248             },
30249             settings: this,
30250             domContainer: this.el.dom,
30251             geodecoder: new google.maps.Geocoder()
30252         };
30253     },
30254     
30255     drawCircle: function(center, radius, options) 
30256     {
30257         if (this.gMapContext.circle != null) {
30258             this.gMapContext.circle.setMap(null);
30259         }
30260         if (radius > 0) {
30261             radius *= 1;
30262             options = Roo.apply({}, options, {
30263                 strokeColor: "#0000FF",
30264                 strokeOpacity: .35,
30265                 strokeWeight: 2,
30266                 fillColor: "#0000FF",
30267                 fillOpacity: .2
30268             });
30269             
30270             options.map = this.gMapContext.map;
30271             options.radius = radius;
30272             options.center = center;
30273             this.gMapContext.circle = new google.maps.Circle(options);
30274             return this.gMapContext.circle;
30275         }
30276         
30277         return null;
30278     },
30279     
30280     setPosition: function(location) 
30281     {
30282         this.gMapContext.location = location;
30283         this.gMapContext.marker.setPosition(location);
30284         this.gMapContext.map.panTo(location);
30285         this.drawCircle(location, this.gMapContext.radius, {});
30286         
30287         var _this = this;
30288         
30289         if (this.gMapContext.settings.enableReverseGeocode) {
30290             this.gMapContext.geodecoder.geocode({
30291                 latLng: this.gMapContext.location
30292             }, function(results, status) {
30293                 
30294                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30295                     _this.gMapContext.locationName = results[0].formatted_address;
30296                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30297                     
30298                     _this.fireEvent('positionchanged', this, location);
30299                 }
30300             });
30301             
30302             return;
30303         }
30304         
30305         this.fireEvent('positionchanged', this, location);
30306     },
30307     
30308     resize: function()
30309     {
30310         google.maps.event.trigger(this.gMapContext.map, "resize");
30311         
30312         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30313         
30314         this.fireEvent('resize', this);
30315     },
30316     
30317     setPositionByLatLng: function(latitude, longitude)
30318     {
30319         this.setPosition(new google.maps.LatLng(latitude, longitude));
30320     },
30321     
30322     getCurrentPosition: function() 
30323     {
30324         return {
30325             latitude: this.gMapContext.location.lat(),
30326             longitude: this.gMapContext.location.lng()
30327         };
30328     },
30329     
30330     getAddressName: function() 
30331     {
30332         return this.gMapContext.locationName;
30333     },
30334     
30335     getAddressComponents: function() 
30336     {
30337         return this.gMapContext.addressComponents;
30338     },
30339     
30340     address_component_from_google_geocode: function(address_components) 
30341     {
30342         var result = {};
30343         
30344         for (var i = 0; i < address_components.length; i++) {
30345             var component = address_components[i];
30346             if (component.types.indexOf("postal_code") >= 0) {
30347                 result.postalCode = component.short_name;
30348             } else if (component.types.indexOf("street_number") >= 0) {
30349                 result.streetNumber = component.short_name;
30350             } else if (component.types.indexOf("route") >= 0) {
30351                 result.streetName = component.short_name;
30352             } else if (component.types.indexOf("neighborhood") >= 0) {
30353                 result.city = component.short_name;
30354             } else if (component.types.indexOf("locality") >= 0) {
30355                 result.city = component.short_name;
30356             } else if (component.types.indexOf("sublocality") >= 0) {
30357                 result.district = component.short_name;
30358             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30359                 result.stateOrProvince = component.short_name;
30360             } else if (component.types.indexOf("country") >= 0) {
30361                 result.country = component.short_name;
30362             }
30363         }
30364         
30365         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30366         result.addressLine2 = "";
30367         return result;
30368     },
30369     
30370     setZoomLevel: function(zoom)
30371     {
30372         this.gMapContext.map.setZoom(zoom);
30373     },
30374     
30375     show: function()
30376     {
30377         if(!this.el){
30378             return;
30379         }
30380         
30381         this.el.show();
30382         
30383         this.resize();
30384         
30385         this.fireEvent('show', this);
30386     },
30387     
30388     hide: function()
30389     {
30390         if(!this.el){
30391             return;
30392         }
30393         
30394         this.el.hide();
30395         
30396         this.fireEvent('hide', this);
30397     }
30398     
30399 });
30400
30401 Roo.apply(Roo.bootstrap.LocationPicker, {
30402     
30403     OverlayView : function(map, options)
30404     {
30405         options = options || {};
30406         
30407         this.setMap(map);
30408     }
30409     
30410     
30411 });/**
30412  * @class Roo.bootstrap.Alert
30413  * @extends Roo.bootstrap.Component
30414  * Bootstrap Alert class - shows an alert area box
30415  * eg
30416  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30417   Enter a valid email address
30418 </div>
30419  * @licence LGPL
30420  * @cfg {String} title The title of alert
30421  * @cfg {String} html The content of alert
30422  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30423  * @cfg {String} fa font-awesomeicon
30424  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30425  * @cfg {Boolean} close true to show a x closer
30426  * 
30427  * 
30428  * @constructor
30429  * Create a new alert
30430  * @param {Object} config The config object
30431  */
30432
30433
30434 Roo.bootstrap.Alert = function(config){
30435     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30436     
30437 };
30438
30439 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30440     
30441     title: '',
30442     html: '',
30443     weight: false,
30444     fa: false,
30445     faicon: false, // BC
30446     close : false,
30447     
30448     
30449     getAutoCreate : function()
30450     {
30451         
30452         var cfg = {
30453             tag : 'div',
30454             cls : 'alert',
30455             cn : [
30456                 {
30457                     tag: 'button',
30458                     type :  "button",
30459                     cls: "close",
30460                     html : '×',
30461                     style : this.close ? '' : 'display:none'
30462                 },
30463                 {
30464                     tag : 'i',
30465                     cls : 'roo-alert-icon'
30466                     
30467                 },
30468                 {
30469                     tag : 'b',
30470                     cls : 'roo-alert-title',
30471                     html : this.title
30472                 },
30473                 {
30474                     tag : 'span',
30475                     cls : 'roo-alert-text',
30476                     html : this.html
30477                 }
30478             ]
30479         };
30480         
30481         if(this.faicon){
30482             cfg.cn[0].cls += ' fa ' + this.faicon;
30483         }
30484         if(this.fa){
30485             cfg.cn[0].cls += ' fa ' + this.fa;
30486         }
30487         
30488         if(this.weight){
30489             cfg.cls += ' alert-' + this.weight;
30490         }
30491         
30492         return cfg;
30493     },
30494     
30495     initEvents: function() 
30496     {
30497         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30498         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30499         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30500         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30501         if (this.seconds > 0) {
30502             this.hide.defer(this.seconds, this);
30503         }
30504     },
30505     /**
30506      * Set the Title Message HTML
30507      * @param {String} html
30508      */
30509     setTitle : function(str)
30510     {
30511         this.titleEl.dom.innerHTML = str;
30512     },
30513      
30514      /**
30515      * Set the Body Message HTML
30516      * @param {String} html
30517      */
30518     setHtml : function(str)
30519     {
30520         this.htmlEl.dom.innerHTML = str;
30521     },
30522     /**
30523      * Set the Weight of the alert
30524      * @param {String} (success|info|warning|danger) weight
30525      */
30526     
30527     setWeight : function(weight)
30528     {
30529         if(this.weight){
30530             this.el.removeClass('alert-' + this.weight);
30531         }
30532         
30533         this.weight = weight;
30534         
30535         this.el.addClass('alert-' + this.weight);
30536     },
30537       /**
30538      * Set the Icon of the alert
30539      * @param {String} see fontawsome names (name without the 'fa-' bit)
30540      */
30541     setIcon : function(icon)
30542     {
30543         if(this.faicon){
30544             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30545         }
30546         
30547         this.faicon = icon;
30548         
30549         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30550     },
30551     /**
30552      * Hide the Alert
30553      */
30554     hide: function() 
30555     {
30556         this.el.hide();   
30557     },
30558     /**
30559      * Show the Alert
30560      */
30561     show: function() 
30562     {  
30563         this.el.show();   
30564     }
30565     
30566 });
30567
30568  
30569 /*
30570 * Licence: LGPL
30571 */
30572
30573 /**
30574  * @class Roo.bootstrap.UploadCropbox
30575  * @extends Roo.bootstrap.Component
30576  * Bootstrap UploadCropbox class
30577  * @cfg {String} emptyText show when image has been loaded
30578  * @cfg {String} rotateNotify show when image too small to rotate
30579  * @cfg {Number} errorTimeout default 3000
30580  * @cfg {Number} minWidth default 300
30581  * @cfg {Number} minHeight default 300
30582  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30583  * @cfg {Boolean} isDocument (true|false) default false
30584  * @cfg {String} url action url
30585  * @cfg {String} paramName default 'imageUpload'
30586  * @cfg {String} method default POST
30587  * @cfg {Boolean} loadMask (true|false) default true
30588  * @cfg {Boolean} loadingText default 'Loading...'
30589  * 
30590  * @constructor
30591  * Create a new UploadCropbox
30592  * @param {Object} config The config object
30593  */
30594
30595 Roo.bootstrap.UploadCropbox = function(config){
30596     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30597     
30598     this.addEvents({
30599         /**
30600          * @event beforeselectfile
30601          * Fire before select file
30602          * @param {Roo.bootstrap.UploadCropbox} this
30603          */
30604         "beforeselectfile" : true,
30605         /**
30606          * @event initial
30607          * Fire after initEvent
30608          * @param {Roo.bootstrap.UploadCropbox} this
30609          */
30610         "initial" : true,
30611         /**
30612          * @event crop
30613          * Fire after initEvent
30614          * @param {Roo.bootstrap.UploadCropbox} this
30615          * @param {String} data
30616          */
30617         "crop" : true,
30618         /**
30619          * @event prepare
30620          * Fire when preparing the file data
30621          * @param {Roo.bootstrap.UploadCropbox} this
30622          * @param {Object} file
30623          */
30624         "prepare" : true,
30625         /**
30626          * @event exception
30627          * Fire when get exception
30628          * @param {Roo.bootstrap.UploadCropbox} this
30629          * @param {XMLHttpRequest} xhr
30630          */
30631         "exception" : true,
30632         /**
30633          * @event beforeloadcanvas
30634          * Fire before load the canvas
30635          * @param {Roo.bootstrap.UploadCropbox} this
30636          * @param {String} src
30637          */
30638         "beforeloadcanvas" : true,
30639         /**
30640          * @event trash
30641          * Fire when trash image
30642          * @param {Roo.bootstrap.UploadCropbox} this
30643          */
30644         "trash" : true,
30645         /**
30646          * @event download
30647          * Fire when download the image
30648          * @param {Roo.bootstrap.UploadCropbox} this
30649          */
30650         "download" : true,
30651         /**
30652          * @event footerbuttonclick
30653          * Fire when footerbuttonclick
30654          * @param {Roo.bootstrap.UploadCropbox} this
30655          * @param {String} type
30656          */
30657         "footerbuttonclick" : true,
30658         /**
30659          * @event resize
30660          * Fire when resize
30661          * @param {Roo.bootstrap.UploadCropbox} this
30662          */
30663         "resize" : true,
30664         /**
30665          * @event rotate
30666          * Fire when rotate the image
30667          * @param {Roo.bootstrap.UploadCropbox} this
30668          * @param {String} pos
30669          */
30670         "rotate" : true,
30671         /**
30672          * @event inspect
30673          * Fire when inspect the file
30674          * @param {Roo.bootstrap.UploadCropbox} this
30675          * @param {Object} file
30676          */
30677         "inspect" : true,
30678         /**
30679          * @event upload
30680          * Fire when xhr upload the file
30681          * @param {Roo.bootstrap.UploadCropbox} this
30682          * @param {Object} data
30683          */
30684         "upload" : true,
30685         /**
30686          * @event arrange
30687          * Fire when arrange the file data
30688          * @param {Roo.bootstrap.UploadCropbox} this
30689          * @param {Object} formData
30690          */
30691         "arrange" : true
30692     });
30693     
30694     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30695 };
30696
30697 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30698     
30699     emptyText : 'Click to upload image',
30700     rotateNotify : 'Image is too small to rotate',
30701     errorTimeout : 3000,
30702     scale : 0,
30703     baseScale : 1,
30704     rotate : 0,
30705     dragable : false,
30706     pinching : false,
30707     mouseX : 0,
30708     mouseY : 0,
30709     cropData : false,
30710     minWidth : 300,
30711     minHeight : 300,
30712     file : false,
30713     exif : {},
30714     baseRotate : 1,
30715     cropType : 'image/jpeg',
30716     buttons : false,
30717     canvasLoaded : false,
30718     isDocument : false,
30719     method : 'POST',
30720     paramName : 'imageUpload',
30721     loadMask : true,
30722     loadingText : 'Loading...',
30723     maskEl : false,
30724     
30725     getAutoCreate : function()
30726     {
30727         var cfg = {
30728             tag : 'div',
30729             cls : 'roo-upload-cropbox',
30730             cn : [
30731                 {
30732                     tag : 'input',
30733                     cls : 'roo-upload-cropbox-selector',
30734                     type : 'file'
30735                 },
30736                 {
30737                     tag : 'div',
30738                     cls : 'roo-upload-cropbox-body',
30739                     style : 'cursor:pointer',
30740                     cn : [
30741                         {
30742                             tag : 'div',
30743                             cls : 'roo-upload-cropbox-preview'
30744                         },
30745                         {
30746                             tag : 'div',
30747                             cls : 'roo-upload-cropbox-thumb'
30748                         },
30749                         {
30750                             tag : 'div',
30751                             cls : 'roo-upload-cropbox-empty-notify',
30752                             html : this.emptyText
30753                         },
30754                         {
30755                             tag : 'div',
30756                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30757                             html : this.rotateNotify
30758                         }
30759                     ]
30760                 },
30761                 {
30762                     tag : 'div',
30763                     cls : 'roo-upload-cropbox-footer',
30764                     cn : {
30765                         tag : 'div',
30766                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30767                         cn : []
30768                     }
30769                 }
30770             ]
30771         };
30772         
30773         return cfg;
30774     },
30775     
30776     onRender : function(ct, position)
30777     {
30778         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30779         
30780         if (this.buttons.length) {
30781             
30782             Roo.each(this.buttons, function(bb) {
30783                 
30784                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30785                 
30786                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30787                 
30788             }, this);
30789         }
30790         
30791         if(this.loadMask){
30792             this.maskEl = this.el;
30793         }
30794     },
30795     
30796     initEvents : function()
30797     {
30798         this.urlAPI = (window.createObjectURL && window) || 
30799                                 (window.URL && URL.revokeObjectURL && URL) || 
30800                                 (window.webkitURL && webkitURL);
30801                         
30802         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30803         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30804         
30805         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30806         this.selectorEl.hide();
30807         
30808         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30809         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30810         
30811         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30812         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30813         this.thumbEl.hide();
30814         
30815         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30816         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30817         
30818         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30819         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30820         this.errorEl.hide();
30821         
30822         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30823         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30824         this.footerEl.hide();
30825         
30826         this.setThumbBoxSize();
30827         
30828         this.bind();
30829         
30830         this.resize();
30831         
30832         this.fireEvent('initial', this);
30833     },
30834
30835     bind : function()
30836     {
30837         var _this = this;
30838         
30839         window.addEventListener("resize", function() { _this.resize(); } );
30840         
30841         this.bodyEl.on('click', this.beforeSelectFile, this);
30842         
30843         if(Roo.isTouch){
30844             this.bodyEl.on('touchstart', this.onTouchStart, this);
30845             this.bodyEl.on('touchmove', this.onTouchMove, this);
30846             this.bodyEl.on('touchend', this.onTouchEnd, this);
30847         }
30848         
30849         if(!Roo.isTouch){
30850             this.bodyEl.on('mousedown', this.onMouseDown, this);
30851             this.bodyEl.on('mousemove', this.onMouseMove, this);
30852             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30853             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30854             Roo.get(document).on('mouseup', this.onMouseUp, this);
30855         }
30856         
30857         this.selectorEl.on('change', this.onFileSelected, this);
30858     },
30859     
30860     reset : function()
30861     {    
30862         this.scale = 0;
30863         this.baseScale = 1;
30864         this.rotate = 0;
30865         this.baseRotate = 1;
30866         this.dragable = false;
30867         this.pinching = false;
30868         this.mouseX = 0;
30869         this.mouseY = 0;
30870         this.cropData = false;
30871         this.notifyEl.dom.innerHTML = this.emptyText;
30872         
30873         this.selectorEl.dom.value = '';
30874         
30875     },
30876     
30877     resize : function()
30878     {
30879         if(this.fireEvent('resize', this) != false){
30880             this.setThumbBoxPosition();
30881             this.setCanvasPosition();
30882         }
30883     },
30884     
30885     onFooterButtonClick : function(e, el, o, type)
30886     {
30887         switch (type) {
30888             case 'rotate-left' :
30889                 this.onRotateLeft(e);
30890                 break;
30891             case 'rotate-right' :
30892                 this.onRotateRight(e);
30893                 break;
30894             case 'picture' :
30895                 this.beforeSelectFile(e);
30896                 break;
30897             case 'trash' :
30898                 this.trash(e);
30899                 break;
30900             case 'crop' :
30901                 this.crop(e);
30902                 break;
30903             case 'download' :
30904                 this.download(e);
30905                 break;
30906             default :
30907                 break;
30908         }
30909         
30910         this.fireEvent('footerbuttonclick', this, type);
30911     },
30912     
30913     beforeSelectFile : function(e)
30914     {
30915         e.preventDefault();
30916         
30917         if(this.fireEvent('beforeselectfile', this) != false){
30918             this.selectorEl.dom.click();
30919         }
30920     },
30921     
30922     onFileSelected : function(e)
30923     {
30924         e.preventDefault();
30925         
30926         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30927             return;
30928         }
30929         
30930         var file = this.selectorEl.dom.files[0];
30931         
30932         if(this.fireEvent('inspect', this, file) != false){
30933             this.prepare(file);
30934         }
30935         
30936     },
30937     
30938     trash : function(e)
30939     {
30940         this.fireEvent('trash', this);
30941     },
30942     
30943     download : function(e)
30944     {
30945         this.fireEvent('download', this);
30946     },
30947     
30948     loadCanvas : function(src)
30949     {   
30950         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30951             
30952             this.reset();
30953             
30954             this.imageEl = document.createElement('img');
30955             
30956             var _this = this;
30957             
30958             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30959             
30960             this.imageEl.src = src;
30961         }
30962     },
30963     
30964     onLoadCanvas : function()
30965     {   
30966         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30967         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30968         
30969         this.bodyEl.un('click', this.beforeSelectFile, this);
30970         
30971         this.notifyEl.hide();
30972         this.thumbEl.show();
30973         this.footerEl.show();
30974         
30975         this.baseRotateLevel();
30976         
30977         if(this.isDocument){
30978             this.setThumbBoxSize();
30979         }
30980         
30981         this.setThumbBoxPosition();
30982         
30983         this.baseScaleLevel();
30984         
30985         this.draw();
30986         
30987         this.resize();
30988         
30989         this.canvasLoaded = true;
30990         
30991         if(this.loadMask){
30992             this.maskEl.unmask();
30993         }
30994         
30995     },
30996     
30997     setCanvasPosition : function()
30998     {   
30999         if(!this.canvasEl){
31000             return;
31001         }
31002         
31003         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31004         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31005         
31006         this.previewEl.setLeft(pw);
31007         this.previewEl.setTop(ph);
31008         
31009     },
31010     
31011     onMouseDown : function(e)
31012     {   
31013         e.stopEvent();
31014         
31015         this.dragable = true;
31016         this.pinching = false;
31017         
31018         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31019             this.dragable = false;
31020             return;
31021         }
31022         
31023         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31024         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31025         
31026     },
31027     
31028     onMouseMove : function(e)
31029     {   
31030         e.stopEvent();
31031         
31032         if(!this.canvasLoaded){
31033             return;
31034         }
31035         
31036         if (!this.dragable){
31037             return;
31038         }
31039         
31040         var minX = Math.ceil(this.thumbEl.getLeft(true));
31041         var minY = Math.ceil(this.thumbEl.getTop(true));
31042         
31043         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31044         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31045         
31046         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31047         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31048         
31049         x = x - this.mouseX;
31050         y = y - this.mouseY;
31051         
31052         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31053         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31054         
31055         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31056         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31057         
31058         this.previewEl.setLeft(bgX);
31059         this.previewEl.setTop(bgY);
31060         
31061         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31062         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31063     },
31064     
31065     onMouseUp : function(e)
31066     {   
31067         e.stopEvent();
31068         
31069         this.dragable = false;
31070     },
31071     
31072     onMouseWheel : function(e)
31073     {   
31074         e.stopEvent();
31075         
31076         this.startScale = this.scale;
31077         
31078         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31079         
31080         if(!this.zoomable()){
31081             this.scale = this.startScale;
31082             return;
31083         }
31084         
31085         this.draw();
31086         
31087         return;
31088     },
31089     
31090     zoomable : function()
31091     {
31092         var minScale = this.thumbEl.getWidth() / this.minWidth;
31093         
31094         if(this.minWidth < this.minHeight){
31095             minScale = this.thumbEl.getHeight() / this.minHeight;
31096         }
31097         
31098         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31099         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31100         
31101         if(
31102                 this.isDocument &&
31103                 (this.rotate == 0 || this.rotate == 180) && 
31104                 (
31105                     width > this.imageEl.OriginWidth || 
31106                     height > this.imageEl.OriginHeight ||
31107                     (width < this.minWidth && height < this.minHeight)
31108                 )
31109         ){
31110             return false;
31111         }
31112         
31113         if(
31114                 this.isDocument &&
31115                 (this.rotate == 90 || this.rotate == 270) && 
31116                 (
31117                     width > this.imageEl.OriginWidth || 
31118                     height > this.imageEl.OriginHeight ||
31119                     (width < this.minHeight && height < this.minWidth)
31120                 )
31121         ){
31122             return false;
31123         }
31124         
31125         if(
31126                 !this.isDocument &&
31127                 (this.rotate == 0 || this.rotate == 180) && 
31128                 (
31129                     width < this.minWidth || 
31130                     width > this.imageEl.OriginWidth || 
31131                     height < this.minHeight || 
31132                     height > this.imageEl.OriginHeight
31133                 )
31134         ){
31135             return false;
31136         }
31137         
31138         if(
31139                 !this.isDocument &&
31140                 (this.rotate == 90 || this.rotate == 270) && 
31141                 (
31142                     width < this.minHeight || 
31143                     width > this.imageEl.OriginWidth || 
31144                     height < this.minWidth || 
31145                     height > this.imageEl.OriginHeight
31146                 )
31147         ){
31148             return false;
31149         }
31150         
31151         return true;
31152         
31153     },
31154     
31155     onRotateLeft : function(e)
31156     {   
31157         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31158             
31159             var minScale = this.thumbEl.getWidth() / this.minWidth;
31160             
31161             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31162             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31163             
31164             this.startScale = this.scale;
31165             
31166             while (this.getScaleLevel() < minScale){
31167             
31168                 this.scale = this.scale + 1;
31169                 
31170                 if(!this.zoomable()){
31171                     break;
31172                 }
31173                 
31174                 if(
31175                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31176                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31177                 ){
31178                     continue;
31179                 }
31180                 
31181                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31182
31183                 this.draw();
31184                 
31185                 return;
31186             }
31187             
31188             this.scale = this.startScale;
31189             
31190             this.onRotateFail();
31191             
31192             return false;
31193         }
31194         
31195         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31196
31197         if(this.isDocument){
31198             this.setThumbBoxSize();
31199             this.setThumbBoxPosition();
31200             this.setCanvasPosition();
31201         }
31202         
31203         this.draw();
31204         
31205         this.fireEvent('rotate', this, 'left');
31206         
31207     },
31208     
31209     onRotateRight : function(e)
31210     {
31211         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31212             
31213             var minScale = this.thumbEl.getWidth() / this.minWidth;
31214         
31215             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31216             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31217             
31218             this.startScale = this.scale;
31219             
31220             while (this.getScaleLevel() < minScale){
31221             
31222                 this.scale = this.scale + 1;
31223                 
31224                 if(!this.zoomable()){
31225                     break;
31226                 }
31227                 
31228                 if(
31229                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31230                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31231                 ){
31232                     continue;
31233                 }
31234                 
31235                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31236
31237                 this.draw();
31238                 
31239                 return;
31240             }
31241             
31242             this.scale = this.startScale;
31243             
31244             this.onRotateFail();
31245             
31246             return false;
31247         }
31248         
31249         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31250
31251         if(this.isDocument){
31252             this.setThumbBoxSize();
31253             this.setThumbBoxPosition();
31254             this.setCanvasPosition();
31255         }
31256         
31257         this.draw();
31258         
31259         this.fireEvent('rotate', this, 'right');
31260     },
31261     
31262     onRotateFail : function()
31263     {
31264         this.errorEl.show(true);
31265         
31266         var _this = this;
31267         
31268         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31269     },
31270     
31271     draw : function()
31272     {
31273         this.previewEl.dom.innerHTML = '';
31274         
31275         var canvasEl = document.createElement("canvas");
31276         
31277         var contextEl = canvasEl.getContext("2d");
31278         
31279         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31280         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31281         var center = this.imageEl.OriginWidth / 2;
31282         
31283         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31284             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31285             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31286             center = this.imageEl.OriginHeight / 2;
31287         }
31288         
31289         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31290         
31291         contextEl.translate(center, center);
31292         contextEl.rotate(this.rotate * Math.PI / 180);
31293
31294         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31295         
31296         this.canvasEl = document.createElement("canvas");
31297         
31298         this.contextEl = this.canvasEl.getContext("2d");
31299         
31300         switch (this.rotate) {
31301             case 0 :
31302                 
31303                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31304                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31305                 
31306                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31307                 
31308                 break;
31309             case 90 : 
31310                 
31311                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31312                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31313                 
31314                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31315                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31316                     break;
31317                 }
31318                 
31319                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31320                 
31321                 break;
31322             case 180 :
31323                 
31324                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31325                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31326                 
31327                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31328                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31329                     break;
31330                 }
31331                 
31332                 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);
31333                 
31334                 break;
31335             case 270 :
31336                 
31337                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31338                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31339         
31340                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31341                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31342                     break;
31343                 }
31344                 
31345                 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);
31346                 
31347                 break;
31348             default : 
31349                 break;
31350         }
31351         
31352         this.previewEl.appendChild(this.canvasEl);
31353         
31354         this.setCanvasPosition();
31355     },
31356     
31357     crop : function()
31358     {
31359         if(!this.canvasLoaded){
31360             return;
31361         }
31362         
31363         var imageCanvas = document.createElement("canvas");
31364         
31365         var imageContext = imageCanvas.getContext("2d");
31366         
31367         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31368         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31369         
31370         var center = imageCanvas.width / 2;
31371         
31372         imageContext.translate(center, center);
31373         
31374         imageContext.rotate(this.rotate * Math.PI / 180);
31375         
31376         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31377         
31378         var canvas = document.createElement("canvas");
31379         
31380         var context = canvas.getContext("2d");
31381                 
31382         canvas.width = this.minWidth;
31383         canvas.height = this.minHeight;
31384
31385         switch (this.rotate) {
31386             case 0 :
31387                 
31388                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31389                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31390                 
31391                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31392                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31393                 
31394                 var targetWidth = this.minWidth - 2 * x;
31395                 var targetHeight = this.minHeight - 2 * y;
31396                 
31397                 var scale = 1;
31398                 
31399                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31400                     scale = targetWidth / width;
31401                 }
31402                 
31403                 if(x > 0 && y == 0){
31404                     scale = targetHeight / height;
31405                 }
31406                 
31407                 if(x > 0 && y > 0){
31408                     scale = targetWidth / width;
31409                     
31410                     if(width < height){
31411                         scale = targetHeight / height;
31412                     }
31413                 }
31414                 
31415                 context.scale(scale, scale);
31416                 
31417                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31418                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31419
31420                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31421                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31422
31423                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31424                 
31425                 break;
31426             case 90 : 
31427                 
31428                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31429                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31430                 
31431                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31432                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31433                 
31434                 var targetWidth = this.minWidth - 2 * x;
31435                 var targetHeight = this.minHeight - 2 * y;
31436                 
31437                 var scale = 1;
31438                 
31439                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31440                     scale = targetWidth / width;
31441                 }
31442                 
31443                 if(x > 0 && y == 0){
31444                     scale = targetHeight / height;
31445                 }
31446                 
31447                 if(x > 0 && y > 0){
31448                     scale = targetWidth / width;
31449                     
31450                     if(width < height){
31451                         scale = targetHeight / height;
31452                     }
31453                 }
31454                 
31455                 context.scale(scale, scale);
31456                 
31457                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31458                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31459
31460                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31461                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31462                 
31463                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31464                 
31465                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31466                 
31467                 break;
31468             case 180 :
31469                 
31470                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31471                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31472                 
31473                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31474                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31475                 
31476                 var targetWidth = this.minWidth - 2 * x;
31477                 var targetHeight = this.minHeight - 2 * y;
31478                 
31479                 var scale = 1;
31480                 
31481                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31482                     scale = targetWidth / width;
31483                 }
31484                 
31485                 if(x > 0 && y == 0){
31486                     scale = targetHeight / height;
31487                 }
31488                 
31489                 if(x > 0 && y > 0){
31490                     scale = targetWidth / width;
31491                     
31492                     if(width < height){
31493                         scale = targetHeight / height;
31494                     }
31495                 }
31496                 
31497                 context.scale(scale, scale);
31498                 
31499                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31500                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31501
31502                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31503                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31504
31505                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31506                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31507                 
31508                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31509                 
31510                 break;
31511             case 270 :
31512                 
31513                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31514                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31515                 
31516                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31517                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31518                 
31519                 var targetWidth = this.minWidth - 2 * x;
31520                 var targetHeight = this.minHeight - 2 * y;
31521                 
31522                 var scale = 1;
31523                 
31524                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31525                     scale = targetWidth / width;
31526                 }
31527                 
31528                 if(x > 0 && y == 0){
31529                     scale = targetHeight / height;
31530                 }
31531                 
31532                 if(x > 0 && y > 0){
31533                     scale = targetWidth / width;
31534                     
31535                     if(width < height){
31536                         scale = targetHeight / height;
31537                     }
31538                 }
31539                 
31540                 context.scale(scale, scale);
31541                 
31542                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31543                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31544
31545                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31546                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31547                 
31548                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31549                 
31550                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31551                 
31552                 break;
31553             default : 
31554                 break;
31555         }
31556         
31557         this.cropData = canvas.toDataURL(this.cropType);
31558         
31559         if(this.fireEvent('crop', this, this.cropData) !== false){
31560             this.process(this.file, this.cropData);
31561         }
31562         
31563         return;
31564         
31565     },
31566     
31567     setThumbBoxSize : function()
31568     {
31569         var width, height;
31570         
31571         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31572             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31573             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31574             
31575             this.minWidth = width;
31576             this.minHeight = height;
31577             
31578             if(this.rotate == 90 || this.rotate == 270){
31579                 this.minWidth = height;
31580                 this.minHeight = width;
31581             }
31582         }
31583         
31584         height = 300;
31585         width = Math.ceil(this.minWidth * height / this.minHeight);
31586         
31587         if(this.minWidth > this.minHeight){
31588             width = 300;
31589             height = Math.ceil(this.minHeight * width / this.minWidth);
31590         }
31591         
31592         this.thumbEl.setStyle({
31593             width : width + 'px',
31594             height : height + 'px'
31595         });
31596
31597         return;
31598             
31599     },
31600     
31601     setThumbBoxPosition : function()
31602     {
31603         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31604         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31605         
31606         this.thumbEl.setLeft(x);
31607         this.thumbEl.setTop(y);
31608         
31609     },
31610     
31611     baseRotateLevel : function()
31612     {
31613         this.baseRotate = 1;
31614         
31615         if(
31616                 typeof(this.exif) != 'undefined' &&
31617                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31618                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31619         ){
31620             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31621         }
31622         
31623         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31624         
31625     },
31626     
31627     baseScaleLevel : function()
31628     {
31629         var width, height;
31630         
31631         if(this.isDocument){
31632             
31633             if(this.baseRotate == 6 || this.baseRotate == 8){
31634             
31635                 height = this.thumbEl.getHeight();
31636                 this.baseScale = height / this.imageEl.OriginWidth;
31637
31638                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31639                     width = this.thumbEl.getWidth();
31640                     this.baseScale = width / this.imageEl.OriginHeight;
31641                 }
31642
31643                 return;
31644             }
31645
31646             height = this.thumbEl.getHeight();
31647             this.baseScale = height / this.imageEl.OriginHeight;
31648
31649             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31650                 width = this.thumbEl.getWidth();
31651                 this.baseScale = width / this.imageEl.OriginWidth;
31652             }
31653
31654             return;
31655         }
31656         
31657         if(this.baseRotate == 6 || this.baseRotate == 8){
31658             
31659             width = this.thumbEl.getHeight();
31660             this.baseScale = width / this.imageEl.OriginHeight;
31661             
31662             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31663                 height = this.thumbEl.getWidth();
31664                 this.baseScale = height / this.imageEl.OriginHeight;
31665             }
31666             
31667             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31668                 height = this.thumbEl.getWidth();
31669                 this.baseScale = height / this.imageEl.OriginHeight;
31670                 
31671                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31672                     width = this.thumbEl.getHeight();
31673                     this.baseScale = width / this.imageEl.OriginWidth;
31674                 }
31675             }
31676             
31677             return;
31678         }
31679         
31680         width = this.thumbEl.getWidth();
31681         this.baseScale = width / this.imageEl.OriginWidth;
31682         
31683         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31684             height = this.thumbEl.getHeight();
31685             this.baseScale = height / this.imageEl.OriginHeight;
31686         }
31687         
31688         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31689             
31690             height = this.thumbEl.getHeight();
31691             this.baseScale = height / this.imageEl.OriginHeight;
31692             
31693             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31694                 width = this.thumbEl.getWidth();
31695                 this.baseScale = width / this.imageEl.OriginWidth;
31696             }
31697             
31698         }
31699         
31700         return;
31701     },
31702     
31703     getScaleLevel : function()
31704     {
31705         return this.baseScale * Math.pow(1.1, this.scale);
31706     },
31707     
31708     onTouchStart : function(e)
31709     {
31710         if(!this.canvasLoaded){
31711             this.beforeSelectFile(e);
31712             return;
31713         }
31714         
31715         var touches = e.browserEvent.touches;
31716         
31717         if(!touches){
31718             return;
31719         }
31720         
31721         if(touches.length == 1){
31722             this.onMouseDown(e);
31723             return;
31724         }
31725         
31726         if(touches.length != 2){
31727             return;
31728         }
31729         
31730         var coords = [];
31731         
31732         for(var i = 0, finger; finger = touches[i]; i++){
31733             coords.push(finger.pageX, finger.pageY);
31734         }
31735         
31736         var x = Math.pow(coords[0] - coords[2], 2);
31737         var y = Math.pow(coords[1] - coords[3], 2);
31738         
31739         this.startDistance = Math.sqrt(x + y);
31740         
31741         this.startScale = this.scale;
31742         
31743         this.pinching = true;
31744         this.dragable = false;
31745         
31746     },
31747     
31748     onTouchMove : function(e)
31749     {
31750         if(!this.pinching && !this.dragable){
31751             return;
31752         }
31753         
31754         var touches = e.browserEvent.touches;
31755         
31756         if(!touches){
31757             return;
31758         }
31759         
31760         if(this.dragable){
31761             this.onMouseMove(e);
31762             return;
31763         }
31764         
31765         var coords = [];
31766         
31767         for(var i = 0, finger; finger = touches[i]; i++){
31768             coords.push(finger.pageX, finger.pageY);
31769         }
31770         
31771         var x = Math.pow(coords[0] - coords[2], 2);
31772         var y = Math.pow(coords[1] - coords[3], 2);
31773         
31774         this.endDistance = Math.sqrt(x + y);
31775         
31776         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31777         
31778         if(!this.zoomable()){
31779             this.scale = this.startScale;
31780             return;
31781         }
31782         
31783         this.draw();
31784         
31785     },
31786     
31787     onTouchEnd : function(e)
31788     {
31789         this.pinching = false;
31790         this.dragable = false;
31791         
31792     },
31793     
31794     process : function(file, crop)
31795     {
31796         if(this.loadMask){
31797             this.maskEl.mask(this.loadingText);
31798         }
31799         
31800         this.xhr = new XMLHttpRequest();
31801         
31802         file.xhr = this.xhr;
31803
31804         this.xhr.open(this.method, this.url, true);
31805         
31806         var headers = {
31807             "Accept": "application/json",
31808             "Cache-Control": "no-cache",
31809             "X-Requested-With": "XMLHttpRequest"
31810         };
31811         
31812         for (var headerName in headers) {
31813             var headerValue = headers[headerName];
31814             if (headerValue) {
31815                 this.xhr.setRequestHeader(headerName, headerValue);
31816             }
31817         }
31818         
31819         var _this = this;
31820         
31821         this.xhr.onload = function()
31822         {
31823             _this.xhrOnLoad(_this.xhr);
31824         }
31825         
31826         this.xhr.onerror = function()
31827         {
31828             _this.xhrOnError(_this.xhr);
31829         }
31830         
31831         var formData = new FormData();
31832
31833         formData.append('returnHTML', 'NO');
31834         
31835         if(crop){
31836             formData.append('crop', crop);
31837         }
31838         
31839         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31840             formData.append(this.paramName, file, file.name);
31841         }
31842         
31843         if(typeof(file.filename) != 'undefined'){
31844             formData.append('filename', file.filename);
31845         }
31846         
31847         if(typeof(file.mimetype) != 'undefined'){
31848             formData.append('mimetype', file.mimetype);
31849         }
31850         
31851         if(this.fireEvent('arrange', this, formData) != false){
31852             this.xhr.send(formData);
31853         };
31854     },
31855     
31856     xhrOnLoad : function(xhr)
31857     {
31858         if(this.loadMask){
31859             this.maskEl.unmask();
31860         }
31861         
31862         if (xhr.readyState !== 4) {
31863             this.fireEvent('exception', this, xhr);
31864             return;
31865         }
31866
31867         var response = Roo.decode(xhr.responseText);
31868         
31869         if(!response.success){
31870             this.fireEvent('exception', this, xhr);
31871             return;
31872         }
31873         
31874         var response = Roo.decode(xhr.responseText);
31875         
31876         this.fireEvent('upload', this, response);
31877         
31878     },
31879     
31880     xhrOnError : function()
31881     {
31882         if(this.loadMask){
31883             this.maskEl.unmask();
31884         }
31885         
31886         Roo.log('xhr on error');
31887         
31888         var response = Roo.decode(xhr.responseText);
31889           
31890         Roo.log(response);
31891         
31892     },
31893     
31894     prepare : function(file)
31895     {   
31896         if(this.loadMask){
31897             this.maskEl.mask(this.loadingText);
31898         }
31899         
31900         this.file = false;
31901         this.exif = {};
31902         
31903         if(typeof(file) === 'string'){
31904             this.loadCanvas(file);
31905             return;
31906         }
31907         
31908         if(!file || !this.urlAPI){
31909             return;
31910         }
31911         
31912         this.file = file;
31913         this.cropType = file.type;
31914         
31915         var _this = this;
31916         
31917         if(this.fireEvent('prepare', this, this.file) != false){
31918             
31919             var reader = new FileReader();
31920             
31921             reader.onload = function (e) {
31922                 if (e.target.error) {
31923                     Roo.log(e.target.error);
31924                     return;
31925                 }
31926                 
31927                 var buffer = e.target.result,
31928                     dataView = new DataView(buffer),
31929                     offset = 2,
31930                     maxOffset = dataView.byteLength - 4,
31931                     markerBytes,
31932                     markerLength;
31933                 
31934                 if (dataView.getUint16(0) === 0xffd8) {
31935                     while (offset < maxOffset) {
31936                         markerBytes = dataView.getUint16(offset);
31937                         
31938                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31939                             markerLength = dataView.getUint16(offset + 2) + 2;
31940                             if (offset + markerLength > dataView.byteLength) {
31941                                 Roo.log('Invalid meta data: Invalid segment size.');
31942                                 break;
31943                             }
31944                             
31945                             if(markerBytes == 0xffe1){
31946                                 _this.parseExifData(
31947                                     dataView,
31948                                     offset,
31949                                     markerLength
31950                                 );
31951                             }
31952                             
31953                             offset += markerLength;
31954                             
31955                             continue;
31956                         }
31957                         
31958                         break;
31959                     }
31960                     
31961                 }
31962                 
31963                 var url = _this.urlAPI.createObjectURL(_this.file);
31964                 
31965                 _this.loadCanvas(url);
31966                 
31967                 return;
31968             }
31969             
31970             reader.readAsArrayBuffer(this.file);
31971             
31972         }
31973         
31974     },
31975     
31976     parseExifData : function(dataView, offset, length)
31977     {
31978         var tiffOffset = offset + 10,
31979             littleEndian,
31980             dirOffset;
31981     
31982         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31983             // No Exif data, might be XMP data instead
31984             return;
31985         }
31986         
31987         // Check for the ASCII code for "Exif" (0x45786966):
31988         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31989             // No Exif data, might be XMP data instead
31990             return;
31991         }
31992         if (tiffOffset + 8 > dataView.byteLength) {
31993             Roo.log('Invalid Exif data: Invalid segment size.');
31994             return;
31995         }
31996         // Check for the two null bytes:
31997         if (dataView.getUint16(offset + 8) !== 0x0000) {
31998             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31999             return;
32000         }
32001         // Check the byte alignment:
32002         switch (dataView.getUint16(tiffOffset)) {
32003         case 0x4949:
32004             littleEndian = true;
32005             break;
32006         case 0x4D4D:
32007             littleEndian = false;
32008             break;
32009         default:
32010             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32011             return;
32012         }
32013         // Check for the TIFF tag marker (0x002A):
32014         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32015             Roo.log('Invalid Exif data: Missing TIFF marker.');
32016             return;
32017         }
32018         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32019         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32020         
32021         this.parseExifTags(
32022             dataView,
32023             tiffOffset,
32024             tiffOffset + dirOffset,
32025             littleEndian
32026         );
32027     },
32028     
32029     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32030     {
32031         var tagsNumber,
32032             dirEndOffset,
32033             i;
32034         if (dirOffset + 6 > dataView.byteLength) {
32035             Roo.log('Invalid Exif data: Invalid directory offset.');
32036             return;
32037         }
32038         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32039         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32040         if (dirEndOffset + 4 > dataView.byteLength) {
32041             Roo.log('Invalid Exif data: Invalid directory size.');
32042             return;
32043         }
32044         for (i = 0; i < tagsNumber; i += 1) {
32045             this.parseExifTag(
32046                 dataView,
32047                 tiffOffset,
32048                 dirOffset + 2 + 12 * i, // tag offset
32049                 littleEndian
32050             );
32051         }
32052         // Return the offset to the next directory:
32053         return dataView.getUint32(dirEndOffset, littleEndian);
32054     },
32055     
32056     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32057     {
32058         var tag = dataView.getUint16(offset, littleEndian);
32059         
32060         this.exif[tag] = this.getExifValue(
32061             dataView,
32062             tiffOffset,
32063             offset,
32064             dataView.getUint16(offset + 2, littleEndian), // tag type
32065             dataView.getUint32(offset + 4, littleEndian), // tag length
32066             littleEndian
32067         );
32068     },
32069     
32070     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32071     {
32072         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32073             tagSize,
32074             dataOffset,
32075             values,
32076             i,
32077             str,
32078             c;
32079     
32080         if (!tagType) {
32081             Roo.log('Invalid Exif data: Invalid tag type.');
32082             return;
32083         }
32084         
32085         tagSize = tagType.size * length;
32086         // Determine if the value is contained in the dataOffset bytes,
32087         // or if the value at the dataOffset is a pointer to the actual data:
32088         dataOffset = tagSize > 4 ?
32089                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32090         if (dataOffset + tagSize > dataView.byteLength) {
32091             Roo.log('Invalid Exif data: Invalid data offset.');
32092             return;
32093         }
32094         if (length === 1) {
32095             return tagType.getValue(dataView, dataOffset, littleEndian);
32096         }
32097         values = [];
32098         for (i = 0; i < length; i += 1) {
32099             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32100         }
32101         
32102         if (tagType.ascii) {
32103             str = '';
32104             // Concatenate the chars:
32105             for (i = 0; i < values.length; i += 1) {
32106                 c = values[i];
32107                 // Ignore the terminating NULL byte(s):
32108                 if (c === '\u0000') {
32109                     break;
32110                 }
32111                 str += c;
32112             }
32113             return str;
32114         }
32115         return values;
32116     }
32117     
32118 });
32119
32120 Roo.apply(Roo.bootstrap.UploadCropbox, {
32121     tags : {
32122         'Orientation': 0x0112
32123     },
32124     
32125     Orientation: {
32126             1: 0, //'top-left',
32127 //            2: 'top-right',
32128             3: 180, //'bottom-right',
32129 //            4: 'bottom-left',
32130 //            5: 'left-top',
32131             6: 90, //'right-top',
32132 //            7: 'right-bottom',
32133             8: 270 //'left-bottom'
32134     },
32135     
32136     exifTagTypes : {
32137         // byte, 8-bit unsigned int:
32138         1: {
32139             getValue: function (dataView, dataOffset) {
32140                 return dataView.getUint8(dataOffset);
32141             },
32142             size: 1
32143         },
32144         // ascii, 8-bit byte:
32145         2: {
32146             getValue: function (dataView, dataOffset) {
32147                 return String.fromCharCode(dataView.getUint8(dataOffset));
32148             },
32149             size: 1,
32150             ascii: true
32151         },
32152         // short, 16 bit int:
32153         3: {
32154             getValue: function (dataView, dataOffset, littleEndian) {
32155                 return dataView.getUint16(dataOffset, littleEndian);
32156             },
32157             size: 2
32158         },
32159         // long, 32 bit int:
32160         4: {
32161             getValue: function (dataView, dataOffset, littleEndian) {
32162                 return dataView.getUint32(dataOffset, littleEndian);
32163             },
32164             size: 4
32165         },
32166         // rational = two long values, first is numerator, second is denominator:
32167         5: {
32168             getValue: function (dataView, dataOffset, littleEndian) {
32169                 return dataView.getUint32(dataOffset, littleEndian) /
32170                     dataView.getUint32(dataOffset + 4, littleEndian);
32171             },
32172             size: 8
32173         },
32174         // slong, 32 bit signed int:
32175         9: {
32176             getValue: function (dataView, dataOffset, littleEndian) {
32177                 return dataView.getInt32(dataOffset, littleEndian);
32178             },
32179             size: 4
32180         },
32181         // srational, two slongs, first is numerator, second is denominator:
32182         10: {
32183             getValue: function (dataView, dataOffset, littleEndian) {
32184                 return dataView.getInt32(dataOffset, littleEndian) /
32185                     dataView.getInt32(dataOffset + 4, littleEndian);
32186             },
32187             size: 8
32188         }
32189     },
32190     
32191     footer : {
32192         STANDARD : [
32193             {
32194                 tag : 'div',
32195                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32196                 action : 'rotate-left',
32197                 cn : [
32198                     {
32199                         tag : 'button',
32200                         cls : 'btn btn-default',
32201                         html : '<i class="fa fa-undo"></i>'
32202                     }
32203                 ]
32204             },
32205             {
32206                 tag : 'div',
32207                 cls : 'btn-group roo-upload-cropbox-picture',
32208                 action : 'picture',
32209                 cn : [
32210                     {
32211                         tag : 'button',
32212                         cls : 'btn btn-default',
32213                         html : '<i class="fa fa-picture-o"></i>'
32214                     }
32215                 ]
32216             },
32217             {
32218                 tag : 'div',
32219                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32220                 action : 'rotate-right',
32221                 cn : [
32222                     {
32223                         tag : 'button',
32224                         cls : 'btn btn-default',
32225                         html : '<i class="fa fa-repeat"></i>'
32226                     }
32227                 ]
32228             }
32229         ],
32230         DOCUMENT : [
32231             {
32232                 tag : 'div',
32233                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32234                 action : 'rotate-left',
32235                 cn : [
32236                     {
32237                         tag : 'button',
32238                         cls : 'btn btn-default',
32239                         html : '<i class="fa fa-undo"></i>'
32240                     }
32241                 ]
32242             },
32243             {
32244                 tag : 'div',
32245                 cls : 'btn-group roo-upload-cropbox-download',
32246                 action : 'download',
32247                 cn : [
32248                     {
32249                         tag : 'button',
32250                         cls : 'btn btn-default',
32251                         html : '<i class="fa fa-download"></i>'
32252                     }
32253                 ]
32254             },
32255             {
32256                 tag : 'div',
32257                 cls : 'btn-group roo-upload-cropbox-crop',
32258                 action : 'crop',
32259                 cn : [
32260                     {
32261                         tag : 'button',
32262                         cls : 'btn btn-default',
32263                         html : '<i class="fa fa-crop"></i>'
32264                     }
32265                 ]
32266             },
32267             {
32268                 tag : 'div',
32269                 cls : 'btn-group roo-upload-cropbox-trash',
32270                 action : 'trash',
32271                 cn : [
32272                     {
32273                         tag : 'button',
32274                         cls : 'btn btn-default',
32275                         html : '<i class="fa fa-trash"></i>'
32276                     }
32277                 ]
32278             },
32279             {
32280                 tag : 'div',
32281                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32282                 action : 'rotate-right',
32283                 cn : [
32284                     {
32285                         tag : 'button',
32286                         cls : 'btn btn-default',
32287                         html : '<i class="fa fa-repeat"></i>'
32288                     }
32289                 ]
32290             }
32291         ],
32292         ROTATOR : [
32293             {
32294                 tag : 'div',
32295                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32296                 action : 'rotate-left',
32297                 cn : [
32298                     {
32299                         tag : 'button',
32300                         cls : 'btn btn-default',
32301                         html : '<i class="fa fa-undo"></i>'
32302                     }
32303                 ]
32304             },
32305             {
32306                 tag : 'div',
32307                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32308                 action : 'rotate-right',
32309                 cn : [
32310                     {
32311                         tag : 'button',
32312                         cls : 'btn btn-default',
32313                         html : '<i class="fa fa-repeat"></i>'
32314                     }
32315                 ]
32316             }
32317         ]
32318     }
32319 });
32320
32321 /*
32322 * Licence: LGPL
32323 */
32324
32325 /**
32326  * @class Roo.bootstrap.DocumentManager
32327  * @extends Roo.bootstrap.Component
32328  * Bootstrap DocumentManager class
32329  * @cfg {String} paramName default 'imageUpload'
32330  * @cfg {String} toolTipName default 'filename'
32331  * @cfg {String} method default POST
32332  * @cfg {String} url action url
32333  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32334  * @cfg {Boolean} multiple multiple upload default true
32335  * @cfg {Number} thumbSize default 300
32336  * @cfg {String} fieldLabel
32337  * @cfg {Number} labelWidth default 4
32338  * @cfg {String} labelAlign (left|top) default left
32339  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32340 * @cfg {Number} labellg set the width of label (1-12)
32341  * @cfg {Number} labelmd set the width of label (1-12)
32342  * @cfg {Number} labelsm set the width of label (1-12)
32343  * @cfg {Number} labelxs set the width of label (1-12)
32344  * 
32345  * @constructor
32346  * Create a new DocumentManager
32347  * @param {Object} config The config object
32348  */
32349
32350 Roo.bootstrap.DocumentManager = function(config){
32351     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32352     
32353     this.files = [];
32354     this.delegates = [];
32355     
32356     this.addEvents({
32357         /**
32358          * @event initial
32359          * Fire when initial the DocumentManager
32360          * @param {Roo.bootstrap.DocumentManager} this
32361          */
32362         "initial" : true,
32363         /**
32364          * @event inspect
32365          * inspect selected file
32366          * @param {Roo.bootstrap.DocumentManager} this
32367          * @param {File} file
32368          */
32369         "inspect" : true,
32370         /**
32371          * @event exception
32372          * Fire when xhr load exception
32373          * @param {Roo.bootstrap.DocumentManager} this
32374          * @param {XMLHttpRequest} xhr
32375          */
32376         "exception" : true,
32377         /**
32378          * @event afterupload
32379          * Fire when xhr load exception
32380          * @param {Roo.bootstrap.DocumentManager} this
32381          * @param {XMLHttpRequest} xhr
32382          */
32383         "afterupload" : true,
32384         /**
32385          * @event prepare
32386          * prepare the form data
32387          * @param {Roo.bootstrap.DocumentManager} this
32388          * @param {Object} formData
32389          */
32390         "prepare" : true,
32391         /**
32392          * @event remove
32393          * Fire when remove the file
32394          * @param {Roo.bootstrap.DocumentManager} this
32395          * @param {Object} file
32396          */
32397         "remove" : true,
32398         /**
32399          * @event refresh
32400          * Fire after refresh the file
32401          * @param {Roo.bootstrap.DocumentManager} this
32402          */
32403         "refresh" : true,
32404         /**
32405          * @event click
32406          * Fire after click the image
32407          * @param {Roo.bootstrap.DocumentManager} this
32408          * @param {Object} file
32409          */
32410         "click" : true,
32411         /**
32412          * @event edit
32413          * Fire when upload a image and editable set to true
32414          * @param {Roo.bootstrap.DocumentManager} this
32415          * @param {Object} file
32416          */
32417         "edit" : true,
32418         /**
32419          * @event beforeselectfile
32420          * Fire before select file
32421          * @param {Roo.bootstrap.DocumentManager} this
32422          */
32423         "beforeselectfile" : true,
32424         /**
32425          * @event process
32426          * Fire before process file
32427          * @param {Roo.bootstrap.DocumentManager} this
32428          * @param {Object} file
32429          */
32430         "process" : true,
32431         /**
32432          * @event previewrendered
32433          * Fire when preview rendered
32434          * @param {Roo.bootstrap.DocumentManager} this
32435          * @param {Object} file
32436          */
32437         "previewrendered" : true,
32438         /**
32439          */
32440         "previewResize" : true
32441         
32442     });
32443 };
32444
32445 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32446     
32447     boxes : 0,
32448     inputName : '',
32449     thumbSize : 300,
32450     multiple : true,
32451     files : false,
32452     method : 'POST',
32453     url : '',
32454     paramName : 'imageUpload',
32455     toolTipName : 'filename',
32456     fieldLabel : '',
32457     labelWidth : 4,
32458     labelAlign : 'left',
32459     editable : true,
32460     delegates : false,
32461     xhr : false, 
32462     
32463     labellg : 0,
32464     labelmd : 0,
32465     labelsm : 0,
32466     labelxs : 0,
32467     
32468     getAutoCreate : function()
32469     {   
32470         var managerWidget = {
32471             tag : 'div',
32472             cls : 'roo-document-manager',
32473             cn : [
32474                 {
32475                     tag : 'input',
32476                     cls : 'roo-document-manager-selector',
32477                     type : 'file'
32478                 },
32479                 {
32480                     tag : 'div',
32481                     cls : 'roo-document-manager-uploader',
32482                     cn : [
32483                         {
32484                             tag : 'div',
32485                             cls : 'roo-document-manager-upload-btn',
32486                             html : '<i class="fa fa-plus"></i>'
32487                         }
32488                     ]
32489                     
32490                 }
32491             ]
32492         };
32493         
32494         var content = [
32495             {
32496                 tag : 'div',
32497                 cls : 'column col-md-12',
32498                 cn : managerWidget
32499             }
32500         ];
32501         
32502         if(this.fieldLabel.length){
32503             
32504             content = [
32505                 {
32506                     tag : 'div',
32507                     cls : 'column col-md-12',
32508                     html : this.fieldLabel
32509                 },
32510                 {
32511                     tag : 'div',
32512                     cls : 'column col-md-12',
32513                     cn : managerWidget
32514                 }
32515             ];
32516
32517             if(this.labelAlign == 'left'){
32518                 content = [
32519                     {
32520                         tag : 'div',
32521                         cls : 'column',
32522                         html : this.fieldLabel
32523                     },
32524                     {
32525                         tag : 'div',
32526                         cls : 'column',
32527                         cn : managerWidget
32528                     }
32529                 ];
32530                 
32531                 if(this.labelWidth > 12){
32532                     content[0].style = "width: " + this.labelWidth + 'px';
32533                 }
32534
32535                 if(this.labelWidth < 13 && this.labelmd == 0){
32536                     this.labelmd = this.labelWidth;
32537                 }
32538
32539                 if(this.labellg > 0){
32540                     content[0].cls += ' col-lg-' + this.labellg;
32541                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32542                 }
32543
32544                 if(this.labelmd > 0){
32545                     content[0].cls += ' col-md-' + this.labelmd;
32546                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32547                 }
32548
32549                 if(this.labelsm > 0){
32550                     content[0].cls += ' col-sm-' + this.labelsm;
32551                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32552                 }
32553
32554                 if(this.labelxs > 0){
32555                     content[0].cls += ' col-xs-' + this.labelxs;
32556                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32557                 }
32558                 
32559             }
32560         }
32561         
32562         var cfg = {
32563             tag : 'div',
32564             cls : 'row clearfix',
32565             cn : content
32566         };
32567         
32568         return cfg;
32569         
32570     },
32571     
32572     initEvents : function()
32573     {
32574         this.managerEl = this.el.select('.roo-document-manager', true).first();
32575         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32576         
32577         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32578         this.selectorEl.hide();
32579         
32580         if(this.multiple){
32581             this.selectorEl.attr('multiple', 'multiple');
32582         }
32583         
32584         this.selectorEl.on('change', this.onFileSelected, this);
32585         
32586         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32587         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32588         
32589         this.uploader.on('click', this.onUploaderClick, this);
32590         
32591         this.renderProgressDialog();
32592         
32593         var _this = this;
32594         
32595         window.addEventListener("resize", function() { _this.refresh(); } );
32596         
32597         this.fireEvent('initial', this);
32598     },
32599     
32600     renderProgressDialog : function()
32601     {
32602         var _this = this;
32603         
32604         this.progressDialog = new Roo.bootstrap.Modal({
32605             cls : 'roo-document-manager-progress-dialog',
32606             allow_close : false,
32607             animate : false,
32608             title : '',
32609             buttons : [
32610                 {
32611                     name  :'cancel',
32612                     weight : 'danger',
32613                     html : 'Cancel'
32614                 }
32615             ], 
32616             listeners : { 
32617                 btnclick : function() {
32618                     _this.uploadCancel();
32619                     this.hide();
32620                 }
32621             }
32622         });
32623          
32624         this.progressDialog.render(Roo.get(document.body));
32625          
32626         this.progress = new Roo.bootstrap.Progress({
32627             cls : 'roo-document-manager-progress',
32628             active : true,
32629             striped : true
32630         });
32631         
32632         this.progress.render(this.progressDialog.getChildContainer());
32633         
32634         this.progressBar = new Roo.bootstrap.ProgressBar({
32635             cls : 'roo-document-manager-progress-bar',
32636             aria_valuenow : 0,
32637             aria_valuemin : 0,
32638             aria_valuemax : 12,
32639             panel : 'success'
32640         });
32641         
32642         this.progressBar.render(this.progress.getChildContainer());
32643     },
32644     
32645     onUploaderClick : function(e)
32646     {
32647         e.preventDefault();
32648      
32649         if(this.fireEvent('beforeselectfile', this) != false){
32650             this.selectorEl.dom.click();
32651         }
32652         
32653     },
32654     
32655     onFileSelected : function(e)
32656     {
32657         e.preventDefault();
32658         
32659         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32660             return;
32661         }
32662         
32663         Roo.each(this.selectorEl.dom.files, function(file){
32664             if(this.fireEvent('inspect', this, file) != false){
32665                 this.files.push(file);
32666             }
32667         }, this);
32668         
32669         this.queue();
32670         
32671     },
32672     
32673     queue : function()
32674     {
32675         this.selectorEl.dom.value = '';
32676         
32677         if(!this.files || !this.files.length){
32678             return;
32679         }
32680         
32681         if(this.boxes > 0 && this.files.length > this.boxes){
32682             this.files = this.files.slice(0, this.boxes);
32683         }
32684         
32685         this.uploader.show();
32686         
32687         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32688             this.uploader.hide();
32689         }
32690         
32691         var _this = this;
32692         
32693         var files = [];
32694         
32695         var docs = [];
32696         
32697         Roo.each(this.files, function(file){
32698             
32699             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32700                 var f = this.renderPreview(file);
32701                 files.push(f);
32702                 return;
32703             }
32704             
32705             if(file.type.indexOf('image') != -1){
32706                 this.delegates.push(
32707                     (function(){
32708                         _this.process(file);
32709                     }).createDelegate(this)
32710                 );
32711         
32712                 return;
32713             }
32714             
32715             docs.push(
32716                 (function(){
32717                     _this.process(file);
32718                 }).createDelegate(this)
32719             );
32720             
32721         }, this);
32722         
32723         this.files = files;
32724         
32725         this.delegates = this.delegates.concat(docs);
32726         
32727         if(!this.delegates.length){
32728             this.refresh();
32729             return;
32730         }
32731         
32732         this.progressBar.aria_valuemax = this.delegates.length;
32733         
32734         this.arrange();
32735         
32736         return;
32737     },
32738     
32739     arrange : function()
32740     {
32741         if(!this.delegates.length){
32742             this.progressDialog.hide();
32743             this.refresh();
32744             return;
32745         }
32746         
32747         var delegate = this.delegates.shift();
32748         
32749         this.progressDialog.show();
32750         
32751         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32752         
32753         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32754         
32755         delegate();
32756     },
32757     
32758     refresh : function()
32759     {
32760         this.uploader.show();
32761         
32762         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32763             this.uploader.hide();
32764         }
32765         
32766         Roo.isTouch ? this.closable(false) : this.closable(true);
32767         
32768         this.fireEvent('refresh', this);
32769     },
32770     
32771     onRemove : function(e, el, o)
32772     {
32773         e.preventDefault();
32774         
32775         this.fireEvent('remove', this, o);
32776         
32777     },
32778     
32779     remove : function(o)
32780     {
32781         var files = [];
32782         
32783         Roo.each(this.files, function(file){
32784             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32785                 files.push(file);
32786                 return;
32787             }
32788
32789             o.target.remove();
32790
32791         }, this);
32792         
32793         this.files = files;
32794         
32795         this.refresh();
32796     },
32797     
32798     clear : function()
32799     {
32800         Roo.each(this.files, function(file){
32801             if(!file.target){
32802                 return;
32803             }
32804             
32805             file.target.remove();
32806
32807         }, this);
32808         
32809         this.files = [];
32810         
32811         this.refresh();
32812     },
32813     
32814     onClick : function(e, el, o)
32815     {
32816         e.preventDefault();
32817         
32818         this.fireEvent('click', this, o);
32819         
32820     },
32821     
32822     closable : function(closable)
32823     {
32824         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32825             
32826             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32827             
32828             if(closable){
32829                 el.show();
32830                 return;
32831             }
32832             
32833             el.hide();
32834             
32835         }, this);
32836     },
32837     
32838     xhrOnLoad : function(xhr)
32839     {
32840         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32841             el.remove();
32842         }, this);
32843         
32844         if (xhr.readyState !== 4) {
32845             this.arrange();
32846             this.fireEvent('exception', this, xhr);
32847             return;
32848         }
32849
32850         var response = Roo.decode(xhr.responseText);
32851         
32852         if(!response.success){
32853             this.arrange();
32854             this.fireEvent('exception', this, xhr);
32855             return;
32856         }
32857         
32858         var file = this.renderPreview(response.data);
32859         
32860         this.files.push(file);
32861         
32862         this.arrange();
32863         
32864         this.fireEvent('afterupload', this, xhr);
32865         
32866     },
32867     
32868     xhrOnError : function(xhr)
32869     {
32870         Roo.log('xhr on error');
32871         
32872         var response = Roo.decode(xhr.responseText);
32873           
32874         Roo.log(response);
32875         
32876         this.arrange();
32877     },
32878     
32879     process : function(file)
32880     {
32881         if(this.fireEvent('process', this, file) !== false){
32882             if(this.editable && file.type.indexOf('image') != -1){
32883                 this.fireEvent('edit', this, file);
32884                 return;
32885             }
32886
32887             this.uploadStart(file, false);
32888
32889             return;
32890         }
32891         
32892     },
32893     
32894     uploadStart : function(file, crop)
32895     {
32896         this.xhr = new XMLHttpRequest();
32897         
32898         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32899             this.arrange();
32900             return;
32901         }
32902         
32903         file.xhr = this.xhr;
32904             
32905         this.managerEl.createChild({
32906             tag : 'div',
32907             cls : 'roo-document-manager-loading',
32908             cn : [
32909                 {
32910                     tag : 'div',
32911                     tooltip : file.name,
32912                     cls : 'roo-document-manager-thumb',
32913                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32914                 }
32915             ]
32916
32917         });
32918
32919         this.xhr.open(this.method, this.url, true);
32920         
32921         var headers = {
32922             "Accept": "application/json",
32923             "Cache-Control": "no-cache",
32924             "X-Requested-With": "XMLHttpRequest"
32925         };
32926         
32927         for (var headerName in headers) {
32928             var headerValue = headers[headerName];
32929             if (headerValue) {
32930                 this.xhr.setRequestHeader(headerName, headerValue);
32931             }
32932         }
32933         
32934         var _this = this;
32935         
32936         this.xhr.onload = function()
32937         {
32938             _this.xhrOnLoad(_this.xhr);
32939         }
32940         
32941         this.xhr.onerror = function()
32942         {
32943             _this.xhrOnError(_this.xhr);
32944         }
32945         
32946         var formData = new FormData();
32947
32948         formData.append('returnHTML', 'NO');
32949         
32950         if(crop){
32951             formData.append('crop', crop);
32952         }
32953         
32954         formData.append(this.paramName, file, file.name);
32955         
32956         var options = {
32957             file : file, 
32958             manually : false
32959         };
32960         
32961         if(this.fireEvent('prepare', this, formData, options) != false){
32962             
32963             if(options.manually){
32964                 return;
32965             }
32966             
32967             this.xhr.send(formData);
32968             return;
32969         };
32970         
32971         this.uploadCancel();
32972     },
32973     
32974     uploadCancel : function()
32975     {
32976         if (this.xhr) {
32977             this.xhr.abort();
32978         }
32979         
32980         this.delegates = [];
32981         
32982         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32983             el.remove();
32984         }, this);
32985         
32986         this.arrange();
32987     },
32988     
32989     renderPreview : function(file)
32990     {
32991         if(typeof(file.target) != 'undefined' && file.target){
32992             return file;
32993         }
32994         
32995         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32996         
32997         var previewEl = this.managerEl.createChild({
32998             tag : 'div',
32999             cls : 'roo-document-manager-preview',
33000             cn : [
33001                 {
33002                     tag : 'div',
33003                     tooltip : file[this.toolTipName],
33004                     cls : 'roo-document-manager-thumb',
33005                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33006                 },
33007                 {
33008                     tag : 'button',
33009                     cls : 'close',
33010                     html : '<i class="fa fa-times-circle"></i>'
33011                 }
33012             ]
33013         });
33014
33015         var close = previewEl.select('button.close', true).first();
33016
33017         close.on('click', this.onRemove, this, file);
33018
33019         file.target = previewEl;
33020
33021         var image = previewEl.select('img', true).first();
33022         
33023         var _this = this;
33024         
33025         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33026         
33027         image.on('click', this.onClick, this, file);
33028         
33029         this.fireEvent('previewrendered', this, file);
33030         
33031         return file;
33032         
33033     },
33034     
33035     onPreviewLoad : function(file, image)
33036     {
33037         if(typeof(file.target) == 'undefined' || !file.target){
33038             return;
33039         }
33040         
33041         var width = image.dom.naturalWidth || image.dom.width;
33042         var height = image.dom.naturalHeight || image.dom.height;
33043         
33044         if(!this.previewResize) {
33045             return;
33046         }
33047         
33048         if(width > height){
33049             file.target.addClass('wide');
33050             return;
33051         }
33052         
33053         file.target.addClass('tall');
33054         return;
33055         
33056     },
33057     
33058     uploadFromSource : function(file, crop)
33059     {
33060         this.xhr = new XMLHttpRequest();
33061         
33062         this.managerEl.createChild({
33063             tag : 'div',
33064             cls : 'roo-document-manager-loading',
33065             cn : [
33066                 {
33067                     tag : 'div',
33068                     tooltip : file.name,
33069                     cls : 'roo-document-manager-thumb',
33070                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33071                 }
33072             ]
33073
33074         });
33075
33076         this.xhr.open(this.method, this.url, true);
33077         
33078         var headers = {
33079             "Accept": "application/json",
33080             "Cache-Control": "no-cache",
33081             "X-Requested-With": "XMLHttpRequest"
33082         };
33083         
33084         for (var headerName in headers) {
33085             var headerValue = headers[headerName];
33086             if (headerValue) {
33087                 this.xhr.setRequestHeader(headerName, headerValue);
33088             }
33089         }
33090         
33091         var _this = this;
33092         
33093         this.xhr.onload = function()
33094         {
33095             _this.xhrOnLoad(_this.xhr);
33096         }
33097         
33098         this.xhr.onerror = function()
33099         {
33100             _this.xhrOnError(_this.xhr);
33101         }
33102         
33103         var formData = new FormData();
33104
33105         formData.append('returnHTML', 'NO');
33106         
33107         formData.append('crop', crop);
33108         
33109         if(typeof(file.filename) != 'undefined'){
33110             formData.append('filename', file.filename);
33111         }
33112         
33113         if(typeof(file.mimetype) != 'undefined'){
33114             formData.append('mimetype', file.mimetype);
33115         }
33116         
33117         Roo.log(formData);
33118         
33119         if(this.fireEvent('prepare', this, formData) != false){
33120             this.xhr.send(formData);
33121         };
33122     }
33123 });
33124
33125 /*
33126 * Licence: LGPL
33127 */
33128
33129 /**
33130  * @class Roo.bootstrap.DocumentViewer
33131  * @extends Roo.bootstrap.Component
33132  * Bootstrap DocumentViewer class
33133  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33134  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33135  * 
33136  * @constructor
33137  * Create a new DocumentViewer
33138  * @param {Object} config The config object
33139  */
33140
33141 Roo.bootstrap.DocumentViewer = function(config){
33142     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33143     
33144     this.addEvents({
33145         /**
33146          * @event initial
33147          * Fire after initEvent
33148          * @param {Roo.bootstrap.DocumentViewer} this
33149          */
33150         "initial" : true,
33151         /**
33152          * @event click
33153          * Fire after click
33154          * @param {Roo.bootstrap.DocumentViewer} this
33155          */
33156         "click" : true,
33157         /**
33158          * @event download
33159          * Fire after download button
33160          * @param {Roo.bootstrap.DocumentViewer} this
33161          */
33162         "download" : true,
33163         /**
33164          * @event trash
33165          * Fire after trash button
33166          * @param {Roo.bootstrap.DocumentViewer} this
33167          */
33168         "trash" : true
33169         
33170     });
33171 };
33172
33173 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33174     
33175     showDownload : true,
33176     
33177     showTrash : true,
33178     
33179     getAutoCreate : function()
33180     {
33181         var cfg = {
33182             tag : 'div',
33183             cls : 'roo-document-viewer',
33184             cn : [
33185                 {
33186                     tag : 'div',
33187                     cls : 'roo-document-viewer-body',
33188                     cn : [
33189                         {
33190                             tag : 'div',
33191                             cls : 'roo-document-viewer-thumb',
33192                             cn : [
33193                                 {
33194                                     tag : 'img',
33195                                     cls : 'roo-document-viewer-image'
33196                                 }
33197                             ]
33198                         }
33199                     ]
33200                 },
33201                 {
33202                     tag : 'div',
33203                     cls : 'roo-document-viewer-footer',
33204                     cn : {
33205                         tag : 'div',
33206                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33207                         cn : [
33208                             {
33209                                 tag : 'div',
33210                                 cls : 'btn-group roo-document-viewer-download',
33211                                 cn : [
33212                                     {
33213                                         tag : 'button',
33214                                         cls : 'btn btn-default',
33215                                         html : '<i class="fa fa-download"></i>'
33216                                     }
33217                                 ]
33218                             },
33219                             {
33220                                 tag : 'div',
33221                                 cls : 'btn-group roo-document-viewer-trash',
33222                                 cn : [
33223                                     {
33224                                         tag : 'button',
33225                                         cls : 'btn btn-default',
33226                                         html : '<i class="fa fa-trash"></i>'
33227                                     }
33228                                 ]
33229                             }
33230                         ]
33231                     }
33232                 }
33233             ]
33234         };
33235         
33236         return cfg;
33237     },
33238     
33239     initEvents : function()
33240     {
33241         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33242         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33243         
33244         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33245         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33246         
33247         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33248         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33249         
33250         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33251         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33252         
33253         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33254         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33255         
33256         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33257         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33258         
33259         this.bodyEl.on('click', this.onClick, this);
33260         this.downloadBtn.on('click', this.onDownload, this);
33261         this.trashBtn.on('click', this.onTrash, this);
33262         
33263         this.downloadBtn.hide();
33264         this.trashBtn.hide();
33265         
33266         if(this.showDownload){
33267             this.downloadBtn.show();
33268         }
33269         
33270         if(this.showTrash){
33271             this.trashBtn.show();
33272         }
33273         
33274         if(!this.showDownload && !this.showTrash) {
33275             this.footerEl.hide();
33276         }
33277         
33278     },
33279     
33280     initial : function()
33281     {
33282         this.fireEvent('initial', this);
33283         
33284     },
33285     
33286     onClick : function(e)
33287     {
33288         e.preventDefault();
33289         
33290         this.fireEvent('click', this);
33291     },
33292     
33293     onDownload : function(e)
33294     {
33295         e.preventDefault();
33296         
33297         this.fireEvent('download', this);
33298     },
33299     
33300     onTrash : function(e)
33301     {
33302         e.preventDefault();
33303         
33304         this.fireEvent('trash', this);
33305     }
33306     
33307 });
33308 /*
33309  * - LGPL
33310  *
33311  * nav progress bar
33312  * 
33313  */
33314
33315 /**
33316  * @class Roo.bootstrap.NavProgressBar
33317  * @extends Roo.bootstrap.Component
33318  * Bootstrap NavProgressBar class
33319  * 
33320  * @constructor
33321  * Create a new nav progress bar
33322  * @param {Object} config The config object
33323  */
33324
33325 Roo.bootstrap.NavProgressBar = function(config){
33326     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33327
33328     this.bullets = this.bullets || [];
33329    
33330 //    Roo.bootstrap.NavProgressBar.register(this);
33331      this.addEvents({
33332         /**
33333              * @event changed
33334              * Fires when the active item changes
33335              * @param {Roo.bootstrap.NavProgressBar} this
33336              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33337              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33338          */
33339         'changed': true
33340      });
33341     
33342 };
33343
33344 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33345     
33346     bullets : [],
33347     barItems : [],
33348     
33349     getAutoCreate : function()
33350     {
33351         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33352         
33353         cfg = {
33354             tag : 'div',
33355             cls : 'roo-navigation-bar-group',
33356             cn : [
33357                 {
33358                     tag : 'div',
33359                     cls : 'roo-navigation-top-bar'
33360                 },
33361                 {
33362                     tag : 'div',
33363                     cls : 'roo-navigation-bullets-bar',
33364                     cn : [
33365                         {
33366                             tag : 'ul',
33367                             cls : 'roo-navigation-bar'
33368                         }
33369                     ]
33370                 },
33371                 
33372                 {
33373                     tag : 'div',
33374                     cls : 'roo-navigation-bottom-bar'
33375                 }
33376             ]
33377             
33378         };
33379         
33380         return cfg;
33381         
33382     },
33383     
33384     initEvents: function() 
33385     {
33386         
33387     },
33388     
33389     onRender : function(ct, position) 
33390     {
33391         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33392         
33393         if(this.bullets.length){
33394             Roo.each(this.bullets, function(b){
33395                this.addItem(b);
33396             }, this);
33397         }
33398         
33399         this.format();
33400         
33401     },
33402     
33403     addItem : function(cfg)
33404     {
33405         var item = new Roo.bootstrap.NavProgressItem(cfg);
33406         
33407         item.parentId = this.id;
33408         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33409         
33410         if(cfg.html){
33411             var top = new Roo.bootstrap.Element({
33412                 tag : 'div',
33413                 cls : 'roo-navigation-bar-text'
33414             });
33415             
33416             var bottom = new Roo.bootstrap.Element({
33417                 tag : 'div',
33418                 cls : 'roo-navigation-bar-text'
33419             });
33420             
33421             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33422             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33423             
33424             var topText = new Roo.bootstrap.Element({
33425                 tag : 'span',
33426                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33427             });
33428             
33429             var bottomText = new Roo.bootstrap.Element({
33430                 tag : 'span',
33431                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33432             });
33433             
33434             topText.onRender(top.el, null);
33435             bottomText.onRender(bottom.el, null);
33436             
33437             item.topEl = top;
33438             item.bottomEl = bottom;
33439         }
33440         
33441         this.barItems.push(item);
33442         
33443         return item;
33444     },
33445     
33446     getActive : function()
33447     {
33448         var active = false;
33449         
33450         Roo.each(this.barItems, function(v){
33451             
33452             if (!v.isActive()) {
33453                 return;
33454             }
33455             
33456             active = v;
33457             return false;
33458             
33459         });
33460         
33461         return active;
33462     },
33463     
33464     setActiveItem : function(item)
33465     {
33466         var prev = false;
33467         
33468         Roo.each(this.barItems, function(v){
33469             if (v.rid == item.rid) {
33470                 return ;
33471             }
33472             
33473             if (v.isActive()) {
33474                 v.setActive(false);
33475                 prev = v;
33476             }
33477         });
33478
33479         item.setActive(true);
33480         
33481         this.fireEvent('changed', this, item, prev);
33482     },
33483     
33484     getBarItem: function(rid)
33485     {
33486         var ret = false;
33487         
33488         Roo.each(this.barItems, function(e) {
33489             if (e.rid != rid) {
33490                 return;
33491             }
33492             
33493             ret =  e;
33494             return false;
33495         });
33496         
33497         return ret;
33498     },
33499     
33500     indexOfItem : function(item)
33501     {
33502         var index = false;
33503         
33504         Roo.each(this.barItems, function(v, i){
33505             
33506             if (v.rid != item.rid) {
33507                 return;
33508             }
33509             
33510             index = i;
33511             return false
33512         });
33513         
33514         return index;
33515     },
33516     
33517     setActiveNext : function()
33518     {
33519         var i = this.indexOfItem(this.getActive());
33520         
33521         if (i > this.barItems.length) {
33522             return;
33523         }
33524         
33525         this.setActiveItem(this.barItems[i+1]);
33526     },
33527     
33528     setActivePrev : function()
33529     {
33530         var i = this.indexOfItem(this.getActive());
33531         
33532         if (i  < 1) {
33533             return;
33534         }
33535         
33536         this.setActiveItem(this.barItems[i-1]);
33537     },
33538     
33539     format : function()
33540     {
33541         if(!this.barItems.length){
33542             return;
33543         }
33544      
33545         var width = 100 / this.barItems.length;
33546         
33547         Roo.each(this.barItems, function(i){
33548             i.el.setStyle('width', width + '%');
33549             i.topEl.el.setStyle('width', width + '%');
33550             i.bottomEl.el.setStyle('width', width + '%');
33551         }, this);
33552         
33553     }
33554     
33555 });
33556 /*
33557  * - LGPL
33558  *
33559  * Nav Progress Item
33560  * 
33561  */
33562
33563 /**
33564  * @class Roo.bootstrap.NavProgressItem
33565  * @extends Roo.bootstrap.Component
33566  * Bootstrap NavProgressItem class
33567  * @cfg {String} rid the reference id
33568  * @cfg {Boolean} active (true|false) Is item active default false
33569  * @cfg {Boolean} disabled (true|false) Is item active default false
33570  * @cfg {String} html
33571  * @cfg {String} position (top|bottom) text position default bottom
33572  * @cfg {String} icon show icon instead of number
33573  * 
33574  * @constructor
33575  * Create a new NavProgressItem
33576  * @param {Object} config The config object
33577  */
33578 Roo.bootstrap.NavProgressItem = function(config){
33579     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33580     this.addEvents({
33581         // raw events
33582         /**
33583          * @event click
33584          * The raw click event for the entire grid.
33585          * @param {Roo.bootstrap.NavProgressItem} this
33586          * @param {Roo.EventObject} e
33587          */
33588         "click" : true
33589     });
33590    
33591 };
33592
33593 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33594     
33595     rid : '',
33596     active : false,
33597     disabled : false,
33598     html : '',
33599     position : 'bottom',
33600     icon : false,
33601     
33602     getAutoCreate : function()
33603     {
33604         var iconCls = 'roo-navigation-bar-item-icon';
33605         
33606         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33607         
33608         var cfg = {
33609             tag: 'li',
33610             cls: 'roo-navigation-bar-item',
33611             cn : [
33612                 {
33613                     tag : 'i',
33614                     cls : iconCls
33615                 }
33616             ]
33617         };
33618         
33619         if(this.active){
33620             cfg.cls += ' active';
33621         }
33622         if(this.disabled){
33623             cfg.cls += ' disabled';
33624         }
33625         
33626         return cfg;
33627     },
33628     
33629     disable : function()
33630     {
33631         this.setDisabled(true);
33632     },
33633     
33634     enable : function()
33635     {
33636         this.setDisabled(false);
33637     },
33638     
33639     initEvents: function() 
33640     {
33641         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33642         
33643         this.iconEl.on('click', this.onClick, this);
33644     },
33645     
33646     onClick : function(e)
33647     {
33648         e.preventDefault();
33649         
33650         if(this.disabled){
33651             return;
33652         }
33653         
33654         if(this.fireEvent('click', this, e) === false){
33655             return;
33656         };
33657         
33658         this.parent().setActiveItem(this);
33659     },
33660     
33661     isActive: function () 
33662     {
33663         return this.active;
33664     },
33665     
33666     setActive : function(state)
33667     {
33668         if(this.active == state){
33669             return;
33670         }
33671         
33672         this.active = state;
33673         
33674         if (state) {
33675             this.el.addClass('active');
33676             return;
33677         }
33678         
33679         this.el.removeClass('active');
33680         
33681         return;
33682     },
33683     
33684     setDisabled : function(state)
33685     {
33686         if(this.disabled == state){
33687             return;
33688         }
33689         
33690         this.disabled = state;
33691         
33692         if (state) {
33693             this.el.addClass('disabled');
33694             return;
33695         }
33696         
33697         this.el.removeClass('disabled');
33698     },
33699     
33700     tooltipEl : function()
33701     {
33702         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33703     }
33704 });
33705  
33706
33707  /*
33708  * - LGPL
33709  *
33710  * FieldLabel
33711  * 
33712  */
33713
33714 /**
33715  * @class Roo.bootstrap.FieldLabel
33716  * @extends Roo.bootstrap.Component
33717  * Bootstrap FieldLabel class
33718  * @cfg {String} html contents of the element
33719  * @cfg {String} tag tag of the element default label
33720  * @cfg {String} cls class of the element
33721  * @cfg {String} target label target 
33722  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33723  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33724  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33725  * @cfg {String} iconTooltip default "This field is required"
33726  * @cfg {String} indicatorpos (left|right) default left
33727  * 
33728  * @constructor
33729  * Create a new FieldLabel
33730  * @param {Object} config The config object
33731  */
33732
33733 Roo.bootstrap.FieldLabel = function(config){
33734     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33735     
33736     this.addEvents({
33737             /**
33738              * @event invalid
33739              * Fires after the field has been marked as invalid.
33740              * @param {Roo.form.FieldLabel} this
33741              * @param {String} msg The validation message
33742              */
33743             invalid : true,
33744             /**
33745              * @event valid
33746              * Fires after the field has been validated with no errors.
33747              * @param {Roo.form.FieldLabel} this
33748              */
33749             valid : true
33750         });
33751 };
33752
33753 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33754     
33755     tag: 'label',
33756     cls: '',
33757     html: '',
33758     target: '',
33759     allowBlank : true,
33760     invalidClass : 'has-warning',
33761     validClass : 'has-success',
33762     iconTooltip : 'This field is required',
33763     indicatorpos : 'left',
33764     
33765     getAutoCreate : function(){
33766         
33767         var cls = "";
33768         if (!this.allowBlank) {
33769             cls  = "visible";
33770         }
33771         
33772         var cfg = {
33773             tag : this.tag,
33774             cls : 'roo-bootstrap-field-label ' + this.cls,
33775             for : this.target,
33776             cn : [
33777                 {
33778                     tag : 'i',
33779                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33780                     tooltip : this.iconTooltip
33781                 },
33782                 {
33783                     tag : 'span',
33784                     html : this.html
33785                 }
33786             ] 
33787         };
33788         
33789         if(this.indicatorpos == 'right'){
33790             var cfg = {
33791                 tag : this.tag,
33792                 cls : 'roo-bootstrap-field-label ' + this.cls,
33793                 for : this.target,
33794                 cn : [
33795                     {
33796                         tag : 'span',
33797                         html : this.html
33798                     },
33799                     {
33800                         tag : 'i',
33801                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33802                         tooltip : this.iconTooltip
33803                     }
33804                 ] 
33805             };
33806         }
33807         
33808         return cfg;
33809     },
33810     
33811     initEvents: function() 
33812     {
33813         Roo.bootstrap.Element.superclass.initEvents.call(this);
33814         
33815         this.indicator = this.indicatorEl();
33816         
33817         if(this.indicator){
33818             this.indicator.removeClass('visible');
33819             this.indicator.addClass('invisible');
33820         }
33821         
33822         Roo.bootstrap.FieldLabel.register(this);
33823     },
33824     
33825     indicatorEl : function()
33826     {
33827         var indicator = this.el.select('i.roo-required-indicator',true).first();
33828         
33829         if(!indicator){
33830             return false;
33831         }
33832         
33833         return indicator;
33834         
33835     },
33836     
33837     /**
33838      * Mark this field as valid
33839      */
33840     markValid : function()
33841     {
33842         if(this.indicator){
33843             this.indicator.removeClass('visible');
33844             this.indicator.addClass('invisible');
33845         }
33846         if (Roo.bootstrap.version == 3) {
33847             this.el.removeClass(this.invalidClass);
33848             this.el.addClass(this.validClass);
33849         } else {
33850             this.el.removeClass('is-invalid');
33851             this.el.addClass('is-valid');
33852         }
33853         
33854         
33855         this.fireEvent('valid', this);
33856     },
33857     
33858     /**
33859      * Mark this field as invalid
33860      * @param {String} msg The validation message
33861      */
33862     markInvalid : function(msg)
33863     {
33864         if(this.indicator){
33865             this.indicator.removeClass('invisible');
33866             this.indicator.addClass('visible');
33867         }
33868           if (Roo.bootstrap.version == 3) {
33869             this.el.removeClass(this.validClass);
33870             this.el.addClass(this.invalidClass);
33871         } else {
33872             this.el.removeClass('is-valid');
33873             this.el.addClass('is-invalid');
33874         }
33875         
33876         
33877         this.fireEvent('invalid', this, msg);
33878     }
33879     
33880    
33881 });
33882
33883 Roo.apply(Roo.bootstrap.FieldLabel, {
33884     
33885     groups: {},
33886     
33887      /**
33888     * register a FieldLabel Group
33889     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33890     */
33891     register : function(label)
33892     {
33893         if(this.groups.hasOwnProperty(label.target)){
33894             return;
33895         }
33896      
33897         this.groups[label.target] = label;
33898         
33899     },
33900     /**
33901     * fetch a FieldLabel Group based on the target
33902     * @param {string} target
33903     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33904     */
33905     get: function(target) {
33906         if (typeof(this.groups[target]) == 'undefined') {
33907             return false;
33908         }
33909         
33910         return this.groups[target] ;
33911     }
33912 });
33913
33914  
33915
33916  /*
33917  * - LGPL
33918  *
33919  * page DateSplitField.
33920  * 
33921  */
33922
33923
33924 /**
33925  * @class Roo.bootstrap.DateSplitField
33926  * @extends Roo.bootstrap.Component
33927  * Bootstrap DateSplitField class
33928  * @cfg {string} fieldLabel - the label associated
33929  * @cfg {Number} labelWidth set the width of label (0-12)
33930  * @cfg {String} labelAlign (top|left)
33931  * @cfg {Boolean} dayAllowBlank (true|false) default false
33932  * @cfg {Boolean} monthAllowBlank (true|false) default false
33933  * @cfg {Boolean} yearAllowBlank (true|false) default false
33934  * @cfg {string} dayPlaceholder 
33935  * @cfg {string} monthPlaceholder
33936  * @cfg {string} yearPlaceholder
33937  * @cfg {string} dayFormat default 'd'
33938  * @cfg {string} monthFormat default 'm'
33939  * @cfg {string} yearFormat default 'Y'
33940  * @cfg {Number} labellg set the width of label (1-12)
33941  * @cfg {Number} labelmd set the width of label (1-12)
33942  * @cfg {Number} labelsm set the width of label (1-12)
33943  * @cfg {Number} labelxs set the width of label (1-12)
33944
33945  *     
33946  * @constructor
33947  * Create a new DateSplitField
33948  * @param {Object} config The config object
33949  */
33950
33951 Roo.bootstrap.DateSplitField = function(config){
33952     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33953     
33954     this.addEvents({
33955         // raw events
33956          /**
33957          * @event years
33958          * getting the data of years
33959          * @param {Roo.bootstrap.DateSplitField} this
33960          * @param {Object} years
33961          */
33962         "years" : true,
33963         /**
33964          * @event days
33965          * getting the data of days
33966          * @param {Roo.bootstrap.DateSplitField} this
33967          * @param {Object} days
33968          */
33969         "days" : true,
33970         /**
33971          * @event invalid
33972          * Fires after the field has been marked as invalid.
33973          * @param {Roo.form.Field} this
33974          * @param {String} msg The validation message
33975          */
33976         invalid : true,
33977        /**
33978          * @event valid
33979          * Fires after the field has been validated with no errors.
33980          * @param {Roo.form.Field} this
33981          */
33982         valid : true
33983     });
33984 };
33985
33986 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33987     
33988     fieldLabel : '',
33989     labelAlign : 'top',
33990     labelWidth : 3,
33991     dayAllowBlank : false,
33992     monthAllowBlank : false,
33993     yearAllowBlank : false,
33994     dayPlaceholder : '',
33995     monthPlaceholder : '',
33996     yearPlaceholder : '',
33997     dayFormat : 'd',
33998     monthFormat : 'm',
33999     yearFormat : 'Y',
34000     isFormField : true,
34001     labellg : 0,
34002     labelmd : 0,
34003     labelsm : 0,
34004     labelxs : 0,
34005     
34006     getAutoCreate : function()
34007     {
34008         var cfg = {
34009             tag : 'div',
34010             cls : 'row roo-date-split-field-group',
34011             cn : [
34012                 {
34013                     tag : 'input',
34014                     type : 'hidden',
34015                     cls : 'form-hidden-field roo-date-split-field-group-value',
34016                     name : this.name
34017                 }
34018             ]
34019         };
34020         
34021         var labelCls = 'col-md-12';
34022         var contentCls = 'col-md-4';
34023         
34024         if(this.fieldLabel){
34025             
34026             var label = {
34027                 tag : 'div',
34028                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34029                 cn : [
34030                     {
34031                         tag : 'label',
34032                         html : this.fieldLabel
34033                     }
34034                 ]
34035             };
34036             
34037             if(this.labelAlign == 'left'){
34038             
34039                 if(this.labelWidth > 12){
34040                     label.style = "width: " + this.labelWidth + 'px';
34041                 }
34042
34043                 if(this.labelWidth < 13 && this.labelmd == 0){
34044                     this.labelmd = this.labelWidth;
34045                 }
34046
34047                 if(this.labellg > 0){
34048                     labelCls = ' col-lg-' + this.labellg;
34049                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34050                 }
34051
34052                 if(this.labelmd > 0){
34053                     labelCls = ' col-md-' + this.labelmd;
34054                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34055                 }
34056
34057                 if(this.labelsm > 0){
34058                     labelCls = ' col-sm-' + this.labelsm;
34059                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34060                 }
34061
34062                 if(this.labelxs > 0){
34063                     labelCls = ' col-xs-' + this.labelxs;
34064                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34065                 }
34066             }
34067             
34068             label.cls += ' ' + labelCls;
34069             
34070             cfg.cn.push(label);
34071         }
34072         
34073         Roo.each(['day', 'month', 'year'], function(t){
34074             cfg.cn.push({
34075                 tag : 'div',
34076                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34077             });
34078         }, this);
34079         
34080         return cfg;
34081     },
34082     
34083     inputEl: function ()
34084     {
34085         return this.el.select('.roo-date-split-field-group-value', true).first();
34086     },
34087     
34088     onRender : function(ct, position) 
34089     {
34090         var _this = this;
34091         
34092         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34093         
34094         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34095         
34096         this.dayField = new Roo.bootstrap.ComboBox({
34097             allowBlank : this.dayAllowBlank,
34098             alwaysQuery : true,
34099             displayField : 'value',
34100             editable : false,
34101             fieldLabel : '',
34102             forceSelection : true,
34103             mode : 'local',
34104             placeholder : this.dayPlaceholder,
34105             selectOnFocus : true,
34106             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34107             triggerAction : 'all',
34108             typeAhead : true,
34109             valueField : 'value',
34110             store : new Roo.data.SimpleStore({
34111                 data : (function() {    
34112                     var days = [];
34113                     _this.fireEvent('days', _this, days);
34114                     return days;
34115                 })(),
34116                 fields : [ 'value' ]
34117             }),
34118             listeners : {
34119                 select : function (_self, record, index)
34120                 {
34121                     _this.setValue(_this.getValue());
34122                 }
34123             }
34124         });
34125
34126         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34127         
34128         this.monthField = new Roo.bootstrap.MonthField({
34129             after : '<i class=\"fa fa-calendar\"></i>',
34130             allowBlank : this.monthAllowBlank,
34131             placeholder : this.monthPlaceholder,
34132             readOnly : true,
34133             listeners : {
34134                 render : function (_self)
34135                 {
34136                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34137                         e.preventDefault();
34138                         _self.focus();
34139                     });
34140                 },
34141                 select : function (_self, oldvalue, newvalue)
34142                 {
34143                     _this.setValue(_this.getValue());
34144                 }
34145             }
34146         });
34147         
34148         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34149         
34150         this.yearField = new Roo.bootstrap.ComboBox({
34151             allowBlank : this.yearAllowBlank,
34152             alwaysQuery : true,
34153             displayField : 'value',
34154             editable : false,
34155             fieldLabel : '',
34156             forceSelection : true,
34157             mode : 'local',
34158             placeholder : this.yearPlaceholder,
34159             selectOnFocus : true,
34160             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34161             triggerAction : 'all',
34162             typeAhead : true,
34163             valueField : 'value',
34164             store : new Roo.data.SimpleStore({
34165                 data : (function() {
34166                     var years = [];
34167                     _this.fireEvent('years', _this, years);
34168                     return years;
34169                 })(),
34170                 fields : [ 'value' ]
34171             }),
34172             listeners : {
34173                 select : function (_self, record, index)
34174                 {
34175                     _this.setValue(_this.getValue());
34176                 }
34177             }
34178         });
34179
34180         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34181     },
34182     
34183     setValue : function(v, format)
34184     {
34185         this.inputEl.dom.value = v;
34186         
34187         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34188         
34189         var d = Date.parseDate(v, f);
34190         
34191         if(!d){
34192             this.validate();
34193             return;
34194         }
34195         
34196         this.setDay(d.format(this.dayFormat));
34197         this.setMonth(d.format(this.monthFormat));
34198         this.setYear(d.format(this.yearFormat));
34199         
34200         this.validate();
34201         
34202         return;
34203     },
34204     
34205     setDay : function(v)
34206     {
34207         this.dayField.setValue(v);
34208         this.inputEl.dom.value = this.getValue();
34209         this.validate();
34210         return;
34211     },
34212     
34213     setMonth : function(v)
34214     {
34215         this.monthField.setValue(v, true);
34216         this.inputEl.dom.value = this.getValue();
34217         this.validate();
34218         return;
34219     },
34220     
34221     setYear : function(v)
34222     {
34223         this.yearField.setValue(v);
34224         this.inputEl.dom.value = this.getValue();
34225         this.validate();
34226         return;
34227     },
34228     
34229     getDay : function()
34230     {
34231         return this.dayField.getValue();
34232     },
34233     
34234     getMonth : function()
34235     {
34236         return this.monthField.getValue();
34237     },
34238     
34239     getYear : function()
34240     {
34241         return this.yearField.getValue();
34242     },
34243     
34244     getValue : function()
34245     {
34246         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34247         
34248         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34249         
34250         return date;
34251     },
34252     
34253     reset : function()
34254     {
34255         this.setDay('');
34256         this.setMonth('');
34257         this.setYear('');
34258         this.inputEl.dom.value = '';
34259         this.validate();
34260         return;
34261     },
34262     
34263     validate : function()
34264     {
34265         var d = this.dayField.validate();
34266         var m = this.monthField.validate();
34267         var y = this.yearField.validate();
34268         
34269         var valid = true;
34270         
34271         if(
34272                 (!this.dayAllowBlank && !d) ||
34273                 (!this.monthAllowBlank && !m) ||
34274                 (!this.yearAllowBlank && !y)
34275         ){
34276             valid = false;
34277         }
34278         
34279         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34280             return valid;
34281         }
34282         
34283         if(valid){
34284             this.markValid();
34285             return valid;
34286         }
34287         
34288         this.markInvalid();
34289         
34290         return valid;
34291     },
34292     
34293     markValid : function()
34294     {
34295         
34296         var label = this.el.select('label', true).first();
34297         var icon = this.el.select('i.fa-star', true).first();
34298
34299         if(label && icon){
34300             icon.remove();
34301         }
34302         
34303         this.fireEvent('valid', this);
34304     },
34305     
34306      /**
34307      * Mark this field as invalid
34308      * @param {String} msg The validation message
34309      */
34310     markInvalid : function(msg)
34311     {
34312         
34313         var label = this.el.select('label', true).first();
34314         var icon = this.el.select('i.fa-star', true).first();
34315
34316         if(label && !icon){
34317             this.el.select('.roo-date-split-field-label', true).createChild({
34318                 tag : 'i',
34319                 cls : 'text-danger fa fa-lg fa-star',
34320                 tooltip : 'This field is required',
34321                 style : 'margin-right:5px;'
34322             }, label, true);
34323         }
34324         
34325         this.fireEvent('invalid', this, msg);
34326     },
34327     
34328     clearInvalid : function()
34329     {
34330         var label = this.el.select('label', true).first();
34331         var icon = this.el.select('i.fa-star', true).first();
34332
34333         if(label && icon){
34334             icon.remove();
34335         }
34336         
34337         this.fireEvent('valid', this);
34338     },
34339     
34340     getName: function()
34341     {
34342         return this.name;
34343     }
34344     
34345 });
34346
34347  /**
34348  *
34349  * This is based on 
34350  * http://masonry.desandro.com
34351  *
34352  * The idea is to render all the bricks based on vertical width...
34353  *
34354  * The original code extends 'outlayer' - we might need to use that....
34355  * 
34356  */
34357
34358
34359 /**
34360  * @class Roo.bootstrap.LayoutMasonry
34361  * @extends Roo.bootstrap.Component
34362  * Bootstrap Layout Masonry class
34363  * 
34364  * @constructor
34365  * Create a new Element
34366  * @param {Object} config The config object
34367  */
34368
34369 Roo.bootstrap.LayoutMasonry = function(config){
34370     
34371     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34372     
34373     this.bricks = [];
34374     
34375     Roo.bootstrap.LayoutMasonry.register(this);
34376     
34377     this.addEvents({
34378         // raw events
34379         /**
34380          * @event layout
34381          * Fire after layout the items
34382          * @param {Roo.bootstrap.LayoutMasonry} this
34383          * @param {Roo.EventObject} e
34384          */
34385         "layout" : true
34386     });
34387     
34388 };
34389
34390 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34391     
34392     /**
34393      * @cfg {Boolean} isLayoutInstant = no animation?
34394      */   
34395     isLayoutInstant : false, // needed?
34396    
34397     /**
34398      * @cfg {Number} boxWidth  width of the columns
34399      */   
34400     boxWidth : 450,
34401     
34402       /**
34403      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34404      */   
34405     boxHeight : 0,
34406     
34407     /**
34408      * @cfg {Number} padWidth padding below box..
34409      */   
34410     padWidth : 10, 
34411     
34412     /**
34413      * @cfg {Number} gutter gutter width..
34414      */   
34415     gutter : 10,
34416     
34417      /**
34418      * @cfg {Number} maxCols maximum number of columns
34419      */   
34420     
34421     maxCols: 0,
34422     
34423     /**
34424      * @cfg {Boolean} isAutoInitial defalut true
34425      */   
34426     isAutoInitial : true, 
34427     
34428     containerWidth: 0,
34429     
34430     /**
34431      * @cfg {Boolean} isHorizontal defalut false
34432      */   
34433     isHorizontal : false, 
34434
34435     currentSize : null,
34436     
34437     tag: 'div',
34438     
34439     cls: '',
34440     
34441     bricks: null, //CompositeElement
34442     
34443     cols : 1,
34444     
34445     _isLayoutInited : false,
34446     
34447 //    isAlternative : false, // only use for vertical layout...
34448     
34449     /**
34450      * @cfg {Number} alternativePadWidth padding below box..
34451      */   
34452     alternativePadWidth : 50,
34453     
34454     selectedBrick : [],
34455     
34456     getAutoCreate : function(){
34457         
34458         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34459         
34460         var cfg = {
34461             tag: this.tag,
34462             cls: 'blog-masonary-wrapper ' + this.cls,
34463             cn : {
34464                 cls : 'mas-boxes masonary'
34465             }
34466         };
34467         
34468         return cfg;
34469     },
34470     
34471     getChildContainer: function( )
34472     {
34473         if (this.boxesEl) {
34474             return this.boxesEl;
34475         }
34476         
34477         this.boxesEl = this.el.select('.mas-boxes').first();
34478         
34479         return this.boxesEl;
34480     },
34481     
34482     
34483     initEvents : function()
34484     {
34485         var _this = this;
34486         
34487         if(this.isAutoInitial){
34488             Roo.log('hook children rendered');
34489             this.on('childrenrendered', function() {
34490                 Roo.log('children rendered');
34491                 _this.initial();
34492             } ,this);
34493         }
34494     },
34495     
34496     initial : function()
34497     {
34498         this.selectedBrick = [];
34499         
34500         this.currentSize = this.el.getBox(true);
34501         
34502         Roo.EventManager.onWindowResize(this.resize, this); 
34503
34504         if(!this.isAutoInitial){
34505             this.layout();
34506             return;
34507         }
34508         
34509         this.layout();
34510         
34511         return;
34512         //this.layout.defer(500,this);
34513         
34514     },
34515     
34516     resize : function()
34517     {
34518         var cs = this.el.getBox(true);
34519         
34520         if (
34521                 this.currentSize.width == cs.width && 
34522                 this.currentSize.x == cs.x && 
34523                 this.currentSize.height == cs.height && 
34524                 this.currentSize.y == cs.y 
34525         ) {
34526             Roo.log("no change in with or X or Y");
34527             return;
34528         }
34529         
34530         this.currentSize = cs;
34531         
34532         this.layout();
34533         
34534     },
34535     
34536     layout : function()
34537     {   
34538         this._resetLayout();
34539         
34540         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34541         
34542         this.layoutItems( isInstant );
34543       
34544         this._isLayoutInited = true;
34545         
34546         this.fireEvent('layout', this);
34547         
34548     },
34549     
34550     _resetLayout : function()
34551     {
34552         if(this.isHorizontal){
34553             this.horizontalMeasureColumns();
34554             return;
34555         }
34556         
34557         this.verticalMeasureColumns();
34558         
34559     },
34560     
34561     verticalMeasureColumns : function()
34562     {
34563         this.getContainerWidth();
34564         
34565 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34566 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34567 //            return;
34568 //        }
34569         
34570         var boxWidth = this.boxWidth + this.padWidth;
34571         
34572         if(this.containerWidth < this.boxWidth){
34573             boxWidth = this.containerWidth
34574         }
34575         
34576         var containerWidth = this.containerWidth;
34577         
34578         var cols = Math.floor(containerWidth / boxWidth);
34579         
34580         this.cols = Math.max( cols, 1 );
34581         
34582         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34583         
34584         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34585         
34586         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34587         
34588         this.colWidth = boxWidth + avail - this.padWidth;
34589         
34590         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34591         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34592     },
34593     
34594     horizontalMeasureColumns : function()
34595     {
34596         this.getContainerWidth();
34597         
34598         var boxWidth = this.boxWidth;
34599         
34600         if(this.containerWidth < boxWidth){
34601             boxWidth = this.containerWidth;
34602         }
34603         
34604         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34605         
34606         this.el.setHeight(boxWidth);
34607         
34608     },
34609     
34610     getContainerWidth : function()
34611     {
34612         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34613     },
34614     
34615     layoutItems : function( isInstant )
34616     {
34617         Roo.log(this.bricks);
34618         
34619         var items = Roo.apply([], this.bricks);
34620         
34621         if(this.isHorizontal){
34622             this._horizontalLayoutItems( items , isInstant );
34623             return;
34624         }
34625         
34626 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34627 //            this._verticalAlternativeLayoutItems( items , isInstant );
34628 //            return;
34629 //        }
34630         
34631         this._verticalLayoutItems( items , isInstant );
34632         
34633     },
34634     
34635     _verticalLayoutItems : function ( items , isInstant)
34636     {
34637         if ( !items || !items.length ) {
34638             return;
34639         }
34640         
34641         var standard = [
34642             ['xs', 'xs', 'xs', 'tall'],
34643             ['xs', 'xs', 'tall'],
34644             ['xs', 'xs', 'sm'],
34645             ['xs', 'xs', 'xs'],
34646             ['xs', 'tall'],
34647             ['xs', 'sm'],
34648             ['xs', 'xs'],
34649             ['xs'],
34650             
34651             ['sm', 'xs', 'xs'],
34652             ['sm', 'xs'],
34653             ['sm'],
34654             
34655             ['tall', 'xs', 'xs', 'xs'],
34656             ['tall', 'xs', 'xs'],
34657             ['tall', 'xs'],
34658             ['tall']
34659             
34660         ];
34661         
34662         var queue = [];
34663         
34664         var boxes = [];
34665         
34666         var box = [];
34667         
34668         Roo.each(items, function(item, k){
34669             
34670             switch (item.size) {
34671                 // these layouts take up a full box,
34672                 case 'md' :
34673                 case 'md-left' :
34674                 case 'md-right' :
34675                 case 'wide' :
34676                     
34677                     if(box.length){
34678                         boxes.push(box);
34679                         box = [];
34680                     }
34681                     
34682                     boxes.push([item]);
34683                     
34684                     break;
34685                     
34686                 case 'xs' :
34687                 case 'sm' :
34688                 case 'tall' :
34689                     
34690                     box.push(item);
34691                     
34692                     break;
34693                 default :
34694                     break;
34695                     
34696             }
34697             
34698         }, this);
34699         
34700         if(box.length){
34701             boxes.push(box);
34702             box = [];
34703         }
34704         
34705         var filterPattern = function(box, length)
34706         {
34707             if(!box.length){
34708                 return;
34709             }
34710             
34711             var match = false;
34712             
34713             var pattern = box.slice(0, length);
34714             
34715             var format = [];
34716             
34717             Roo.each(pattern, function(i){
34718                 format.push(i.size);
34719             }, this);
34720             
34721             Roo.each(standard, function(s){
34722                 
34723                 if(String(s) != String(format)){
34724                     return;
34725                 }
34726                 
34727                 match = true;
34728                 return false;
34729                 
34730             }, this);
34731             
34732             if(!match && length == 1){
34733                 return;
34734             }
34735             
34736             if(!match){
34737                 filterPattern(box, length - 1);
34738                 return;
34739             }
34740                 
34741             queue.push(pattern);
34742
34743             box = box.slice(length, box.length);
34744
34745             filterPattern(box, 4);
34746
34747             return;
34748             
34749         }
34750         
34751         Roo.each(boxes, function(box, k){
34752             
34753             if(!box.length){
34754                 return;
34755             }
34756             
34757             if(box.length == 1){
34758                 queue.push(box);
34759                 return;
34760             }
34761             
34762             filterPattern(box, 4);
34763             
34764         }, this);
34765         
34766         this._processVerticalLayoutQueue( queue, isInstant );
34767         
34768     },
34769     
34770 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34771 //    {
34772 //        if ( !items || !items.length ) {
34773 //            return;
34774 //        }
34775 //
34776 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34777 //        
34778 //    },
34779     
34780     _horizontalLayoutItems : function ( items , isInstant)
34781     {
34782         if ( !items || !items.length || items.length < 3) {
34783             return;
34784         }
34785         
34786         items.reverse();
34787         
34788         var eItems = items.slice(0, 3);
34789         
34790         items = items.slice(3, items.length);
34791         
34792         var standard = [
34793             ['xs', 'xs', 'xs', 'wide'],
34794             ['xs', 'xs', 'wide'],
34795             ['xs', 'xs', 'sm'],
34796             ['xs', 'xs', 'xs'],
34797             ['xs', 'wide'],
34798             ['xs', 'sm'],
34799             ['xs', 'xs'],
34800             ['xs'],
34801             
34802             ['sm', 'xs', 'xs'],
34803             ['sm', 'xs'],
34804             ['sm'],
34805             
34806             ['wide', 'xs', 'xs', 'xs'],
34807             ['wide', 'xs', 'xs'],
34808             ['wide', 'xs'],
34809             ['wide'],
34810             
34811             ['wide-thin']
34812         ];
34813         
34814         var queue = [];
34815         
34816         var boxes = [];
34817         
34818         var box = [];
34819         
34820         Roo.each(items, function(item, k){
34821             
34822             switch (item.size) {
34823                 case 'md' :
34824                 case 'md-left' :
34825                 case 'md-right' :
34826                 case 'tall' :
34827                     
34828                     if(box.length){
34829                         boxes.push(box);
34830                         box = [];
34831                     }
34832                     
34833                     boxes.push([item]);
34834                     
34835                     break;
34836                     
34837                 case 'xs' :
34838                 case 'sm' :
34839                 case 'wide' :
34840                 case 'wide-thin' :
34841                     
34842                     box.push(item);
34843                     
34844                     break;
34845                 default :
34846                     break;
34847                     
34848             }
34849             
34850         }, this);
34851         
34852         if(box.length){
34853             boxes.push(box);
34854             box = [];
34855         }
34856         
34857         var filterPattern = function(box, length)
34858         {
34859             if(!box.length){
34860                 return;
34861             }
34862             
34863             var match = false;
34864             
34865             var pattern = box.slice(0, length);
34866             
34867             var format = [];
34868             
34869             Roo.each(pattern, function(i){
34870                 format.push(i.size);
34871             }, this);
34872             
34873             Roo.each(standard, function(s){
34874                 
34875                 if(String(s) != String(format)){
34876                     return;
34877                 }
34878                 
34879                 match = true;
34880                 return false;
34881                 
34882             }, this);
34883             
34884             if(!match && length == 1){
34885                 return;
34886             }
34887             
34888             if(!match){
34889                 filterPattern(box, length - 1);
34890                 return;
34891             }
34892                 
34893             queue.push(pattern);
34894
34895             box = box.slice(length, box.length);
34896
34897             filterPattern(box, 4);
34898
34899             return;
34900             
34901         }
34902         
34903         Roo.each(boxes, function(box, k){
34904             
34905             if(!box.length){
34906                 return;
34907             }
34908             
34909             if(box.length == 1){
34910                 queue.push(box);
34911                 return;
34912             }
34913             
34914             filterPattern(box, 4);
34915             
34916         }, this);
34917         
34918         
34919         var prune = [];
34920         
34921         var pos = this.el.getBox(true);
34922         
34923         var minX = pos.x;
34924         
34925         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34926         
34927         var hit_end = false;
34928         
34929         Roo.each(queue, function(box){
34930             
34931             if(hit_end){
34932                 
34933                 Roo.each(box, function(b){
34934                 
34935                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34936                     b.el.hide();
34937
34938                 }, this);
34939
34940                 return;
34941             }
34942             
34943             var mx = 0;
34944             
34945             Roo.each(box, function(b){
34946                 
34947                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34948                 b.el.show();
34949
34950                 mx = Math.max(mx, b.x);
34951                 
34952             }, this);
34953             
34954             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34955             
34956             if(maxX < minX){
34957                 
34958                 Roo.each(box, function(b){
34959                 
34960                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34961                     b.el.hide();
34962                     
34963                 }, this);
34964                 
34965                 hit_end = true;
34966                 
34967                 return;
34968             }
34969             
34970             prune.push(box);
34971             
34972         }, this);
34973         
34974         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34975     },
34976     
34977     /** Sets position of item in DOM
34978     * @param {Element} item
34979     * @param {Number} x - horizontal position
34980     * @param {Number} y - vertical position
34981     * @param {Boolean} isInstant - disables transitions
34982     */
34983     _processVerticalLayoutQueue : function( queue, isInstant )
34984     {
34985         var pos = this.el.getBox(true);
34986         var x = pos.x;
34987         var y = pos.y;
34988         var maxY = [];
34989         
34990         for (var i = 0; i < this.cols; i++){
34991             maxY[i] = pos.y;
34992         }
34993         
34994         Roo.each(queue, function(box, k){
34995             
34996             var col = k % this.cols;
34997             
34998             Roo.each(box, function(b,kk){
34999                 
35000                 b.el.position('absolute');
35001                 
35002                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35003                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35004                 
35005                 if(b.size == 'md-left' || b.size == 'md-right'){
35006                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35007                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35008                 }
35009                 
35010                 b.el.setWidth(width);
35011                 b.el.setHeight(height);
35012                 // iframe?
35013                 b.el.select('iframe',true).setSize(width,height);
35014                 
35015             }, this);
35016             
35017             for (var i = 0; i < this.cols; i++){
35018                 
35019                 if(maxY[i] < maxY[col]){
35020                     col = i;
35021                     continue;
35022                 }
35023                 
35024                 col = Math.min(col, i);
35025                 
35026             }
35027             
35028             x = pos.x + col * (this.colWidth + this.padWidth);
35029             
35030             y = maxY[col];
35031             
35032             var positions = [];
35033             
35034             switch (box.length){
35035                 case 1 :
35036                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35037                     break;
35038                 case 2 :
35039                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35040                     break;
35041                 case 3 :
35042                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35043                     break;
35044                 case 4 :
35045                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35046                     break;
35047                 default :
35048                     break;
35049             }
35050             
35051             Roo.each(box, function(b,kk){
35052                 
35053                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35054                 
35055                 var sz = b.el.getSize();
35056                 
35057                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35058                 
35059             }, this);
35060             
35061         }, this);
35062         
35063         var mY = 0;
35064         
35065         for (var i = 0; i < this.cols; i++){
35066             mY = Math.max(mY, maxY[i]);
35067         }
35068         
35069         this.el.setHeight(mY - pos.y);
35070         
35071     },
35072     
35073 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35074 //    {
35075 //        var pos = this.el.getBox(true);
35076 //        var x = pos.x;
35077 //        var y = pos.y;
35078 //        var maxX = pos.right;
35079 //        
35080 //        var maxHeight = 0;
35081 //        
35082 //        Roo.each(items, function(item, k){
35083 //            
35084 //            var c = k % 2;
35085 //            
35086 //            item.el.position('absolute');
35087 //                
35088 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35089 //
35090 //            item.el.setWidth(width);
35091 //
35092 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35093 //
35094 //            item.el.setHeight(height);
35095 //            
35096 //            if(c == 0){
35097 //                item.el.setXY([x, y], isInstant ? false : true);
35098 //            } else {
35099 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35100 //            }
35101 //            
35102 //            y = y + height + this.alternativePadWidth;
35103 //            
35104 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35105 //            
35106 //        }, this);
35107 //        
35108 //        this.el.setHeight(maxHeight);
35109 //        
35110 //    },
35111     
35112     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35113     {
35114         var pos = this.el.getBox(true);
35115         
35116         var minX = pos.x;
35117         var minY = pos.y;
35118         
35119         var maxX = pos.right;
35120         
35121         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35122         
35123         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35124         
35125         Roo.each(queue, function(box, k){
35126             
35127             Roo.each(box, function(b, kk){
35128                 
35129                 b.el.position('absolute');
35130                 
35131                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35132                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35133                 
35134                 if(b.size == 'md-left' || b.size == 'md-right'){
35135                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35136                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35137                 }
35138                 
35139                 b.el.setWidth(width);
35140                 b.el.setHeight(height);
35141                 
35142             }, this);
35143             
35144             if(!box.length){
35145                 return;
35146             }
35147             
35148             var positions = [];
35149             
35150             switch (box.length){
35151                 case 1 :
35152                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35153                     break;
35154                 case 2 :
35155                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35156                     break;
35157                 case 3 :
35158                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35159                     break;
35160                 case 4 :
35161                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35162                     break;
35163                 default :
35164                     break;
35165             }
35166             
35167             Roo.each(box, function(b,kk){
35168                 
35169                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35170                 
35171                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35172                 
35173             }, this);
35174             
35175         }, this);
35176         
35177     },
35178     
35179     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35180     {
35181         Roo.each(eItems, function(b,k){
35182             
35183             b.size = (k == 0) ? 'sm' : 'xs';
35184             b.x = (k == 0) ? 2 : 1;
35185             b.y = (k == 0) ? 2 : 1;
35186             
35187             b.el.position('absolute');
35188             
35189             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35190                 
35191             b.el.setWidth(width);
35192             
35193             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35194             
35195             b.el.setHeight(height);
35196             
35197         }, this);
35198
35199         var positions = [];
35200         
35201         positions.push({
35202             x : maxX - this.unitWidth * 2 - this.gutter,
35203             y : minY
35204         });
35205         
35206         positions.push({
35207             x : maxX - this.unitWidth,
35208             y : minY + (this.unitWidth + this.gutter) * 2
35209         });
35210         
35211         positions.push({
35212             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35213             y : minY
35214         });
35215         
35216         Roo.each(eItems, function(b,k){
35217             
35218             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35219
35220         }, this);
35221         
35222     },
35223     
35224     getVerticalOneBoxColPositions : function(x, y, box)
35225     {
35226         var pos = [];
35227         
35228         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35229         
35230         if(box[0].size == 'md-left'){
35231             rand = 0;
35232         }
35233         
35234         if(box[0].size == 'md-right'){
35235             rand = 1;
35236         }
35237         
35238         pos.push({
35239             x : x + (this.unitWidth + this.gutter) * rand,
35240             y : y
35241         });
35242         
35243         return pos;
35244     },
35245     
35246     getVerticalTwoBoxColPositions : function(x, y, box)
35247     {
35248         var pos = [];
35249         
35250         if(box[0].size == 'xs'){
35251             
35252             pos.push({
35253                 x : x,
35254                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35255             });
35256
35257             pos.push({
35258                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35259                 y : y
35260             });
35261             
35262             return pos;
35263             
35264         }
35265         
35266         pos.push({
35267             x : x,
35268             y : y
35269         });
35270
35271         pos.push({
35272             x : x + (this.unitWidth + this.gutter) * 2,
35273             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35274         });
35275         
35276         return pos;
35277         
35278     },
35279     
35280     getVerticalThreeBoxColPositions : function(x, y, box)
35281     {
35282         var pos = [];
35283         
35284         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35285             
35286             pos.push({
35287                 x : x,
35288                 y : y
35289             });
35290
35291             pos.push({
35292                 x : x + (this.unitWidth + this.gutter) * 1,
35293                 y : y
35294             });
35295             
35296             pos.push({
35297                 x : x + (this.unitWidth + this.gutter) * 2,
35298                 y : y
35299             });
35300             
35301             return pos;
35302             
35303         }
35304         
35305         if(box[0].size == 'xs' && box[1].size == 'xs'){
35306             
35307             pos.push({
35308                 x : x,
35309                 y : y
35310             });
35311
35312             pos.push({
35313                 x : x,
35314                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35315             });
35316             
35317             pos.push({
35318                 x : x + (this.unitWidth + this.gutter) * 1,
35319                 y : y
35320             });
35321             
35322             return pos;
35323             
35324         }
35325         
35326         pos.push({
35327             x : x,
35328             y : y
35329         });
35330
35331         pos.push({
35332             x : x + (this.unitWidth + this.gutter) * 2,
35333             y : y
35334         });
35335
35336         pos.push({
35337             x : x + (this.unitWidth + this.gutter) * 2,
35338             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35339         });
35340             
35341         return pos;
35342         
35343     },
35344     
35345     getVerticalFourBoxColPositions : function(x, y, box)
35346     {
35347         var pos = [];
35348         
35349         if(box[0].size == 'xs'){
35350             
35351             pos.push({
35352                 x : x,
35353                 y : y
35354             });
35355
35356             pos.push({
35357                 x : x,
35358                 y : y + (this.unitHeight + this.gutter) * 1
35359             });
35360             
35361             pos.push({
35362                 x : x,
35363                 y : y + (this.unitHeight + this.gutter) * 2
35364             });
35365             
35366             pos.push({
35367                 x : x + (this.unitWidth + this.gutter) * 1,
35368                 y : y
35369             });
35370             
35371             return pos;
35372             
35373         }
35374         
35375         pos.push({
35376             x : x,
35377             y : y
35378         });
35379
35380         pos.push({
35381             x : x + (this.unitWidth + this.gutter) * 2,
35382             y : y
35383         });
35384
35385         pos.push({
35386             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35387             y : y + (this.unitHeight + this.gutter) * 1
35388         });
35389
35390         pos.push({
35391             x : x + (this.unitWidth + this.gutter) * 2,
35392             y : y + (this.unitWidth + this.gutter) * 2
35393         });
35394
35395         return pos;
35396         
35397     },
35398     
35399     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35400     {
35401         var pos = [];
35402         
35403         if(box[0].size == 'md-left'){
35404             pos.push({
35405                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35406                 y : minY
35407             });
35408             
35409             return pos;
35410         }
35411         
35412         if(box[0].size == 'md-right'){
35413             pos.push({
35414                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35415                 y : minY + (this.unitWidth + this.gutter) * 1
35416             });
35417             
35418             return pos;
35419         }
35420         
35421         var rand = Math.floor(Math.random() * (4 - box[0].y));
35422         
35423         pos.push({
35424             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35425             y : minY + (this.unitWidth + this.gutter) * rand
35426         });
35427         
35428         return pos;
35429         
35430     },
35431     
35432     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35433     {
35434         var pos = [];
35435         
35436         if(box[0].size == 'xs'){
35437             
35438             pos.push({
35439                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35440                 y : minY
35441             });
35442
35443             pos.push({
35444                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35445                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35446             });
35447             
35448             return pos;
35449             
35450         }
35451         
35452         pos.push({
35453             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35454             y : minY
35455         });
35456
35457         pos.push({
35458             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35459             y : minY + (this.unitWidth + this.gutter) * 2
35460         });
35461         
35462         return pos;
35463         
35464     },
35465     
35466     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35467     {
35468         var pos = [];
35469         
35470         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35471             
35472             pos.push({
35473                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35474                 y : minY
35475             });
35476
35477             pos.push({
35478                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35479                 y : minY + (this.unitWidth + this.gutter) * 1
35480             });
35481             
35482             pos.push({
35483                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35484                 y : minY + (this.unitWidth + this.gutter) * 2
35485             });
35486             
35487             return pos;
35488             
35489         }
35490         
35491         if(box[0].size == 'xs' && box[1].size == 'xs'){
35492             
35493             pos.push({
35494                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35495                 y : minY
35496             });
35497
35498             pos.push({
35499                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35500                 y : minY
35501             });
35502             
35503             pos.push({
35504                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35505                 y : minY + (this.unitWidth + this.gutter) * 1
35506             });
35507             
35508             return pos;
35509             
35510         }
35511         
35512         pos.push({
35513             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35514             y : minY
35515         });
35516
35517         pos.push({
35518             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35519             y : minY + (this.unitWidth + this.gutter) * 2
35520         });
35521
35522         pos.push({
35523             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35524             y : minY + (this.unitWidth + this.gutter) * 2
35525         });
35526             
35527         return pos;
35528         
35529     },
35530     
35531     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35532     {
35533         var pos = [];
35534         
35535         if(box[0].size == 'xs'){
35536             
35537             pos.push({
35538                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35539                 y : minY
35540             });
35541
35542             pos.push({
35543                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35544                 y : minY
35545             });
35546             
35547             pos.push({
35548                 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),
35549                 y : minY
35550             });
35551             
35552             pos.push({
35553                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35554                 y : minY + (this.unitWidth + this.gutter) * 1
35555             });
35556             
35557             return pos;
35558             
35559         }
35560         
35561         pos.push({
35562             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35563             y : minY
35564         });
35565         
35566         pos.push({
35567             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35568             y : minY + (this.unitWidth + this.gutter) * 2
35569         });
35570         
35571         pos.push({
35572             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35573             y : minY + (this.unitWidth + this.gutter) * 2
35574         });
35575         
35576         pos.push({
35577             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),
35578             y : minY + (this.unitWidth + this.gutter) * 2
35579         });
35580
35581         return pos;
35582         
35583     },
35584     
35585     /**
35586     * remove a Masonry Brick
35587     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35588     */
35589     removeBrick : function(brick_id)
35590     {
35591         if (!brick_id) {
35592             return;
35593         }
35594         
35595         for (var i = 0; i<this.bricks.length; i++) {
35596             if (this.bricks[i].id == brick_id) {
35597                 this.bricks.splice(i,1);
35598                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35599                 this.initial();
35600             }
35601         }
35602     },
35603     
35604     /**
35605     * adds a Masonry Brick
35606     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35607     */
35608     addBrick : function(cfg)
35609     {
35610         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35611         //this.register(cn);
35612         cn.parentId = this.id;
35613         cn.render(this.el);
35614         return cn;
35615     },
35616     
35617     /**
35618     * register a Masonry Brick
35619     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35620     */
35621     
35622     register : function(brick)
35623     {
35624         this.bricks.push(brick);
35625         brick.masonryId = this.id;
35626     },
35627     
35628     /**
35629     * clear all the Masonry Brick
35630     */
35631     clearAll : function()
35632     {
35633         this.bricks = [];
35634         //this.getChildContainer().dom.innerHTML = "";
35635         this.el.dom.innerHTML = '';
35636     },
35637     
35638     getSelected : function()
35639     {
35640         if (!this.selectedBrick) {
35641             return false;
35642         }
35643         
35644         return this.selectedBrick;
35645     }
35646 });
35647
35648 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35649     
35650     groups: {},
35651      /**
35652     * register a Masonry Layout
35653     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35654     */
35655     
35656     register : function(layout)
35657     {
35658         this.groups[layout.id] = layout;
35659     },
35660     /**
35661     * fetch a  Masonry Layout based on the masonry layout ID
35662     * @param {string} the masonry layout to add
35663     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35664     */
35665     
35666     get: function(layout_id) {
35667         if (typeof(this.groups[layout_id]) == 'undefined') {
35668             return false;
35669         }
35670         return this.groups[layout_id] ;
35671     }
35672     
35673     
35674     
35675 });
35676
35677  
35678
35679  /**
35680  *
35681  * This is based on 
35682  * http://masonry.desandro.com
35683  *
35684  * The idea is to render all the bricks based on vertical width...
35685  *
35686  * The original code extends 'outlayer' - we might need to use that....
35687  * 
35688  */
35689
35690
35691 /**
35692  * @class Roo.bootstrap.LayoutMasonryAuto
35693  * @extends Roo.bootstrap.Component
35694  * Bootstrap Layout Masonry class
35695  * 
35696  * @constructor
35697  * Create a new Element
35698  * @param {Object} config The config object
35699  */
35700
35701 Roo.bootstrap.LayoutMasonryAuto = function(config){
35702     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35703 };
35704
35705 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35706     
35707       /**
35708      * @cfg {Boolean} isFitWidth  - resize the width..
35709      */   
35710     isFitWidth : false,  // options..
35711     /**
35712      * @cfg {Boolean} isOriginLeft = left align?
35713      */   
35714     isOriginLeft : true,
35715     /**
35716      * @cfg {Boolean} isOriginTop = top align?
35717      */   
35718     isOriginTop : false,
35719     /**
35720      * @cfg {Boolean} isLayoutInstant = no animation?
35721      */   
35722     isLayoutInstant : false, // needed?
35723     /**
35724      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35725      */   
35726     isResizingContainer : true,
35727     /**
35728      * @cfg {Number} columnWidth  width of the columns 
35729      */   
35730     
35731     columnWidth : 0,
35732     
35733     /**
35734      * @cfg {Number} maxCols maximum number of columns
35735      */   
35736     
35737     maxCols: 0,
35738     /**
35739      * @cfg {Number} padHeight padding below box..
35740      */   
35741     
35742     padHeight : 10, 
35743     
35744     /**
35745      * @cfg {Boolean} isAutoInitial defalut true
35746      */   
35747     
35748     isAutoInitial : true, 
35749     
35750     // private?
35751     gutter : 0,
35752     
35753     containerWidth: 0,
35754     initialColumnWidth : 0,
35755     currentSize : null,
35756     
35757     colYs : null, // array.
35758     maxY : 0,
35759     padWidth: 10,
35760     
35761     
35762     tag: 'div',
35763     cls: '',
35764     bricks: null, //CompositeElement
35765     cols : 0, // array?
35766     // element : null, // wrapped now this.el
35767     _isLayoutInited : null, 
35768     
35769     
35770     getAutoCreate : function(){
35771         
35772         var cfg = {
35773             tag: this.tag,
35774             cls: 'blog-masonary-wrapper ' + this.cls,
35775             cn : {
35776                 cls : 'mas-boxes masonary'
35777             }
35778         };
35779         
35780         return cfg;
35781     },
35782     
35783     getChildContainer: function( )
35784     {
35785         if (this.boxesEl) {
35786             return this.boxesEl;
35787         }
35788         
35789         this.boxesEl = this.el.select('.mas-boxes').first();
35790         
35791         return this.boxesEl;
35792     },
35793     
35794     
35795     initEvents : function()
35796     {
35797         var _this = this;
35798         
35799         if(this.isAutoInitial){
35800             Roo.log('hook children rendered');
35801             this.on('childrenrendered', function() {
35802                 Roo.log('children rendered');
35803                 _this.initial();
35804             } ,this);
35805         }
35806         
35807     },
35808     
35809     initial : function()
35810     {
35811         this.reloadItems();
35812
35813         this.currentSize = this.el.getBox(true);
35814
35815         /// was window resize... - let's see if this works..
35816         Roo.EventManager.onWindowResize(this.resize, this); 
35817
35818         if(!this.isAutoInitial){
35819             this.layout();
35820             return;
35821         }
35822         
35823         this.layout.defer(500,this);
35824     },
35825     
35826     reloadItems: function()
35827     {
35828         this.bricks = this.el.select('.masonry-brick', true);
35829         
35830         this.bricks.each(function(b) {
35831             //Roo.log(b.getSize());
35832             if (!b.attr('originalwidth')) {
35833                 b.attr('originalwidth',  b.getSize().width);
35834             }
35835             
35836         });
35837         
35838         Roo.log(this.bricks.elements.length);
35839     },
35840     
35841     resize : function()
35842     {
35843         Roo.log('resize');
35844         var cs = this.el.getBox(true);
35845         
35846         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35847             Roo.log("no change in with or X");
35848             return;
35849         }
35850         this.currentSize = cs;
35851         this.layout();
35852     },
35853     
35854     layout : function()
35855     {
35856          Roo.log('layout');
35857         this._resetLayout();
35858         //this._manageStamps();
35859       
35860         // don't animate first layout
35861         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35862         this.layoutItems( isInstant );
35863       
35864         // flag for initalized
35865         this._isLayoutInited = true;
35866     },
35867     
35868     layoutItems : function( isInstant )
35869     {
35870         //var items = this._getItemsForLayout( this.items );
35871         // original code supports filtering layout items.. we just ignore it..
35872         
35873         this._layoutItems( this.bricks , isInstant );
35874       
35875         this._postLayout();
35876     },
35877     _layoutItems : function ( items , isInstant)
35878     {
35879        //this.fireEvent( 'layout', this, items );
35880     
35881
35882         if ( !items || !items.elements.length ) {
35883           // no items, emit event with empty array
35884             return;
35885         }
35886
35887         var queue = [];
35888         items.each(function(item) {
35889             Roo.log("layout item");
35890             Roo.log(item);
35891             // get x/y object from method
35892             var position = this._getItemLayoutPosition( item );
35893             // enqueue
35894             position.item = item;
35895             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35896             queue.push( position );
35897         }, this);
35898       
35899         this._processLayoutQueue( queue );
35900     },
35901     /** Sets position of item in DOM
35902     * @param {Element} item
35903     * @param {Number} x - horizontal position
35904     * @param {Number} y - vertical position
35905     * @param {Boolean} isInstant - disables transitions
35906     */
35907     _processLayoutQueue : function( queue )
35908     {
35909         for ( var i=0, len = queue.length; i < len; i++ ) {
35910             var obj = queue[i];
35911             obj.item.position('absolute');
35912             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35913         }
35914     },
35915       
35916     
35917     /**
35918     * Any logic you want to do after each layout,
35919     * i.e. size the container
35920     */
35921     _postLayout : function()
35922     {
35923         this.resizeContainer();
35924     },
35925     
35926     resizeContainer : function()
35927     {
35928         if ( !this.isResizingContainer ) {
35929             return;
35930         }
35931         var size = this._getContainerSize();
35932         if ( size ) {
35933             this.el.setSize(size.width,size.height);
35934             this.boxesEl.setSize(size.width,size.height);
35935         }
35936     },
35937     
35938     
35939     
35940     _resetLayout : function()
35941     {
35942         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35943         this.colWidth = this.el.getWidth();
35944         //this.gutter = this.el.getWidth(); 
35945         
35946         this.measureColumns();
35947
35948         // reset column Y
35949         var i = this.cols;
35950         this.colYs = [];
35951         while (i--) {
35952             this.colYs.push( 0 );
35953         }
35954     
35955         this.maxY = 0;
35956     },
35957
35958     measureColumns : function()
35959     {
35960         this.getContainerWidth();
35961       // if columnWidth is 0, default to outerWidth of first item
35962         if ( !this.columnWidth ) {
35963             var firstItem = this.bricks.first();
35964             Roo.log(firstItem);
35965             this.columnWidth  = this.containerWidth;
35966             if (firstItem && firstItem.attr('originalwidth') ) {
35967                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35968             }
35969             // columnWidth fall back to item of first element
35970             Roo.log("set column width?");
35971                         this.initialColumnWidth = this.columnWidth  ;
35972
35973             // if first elem has no width, default to size of container
35974             
35975         }
35976         
35977         
35978         if (this.initialColumnWidth) {
35979             this.columnWidth = this.initialColumnWidth;
35980         }
35981         
35982         
35983             
35984         // column width is fixed at the top - however if container width get's smaller we should
35985         // reduce it...
35986         
35987         // this bit calcs how man columns..
35988             
35989         var columnWidth = this.columnWidth += this.gutter;
35990       
35991         // calculate columns
35992         var containerWidth = this.containerWidth + this.gutter;
35993         
35994         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35995         // fix rounding errors, typically with gutters
35996         var excess = columnWidth - containerWidth % columnWidth;
35997         
35998         
35999         // if overshoot is less than a pixel, round up, otherwise floor it
36000         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36001         cols = Math[ mathMethod ]( cols );
36002         this.cols = Math.max( cols, 1 );
36003         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36004         
36005          // padding positioning..
36006         var totalColWidth = this.cols * this.columnWidth;
36007         var padavail = this.containerWidth - totalColWidth;
36008         // so for 2 columns - we need 3 'pads'
36009         
36010         var padNeeded = (1+this.cols) * this.padWidth;
36011         
36012         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36013         
36014         this.columnWidth += padExtra
36015         //this.padWidth = Math.floor(padavail /  ( this.cols));
36016         
36017         // adjust colum width so that padding is fixed??
36018         
36019         // we have 3 columns ... total = width * 3
36020         // we have X left over... that should be used by 
36021         
36022         //if (this.expandC) {
36023             
36024         //}
36025         
36026         
36027         
36028     },
36029     
36030     getContainerWidth : function()
36031     {
36032        /* // container is parent if fit width
36033         var container = this.isFitWidth ? this.element.parentNode : this.element;
36034         // check that this.size and size are there
36035         // IE8 triggers resize on body size change, so they might not be
36036         
36037         var size = getSize( container );  //FIXME
36038         this.containerWidth = size && size.innerWidth; //FIXME
36039         */
36040          
36041         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36042         
36043     },
36044     
36045     _getItemLayoutPosition : function( item )  // what is item?
36046     {
36047         // we resize the item to our columnWidth..
36048       
36049         item.setWidth(this.columnWidth);
36050         item.autoBoxAdjust  = false;
36051         
36052         var sz = item.getSize();
36053  
36054         // how many columns does this brick span
36055         var remainder = this.containerWidth % this.columnWidth;
36056         
36057         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36058         // round if off by 1 pixel, otherwise use ceil
36059         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36060         colSpan = Math.min( colSpan, this.cols );
36061         
36062         // normally this should be '1' as we dont' currently allow multi width columns..
36063         
36064         var colGroup = this._getColGroup( colSpan );
36065         // get the minimum Y value from the columns
36066         var minimumY = Math.min.apply( Math, colGroup );
36067         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36068         
36069         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36070          
36071         // position the brick
36072         var position = {
36073             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36074             y: this.currentSize.y + minimumY + this.padHeight
36075         };
36076         
36077         Roo.log(position);
36078         // apply setHeight to necessary columns
36079         var setHeight = minimumY + sz.height + this.padHeight;
36080         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36081         
36082         var setSpan = this.cols + 1 - colGroup.length;
36083         for ( var i = 0; i < setSpan; i++ ) {
36084           this.colYs[ shortColIndex + i ] = setHeight ;
36085         }
36086       
36087         return position;
36088     },
36089     
36090     /**
36091      * @param {Number} colSpan - number of columns the element spans
36092      * @returns {Array} colGroup
36093      */
36094     _getColGroup : function( colSpan )
36095     {
36096         if ( colSpan < 2 ) {
36097           // if brick spans only one column, use all the column Ys
36098           return this.colYs;
36099         }
36100       
36101         var colGroup = [];
36102         // how many different places could this brick fit horizontally
36103         var groupCount = this.cols + 1 - colSpan;
36104         // for each group potential horizontal position
36105         for ( var i = 0; i < groupCount; i++ ) {
36106           // make an array of colY values for that one group
36107           var groupColYs = this.colYs.slice( i, i + colSpan );
36108           // and get the max value of the array
36109           colGroup[i] = Math.max.apply( Math, groupColYs );
36110         }
36111         return colGroup;
36112     },
36113     /*
36114     _manageStamp : function( stamp )
36115     {
36116         var stampSize =  stamp.getSize();
36117         var offset = stamp.getBox();
36118         // get the columns that this stamp affects
36119         var firstX = this.isOriginLeft ? offset.x : offset.right;
36120         var lastX = firstX + stampSize.width;
36121         var firstCol = Math.floor( firstX / this.columnWidth );
36122         firstCol = Math.max( 0, firstCol );
36123         
36124         var lastCol = Math.floor( lastX / this.columnWidth );
36125         // lastCol should not go over if multiple of columnWidth #425
36126         lastCol -= lastX % this.columnWidth ? 0 : 1;
36127         lastCol = Math.min( this.cols - 1, lastCol );
36128         
36129         // set colYs to bottom of the stamp
36130         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36131             stampSize.height;
36132             
36133         for ( var i = firstCol; i <= lastCol; i++ ) {
36134           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36135         }
36136     },
36137     */
36138     
36139     _getContainerSize : function()
36140     {
36141         this.maxY = Math.max.apply( Math, this.colYs );
36142         var size = {
36143             height: this.maxY
36144         };
36145       
36146         if ( this.isFitWidth ) {
36147             size.width = this._getContainerFitWidth();
36148         }
36149       
36150         return size;
36151     },
36152     
36153     _getContainerFitWidth : function()
36154     {
36155         var unusedCols = 0;
36156         // count unused columns
36157         var i = this.cols;
36158         while ( --i ) {
36159           if ( this.colYs[i] !== 0 ) {
36160             break;
36161           }
36162           unusedCols++;
36163         }
36164         // fit container to columns that have been used
36165         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36166     },
36167     
36168     needsResizeLayout : function()
36169     {
36170         var previousWidth = this.containerWidth;
36171         this.getContainerWidth();
36172         return previousWidth !== this.containerWidth;
36173     }
36174  
36175 });
36176
36177  
36178
36179  /*
36180  * - LGPL
36181  *
36182  * element
36183  * 
36184  */
36185
36186 /**
36187  * @class Roo.bootstrap.MasonryBrick
36188  * @extends Roo.bootstrap.Component
36189  * Bootstrap MasonryBrick class
36190  * 
36191  * @constructor
36192  * Create a new MasonryBrick
36193  * @param {Object} config The config object
36194  */
36195
36196 Roo.bootstrap.MasonryBrick = function(config){
36197     
36198     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36199     
36200     Roo.bootstrap.MasonryBrick.register(this);
36201     
36202     this.addEvents({
36203         // raw events
36204         /**
36205          * @event click
36206          * When a MasonryBrick is clcik
36207          * @param {Roo.bootstrap.MasonryBrick} this
36208          * @param {Roo.EventObject} e
36209          */
36210         "click" : true
36211     });
36212 };
36213
36214 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36215     
36216     /**
36217      * @cfg {String} title
36218      */   
36219     title : '',
36220     /**
36221      * @cfg {String} html
36222      */   
36223     html : '',
36224     /**
36225      * @cfg {String} bgimage
36226      */   
36227     bgimage : '',
36228     /**
36229      * @cfg {String} videourl
36230      */   
36231     videourl : '',
36232     /**
36233      * @cfg {String} cls
36234      */   
36235     cls : '',
36236     /**
36237      * @cfg {String} href
36238      */   
36239     href : '',
36240     /**
36241      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36242      */   
36243     size : 'xs',
36244     
36245     /**
36246      * @cfg {String} placetitle (center|bottom)
36247      */   
36248     placetitle : '',
36249     
36250     /**
36251      * @cfg {Boolean} isFitContainer defalut true
36252      */   
36253     isFitContainer : true, 
36254     
36255     /**
36256      * @cfg {Boolean} preventDefault defalut false
36257      */   
36258     preventDefault : false, 
36259     
36260     /**
36261      * @cfg {Boolean} inverse defalut false
36262      */   
36263     maskInverse : false, 
36264     
36265     getAutoCreate : function()
36266     {
36267         if(!this.isFitContainer){
36268             return this.getSplitAutoCreate();
36269         }
36270         
36271         var cls = 'masonry-brick masonry-brick-full';
36272         
36273         if(this.href.length){
36274             cls += ' masonry-brick-link';
36275         }
36276         
36277         if(this.bgimage.length){
36278             cls += ' masonry-brick-image';
36279         }
36280         
36281         if(this.maskInverse){
36282             cls += ' mask-inverse';
36283         }
36284         
36285         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36286             cls += ' enable-mask';
36287         }
36288         
36289         if(this.size){
36290             cls += ' masonry-' + this.size + '-brick';
36291         }
36292         
36293         if(this.placetitle.length){
36294             
36295             switch (this.placetitle) {
36296                 case 'center' :
36297                     cls += ' masonry-center-title';
36298                     break;
36299                 case 'bottom' :
36300                     cls += ' masonry-bottom-title';
36301                     break;
36302                 default:
36303                     break;
36304             }
36305             
36306         } else {
36307             if(!this.html.length && !this.bgimage.length){
36308                 cls += ' masonry-center-title';
36309             }
36310
36311             if(!this.html.length && this.bgimage.length){
36312                 cls += ' masonry-bottom-title';
36313             }
36314         }
36315         
36316         if(this.cls){
36317             cls += ' ' + this.cls;
36318         }
36319         
36320         var cfg = {
36321             tag: (this.href.length) ? 'a' : 'div',
36322             cls: cls,
36323             cn: [
36324                 {
36325                     tag: 'div',
36326                     cls: 'masonry-brick-mask'
36327                 },
36328                 {
36329                     tag: 'div',
36330                     cls: 'masonry-brick-paragraph',
36331                     cn: []
36332                 }
36333             ]
36334         };
36335         
36336         if(this.href.length){
36337             cfg.href = this.href;
36338         }
36339         
36340         var cn = cfg.cn[1].cn;
36341         
36342         if(this.title.length){
36343             cn.push({
36344                 tag: 'h4',
36345                 cls: 'masonry-brick-title',
36346                 html: this.title
36347             });
36348         }
36349         
36350         if(this.html.length){
36351             cn.push({
36352                 tag: 'p',
36353                 cls: 'masonry-brick-text',
36354                 html: this.html
36355             });
36356         }
36357         
36358         if (!this.title.length && !this.html.length) {
36359             cfg.cn[1].cls += ' hide';
36360         }
36361         
36362         if(this.bgimage.length){
36363             cfg.cn.push({
36364                 tag: 'img',
36365                 cls: 'masonry-brick-image-view',
36366                 src: this.bgimage
36367             });
36368         }
36369         
36370         if(this.videourl.length){
36371             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36372             // youtube support only?
36373             cfg.cn.push({
36374                 tag: 'iframe',
36375                 cls: 'masonry-brick-image-view',
36376                 src: vurl,
36377                 frameborder : 0,
36378                 allowfullscreen : true
36379             });
36380         }
36381         
36382         return cfg;
36383         
36384     },
36385     
36386     getSplitAutoCreate : function()
36387     {
36388         var cls = 'masonry-brick masonry-brick-split';
36389         
36390         if(this.href.length){
36391             cls += ' masonry-brick-link';
36392         }
36393         
36394         if(this.bgimage.length){
36395             cls += ' masonry-brick-image';
36396         }
36397         
36398         if(this.size){
36399             cls += ' masonry-' + this.size + '-brick';
36400         }
36401         
36402         switch (this.placetitle) {
36403             case 'center' :
36404                 cls += ' masonry-center-title';
36405                 break;
36406             case 'bottom' :
36407                 cls += ' masonry-bottom-title';
36408                 break;
36409             default:
36410                 if(!this.bgimage.length){
36411                     cls += ' masonry-center-title';
36412                 }
36413
36414                 if(this.bgimage.length){
36415                     cls += ' masonry-bottom-title';
36416                 }
36417                 break;
36418         }
36419         
36420         if(this.cls){
36421             cls += ' ' + this.cls;
36422         }
36423         
36424         var cfg = {
36425             tag: (this.href.length) ? 'a' : 'div',
36426             cls: cls,
36427             cn: [
36428                 {
36429                     tag: 'div',
36430                     cls: 'masonry-brick-split-head',
36431                     cn: [
36432                         {
36433                             tag: 'div',
36434                             cls: 'masonry-brick-paragraph',
36435                             cn: []
36436                         }
36437                     ]
36438                 },
36439                 {
36440                     tag: 'div',
36441                     cls: 'masonry-brick-split-body',
36442                     cn: []
36443                 }
36444             ]
36445         };
36446         
36447         if(this.href.length){
36448             cfg.href = this.href;
36449         }
36450         
36451         if(this.title.length){
36452             cfg.cn[0].cn[0].cn.push({
36453                 tag: 'h4',
36454                 cls: 'masonry-brick-title',
36455                 html: this.title
36456             });
36457         }
36458         
36459         if(this.html.length){
36460             cfg.cn[1].cn.push({
36461                 tag: 'p',
36462                 cls: 'masonry-brick-text',
36463                 html: this.html
36464             });
36465         }
36466
36467         if(this.bgimage.length){
36468             cfg.cn[0].cn.push({
36469                 tag: 'img',
36470                 cls: 'masonry-brick-image-view',
36471                 src: this.bgimage
36472             });
36473         }
36474         
36475         if(this.videourl.length){
36476             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36477             // youtube support only?
36478             cfg.cn[0].cn.cn.push({
36479                 tag: 'iframe',
36480                 cls: 'masonry-brick-image-view',
36481                 src: vurl,
36482                 frameborder : 0,
36483                 allowfullscreen : true
36484             });
36485         }
36486         
36487         return cfg;
36488     },
36489     
36490     initEvents: function() 
36491     {
36492         switch (this.size) {
36493             case 'xs' :
36494                 this.x = 1;
36495                 this.y = 1;
36496                 break;
36497             case 'sm' :
36498                 this.x = 2;
36499                 this.y = 2;
36500                 break;
36501             case 'md' :
36502             case 'md-left' :
36503             case 'md-right' :
36504                 this.x = 3;
36505                 this.y = 3;
36506                 break;
36507             case 'tall' :
36508                 this.x = 2;
36509                 this.y = 3;
36510                 break;
36511             case 'wide' :
36512                 this.x = 3;
36513                 this.y = 2;
36514                 break;
36515             case 'wide-thin' :
36516                 this.x = 3;
36517                 this.y = 1;
36518                 break;
36519                         
36520             default :
36521                 break;
36522         }
36523         
36524         if(Roo.isTouch){
36525             this.el.on('touchstart', this.onTouchStart, this);
36526             this.el.on('touchmove', this.onTouchMove, this);
36527             this.el.on('touchend', this.onTouchEnd, this);
36528             this.el.on('contextmenu', this.onContextMenu, this);
36529         } else {
36530             this.el.on('mouseenter'  ,this.enter, this);
36531             this.el.on('mouseleave', this.leave, this);
36532             this.el.on('click', this.onClick, this);
36533         }
36534         
36535         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36536             this.parent().bricks.push(this);   
36537         }
36538         
36539     },
36540     
36541     onClick: function(e, el)
36542     {
36543         var time = this.endTimer - this.startTimer;
36544         // Roo.log(e.preventDefault());
36545         if(Roo.isTouch){
36546             if(time > 1000){
36547                 e.preventDefault();
36548                 return;
36549             }
36550         }
36551         
36552         if(!this.preventDefault){
36553             return;
36554         }
36555         
36556         e.preventDefault();
36557         
36558         if (this.activeClass != '') {
36559             this.selectBrick();
36560         }
36561         
36562         this.fireEvent('click', this, e);
36563     },
36564     
36565     enter: function(e, el)
36566     {
36567         e.preventDefault();
36568         
36569         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36570             return;
36571         }
36572         
36573         if(this.bgimage.length && this.html.length){
36574             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36575         }
36576     },
36577     
36578     leave: function(e, el)
36579     {
36580         e.preventDefault();
36581         
36582         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36583             return;
36584         }
36585         
36586         if(this.bgimage.length && this.html.length){
36587             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36588         }
36589     },
36590     
36591     onTouchStart: function(e, el)
36592     {
36593 //        e.preventDefault();
36594         
36595         this.touchmoved = false;
36596         
36597         if(!this.isFitContainer){
36598             return;
36599         }
36600         
36601         if(!this.bgimage.length || !this.html.length){
36602             return;
36603         }
36604         
36605         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36606         
36607         this.timer = new Date().getTime();
36608         
36609     },
36610     
36611     onTouchMove: function(e, el)
36612     {
36613         this.touchmoved = true;
36614     },
36615     
36616     onContextMenu : function(e,el)
36617     {
36618         e.preventDefault();
36619         e.stopPropagation();
36620         return false;
36621     },
36622     
36623     onTouchEnd: function(e, el)
36624     {
36625 //        e.preventDefault();
36626         
36627         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36628         
36629             this.leave(e,el);
36630             
36631             return;
36632         }
36633         
36634         if(!this.bgimage.length || !this.html.length){
36635             
36636             if(this.href.length){
36637                 window.location.href = this.href;
36638             }
36639             
36640             return;
36641         }
36642         
36643         if(!this.isFitContainer){
36644             return;
36645         }
36646         
36647         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36648         
36649         window.location.href = this.href;
36650     },
36651     
36652     //selection on single brick only
36653     selectBrick : function() {
36654         
36655         if (!this.parentId) {
36656             return;
36657         }
36658         
36659         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36660         var index = m.selectedBrick.indexOf(this.id);
36661         
36662         if ( index > -1) {
36663             m.selectedBrick.splice(index,1);
36664             this.el.removeClass(this.activeClass);
36665             return;
36666         }
36667         
36668         for(var i = 0; i < m.selectedBrick.length; i++) {
36669             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36670             b.el.removeClass(b.activeClass);
36671         }
36672         
36673         m.selectedBrick = [];
36674         
36675         m.selectedBrick.push(this.id);
36676         this.el.addClass(this.activeClass);
36677         return;
36678     },
36679     
36680     isSelected : function(){
36681         return this.el.hasClass(this.activeClass);
36682         
36683     }
36684 });
36685
36686 Roo.apply(Roo.bootstrap.MasonryBrick, {
36687     
36688     //groups: {},
36689     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36690      /**
36691     * register a Masonry Brick
36692     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36693     */
36694     
36695     register : function(brick)
36696     {
36697         //this.groups[brick.id] = brick;
36698         this.groups.add(brick.id, brick);
36699     },
36700     /**
36701     * fetch a  masonry brick based on the masonry brick ID
36702     * @param {string} the masonry brick to add
36703     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36704     */
36705     
36706     get: function(brick_id) 
36707     {
36708         // if (typeof(this.groups[brick_id]) == 'undefined') {
36709         //     return false;
36710         // }
36711         // return this.groups[brick_id] ;
36712         
36713         if(this.groups.key(brick_id)) {
36714             return this.groups.key(brick_id);
36715         }
36716         
36717         return false;
36718     }
36719     
36720     
36721     
36722 });
36723
36724  /*
36725  * - LGPL
36726  *
36727  * element
36728  * 
36729  */
36730
36731 /**
36732  * @class Roo.bootstrap.Brick
36733  * @extends Roo.bootstrap.Component
36734  * Bootstrap Brick class
36735  * 
36736  * @constructor
36737  * Create a new Brick
36738  * @param {Object} config The config object
36739  */
36740
36741 Roo.bootstrap.Brick = function(config){
36742     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36743     
36744     this.addEvents({
36745         // raw events
36746         /**
36747          * @event click
36748          * When a Brick is click
36749          * @param {Roo.bootstrap.Brick} this
36750          * @param {Roo.EventObject} e
36751          */
36752         "click" : true
36753     });
36754 };
36755
36756 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36757     
36758     /**
36759      * @cfg {String} title
36760      */   
36761     title : '',
36762     /**
36763      * @cfg {String} html
36764      */   
36765     html : '',
36766     /**
36767      * @cfg {String} bgimage
36768      */   
36769     bgimage : '',
36770     /**
36771      * @cfg {String} cls
36772      */   
36773     cls : '',
36774     /**
36775      * @cfg {String} href
36776      */   
36777     href : '',
36778     /**
36779      * @cfg {String} video
36780      */   
36781     video : '',
36782     /**
36783      * @cfg {Boolean} square
36784      */   
36785     square : true,
36786     
36787     getAutoCreate : function()
36788     {
36789         var cls = 'roo-brick';
36790         
36791         if(this.href.length){
36792             cls += ' roo-brick-link';
36793         }
36794         
36795         if(this.bgimage.length){
36796             cls += ' roo-brick-image';
36797         }
36798         
36799         if(!this.html.length && !this.bgimage.length){
36800             cls += ' roo-brick-center-title';
36801         }
36802         
36803         if(!this.html.length && this.bgimage.length){
36804             cls += ' roo-brick-bottom-title';
36805         }
36806         
36807         if(this.cls){
36808             cls += ' ' + this.cls;
36809         }
36810         
36811         var cfg = {
36812             tag: (this.href.length) ? 'a' : 'div',
36813             cls: cls,
36814             cn: [
36815                 {
36816                     tag: 'div',
36817                     cls: 'roo-brick-paragraph',
36818                     cn: []
36819                 }
36820             ]
36821         };
36822         
36823         if(this.href.length){
36824             cfg.href = this.href;
36825         }
36826         
36827         var cn = cfg.cn[0].cn;
36828         
36829         if(this.title.length){
36830             cn.push({
36831                 tag: 'h4',
36832                 cls: 'roo-brick-title',
36833                 html: this.title
36834             });
36835         }
36836         
36837         if(this.html.length){
36838             cn.push({
36839                 tag: 'p',
36840                 cls: 'roo-brick-text',
36841                 html: this.html
36842             });
36843         } else {
36844             cn.cls += ' hide';
36845         }
36846         
36847         if(this.bgimage.length){
36848             cfg.cn.push({
36849                 tag: 'img',
36850                 cls: 'roo-brick-image-view',
36851                 src: this.bgimage
36852             });
36853         }
36854         
36855         return cfg;
36856     },
36857     
36858     initEvents: function() 
36859     {
36860         if(this.title.length || this.html.length){
36861             this.el.on('mouseenter'  ,this.enter, this);
36862             this.el.on('mouseleave', this.leave, this);
36863         }
36864         
36865         Roo.EventManager.onWindowResize(this.resize, this); 
36866         
36867         if(this.bgimage.length){
36868             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36869             this.imageEl.on('load', this.onImageLoad, this);
36870             return;
36871         }
36872         
36873         this.resize();
36874     },
36875     
36876     onImageLoad : function()
36877     {
36878         this.resize();
36879     },
36880     
36881     resize : function()
36882     {
36883         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36884         
36885         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36886         
36887         if(this.bgimage.length){
36888             var image = this.el.select('.roo-brick-image-view', true).first();
36889             
36890             image.setWidth(paragraph.getWidth());
36891             
36892             if(this.square){
36893                 image.setHeight(paragraph.getWidth());
36894             }
36895             
36896             this.el.setHeight(image.getHeight());
36897             paragraph.setHeight(image.getHeight());
36898             
36899         }
36900         
36901     },
36902     
36903     enter: function(e, el)
36904     {
36905         e.preventDefault();
36906         
36907         if(this.bgimage.length){
36908             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36909             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36910         }
36911     },
36912     
36913     leave: function(e, el)
36914     {
36915         e.preventDefault();
36916         
36917         if(this.bgimage.length){
36918             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36919             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36920         }
36921     }
36922     
36923 });
36924
36925  
36926
36927  /*
36928  * - LGPL
36929  *
36930  * Number field 
36931  */
36932
36933 /**
36934  * @class Roo.bootstrap.NumberField
36935  * @extends Roo.bootstrap.Input
36936  * Bootstrap NumberField class
36937  * 
36938  * 
36939  * 
36940  * 
36941  * @constructor
36942  * Create a new NumberField
36943  * @param {Object} config The config object
36944  */
36945
36946 Roo.bootstrap.NumberField = function(config){
36947     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36948 };
36949
36950 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36951     
36952     /**
36953      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36954      */
36955     allowDecimals : true,
36956     /**
36957      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36958      */
36959     decimalSeparator : ".",
36960     /**
36961      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36962      */
36963     decimalPrecision : 2,
36964     /**
36965      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36966      */
36967     allowNegative : true,
36968     
36969     /**
36970      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36971      */
36972     allowZero: true,
36973     /**
36974      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36975      */
36976     minValue : Number.NEGATIVE_INFINITY,
36977     /**
36978      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36979      */
36980     maxValue : Number.MAX_VALUE,
36981     /**
36982      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36983      */
36984     minText : "The minimum value for this field is {0}",
36985     /**
36986      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36987      */
36988     maxText : "The maximum value for this field is {0}",
36989     /**
36990      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36991      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36992      */
36993     nanText : "{0} is not a valid number",
36994     /**
36995      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36996      */
36997     thousandsDelimiter : false,
36998     /**
36999      * @cfg {String} valueAlign alignment of value
37000      */
37001     valueAlign : "left",
37002
37003     getAutoCreate : function()
37004     {
37005         var hiddenInput = {
37006             tag: 'input',
37007             type: 'hidden',
37008             id: Roo.id(),
37009             cls: 'hidden-number-input'
37010         };
37011         
37012         if (this.name) {
37013             hiddenInput.name = this.name;
37014         }
37015         
37016         this.name = '';
37017         
37018         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37019         
37020         this.name = hiddenInput.name;
37021         
37022         if(cfg.cn.length > 0) {
37023             cfg.cn.push(hiddenInput);
37024         }
37025         
37026         return cfg;
37027     },
37028
37029     // private
37030     initEvents : function()
37031     {   
37032         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37033         
37034         var allowed = "0123456789";
37035         
37036         if(this.allowDecimals){
37037             allowed += this.decimalSeparator;
37038         }
37039         
37040         if(this.allowNegative){
37041             allowed += "-";
37042         }
37043         
37044         if(this.thousandsDelimiter) {
37045             allowed += ",";
37046         }
37047         
37048         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37049         
37050         var keyPress = function(e){
37051             
37052             var k = e.getKey();
37053             
37054             var c = e.getCharCode();
37055             
37056             if(
37057                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37058                     allowed.indexOf(String.fromCharCode(c)) === -1
37059             ){
37060                 e.stopEvent();
37061                 return;
37062             }
37063             
37064             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37065                 return;
37066             }
37067             
37068             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37069                 e.stopEvent();
37070             }
37071         };
37072         
37073         this.el.on("keypress", keyPress, this);
37074     },
37075     
37076     validateValue : function(value)
37077     {
37078         
37079         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37080             return false;
37081         }
37082         
37083         var num = this.parseValue(value);
37084         
37085         if(isNaN(num)){
37086             this.markInvalid(String.format(this.nanText, value));
37087             return false;
37088         }
37089         
37090         if(num < this.minValue){
37091             this.markInvalid(String.format(this.minText, this.minValue));
37092             return false;
37093         }
37094         
37095         if(num > this.maxValue){
37096             this.markInvalid(String.format(this.maxText, this.maxValue));
37097             return false;
37098         }
37099         
37100         return true;
37101     },
37102
37103     getValue : function()
37104     {
37105         var v = this.hiddenEl().getValue();
37106         
37107         return this.fixPrecision(this.parseValue(v));
37108     },
37109
37110     parseValue : function(value)
37111     {
37112         if(this.thousandsDelimiter) {
37113             value += "";
37114             r = new RegExp(",", "g");
37115             value = value.replace(r, "");
37116         }
37117         
37118         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37119         return isNaN(value) ? '' : value;
37120     },
37121
37122     fixPrecision : function(value)
37123     {
37124         if(this.thousandsDelimiter) {
37125             value += "";
37126             r = new RegExp(",", "g");
37127             value = value.replace(r, "");
37128         }
37129         
37130         var nan = isNaN(value);
37131         
37132         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37133             return nan ? '' : value;
37134         }
37135         return parseFloat(value).toFixed(this.decimalPrecision);
37136     },
37137
37138     setValue : function(v)
37139     {
37140         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37141         
37142         this.value = v;
37143         
37144         if(this.rendered){
37145             
37146             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37147             
37148             this.inputEl().dom.value = (v == '') ? '' :
37149                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37150             
37151             if(!this.allowZero && v === '0') {
37152                 this.hiddenEl().dom.value = '';
37153                 this.inputEl().dom.value = '';
37154             }
37155             
37156             this.validate();
37157         }
37158     },
37159
37160     decimalPrecisionFcn : function(v)
37161     {
37162         return Math.floor(v);
37163     },
37164
37165     beforeBlur : function()
37166     {
37167         var v = this.parseValue(this.getRawValue());
37168         
37169         if(v || v === 0 || v === ''){
37170             this.setValue(v);
37171         }
37172     },
37173     
37174     hiddenEl : function()
37175     {
37176         return this.el.select('input.hidden-number-input',true).first();
37177     }
37178     
37179 });
37180
37181  
37182
37183 /*
37184 * Licence: LGPL
37185 */
37186
37187 /**
37188  * @class Roo.bootstrap.DocumentSlider
37189  * @extends Roo.bootstrap.Component
37190  * Bootstrap DocumentSlider class
37191  * 
37192  * @constructor
37193  * Create a new DocumentViewer
37194  * @param {Object} config The config object
37195  */
37196
37197 Roo.bootstrap.DocumentSlider = function(config){
37198     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37199     
37200     this.files = [];
37201     
37202     this.addEvents({
37203         /**
37204          * @event initial
37205          * Fire after initEvent
37206          * @param {Roo.bootstrap.DocumentSlider} this
37207          */
37208         "initial" : true,
37209         /**
37210          * @event update
37211          * Fire after update
37212          * @param {Roo.bootstrap.DocumentSlider} this
37213          */
37214         "update" : true,
37215         /**
37216          * @event click
37217          * Fire after click
37218          * @param {Roo.bootstrap.DocumentSlider} this
37219          */
37220         "click" : true
37221     });
37222 };
37223
37224 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37225     
37226     files : false,
37227     
37228     indicator : 0,
37229     
37230     getAutoCreate : function()
37231     {
37232         var cfg = {
37233             tag : 'div',
37234             cls : 'roo-document-slider',
37235             cn : [
37236                 {
37237                     tag : 'div',
37238                     cls : 'roo-document-slider-header',
37239                     cn : [
37240                         {
37241                             tag : 'div',
37242                             cls : 'roo-document-slider-header-title'
37243                         }
37244                     ]
37245                 },
37246                 {
37247                     tag : 'div',
37248                     cls : 'roo-document-slider-body',
37249                     cn : [
37250                         {
37251                             tag : 'div',
37252                             cls : 'roo-document-slider-prev',
37253                             cn : [
37254                                 {
37255                                     tag : 'i',
37256                                     cls : 'fa fa-chevron-left'
37257                                 }
37258                             ]
37259                         },
37260                         {
37261                             tag : 'div',
37262                             cls : 'roo-document-slider-thumb',
37263                             cn : [
37264                                 {
37265                                     tag : 'img',
37266                                     cls : 'roo-document-slider-image'
37267                                 }
37268                             ]
37269                         },
37270                         {
37271                             tag : 'div',
37272                             cls : 'roo-document-slider-next',
37273                             cn : [
37274                                 {
37275                                     tag : 'i',
37276                                     cls : 'fa fa-chevron-right'
37277                                 }
37278                             ]
37279                         }
37280                     ]
37281                 }
37282             ]
37283         };
37284         
37285         return cfg;
37286     },
37287     
37288     initEvents : function()
37289     {
37290         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37291         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37292         
37293         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37294         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37295         
37296         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37297         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37298         
37299         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37300         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37301         
37302         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37303         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37304         
37305         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37306         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37307         
37308         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37309         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37310         
37311         this.thumbEl.on('click', this.onClick, this);
37312         
37313         this.prevIndicator.on('click', this.prev, this);
37314         
37315         this.nextIndicator.on('click', this.next, this);
37316         
37317     },
37318     
37319     initial : function()
37320     {
37321         if(this.files.length){
37322             this.indicator = 1;
37323             this.update()
37324         }
37325         
37326         this.fireEvent('initial', this);
37327     },
37328     
37329     update : function()
37330     {
37331         this.imageEl.attr('src', this.files[this.indicator - 1]);
37332         
37333         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37334         
37335         this.prevIndicator.show();
37336         
37337         if(this.indicator == 1){
37338             this.prevIndicator.hide();
37339         }
37340         
37341         this.nextIndicator.show();
37342         
37343         if(this.indicator == this.files.length){
37344             this.nextIndicator.hide();
37345         }
37346         
37347         this.thumbEl.scrollTo('top');
37348         
37349         this.fireEvent('update', this);
37350     },
37351     
37352     onClick : function(e)
37353     {
37354         e.preventDefault();
37355         
37356         this.fireEvent('click', this);
37357     },
37358     
37359     prev : function(e)
37360     {
37361         e.preventDefault();
37362         
37363         this.indicator = Math.max(1, this.indicator - 1);
37364         
37365         this.update();
37366     },
37367     
37368     next : function(e)
37369     {
37370         e.preventDefault();
37371         
37372         this.indicator = Math.min(this.files.length, this.indicator + 1);
37373         
37374         this.update();
37375     }
37376 });
37377 /*
37378  * - LGPL
37379  *
37380  * RadioSet
37381  *
37382  *
37383  */
37384
37385 /**
37386  * @class Roo.bootstrap.RadioSet
37387  * @extends Roo.bootstrap.Input
37388  * Bootstrap RadioSet class
37389  * @cfg {String} indicatorpos (left|right) default left
37390  * @cfg {Boolean} inline (true|false) inline the element (default true)
37391  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37392  * @constructor
37393  * Create a new RadioSet
37394  * @param {Object} config The config object
37395  */
37396
37397 Roo.bootstrap.RadioSet = function(config){
37398     
37399     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37400     
37401     this.radioes = [];
37402     
37403     Roo.bootstrap.RadioSet.register(this);
37404     
37405     this.addEvents({
37406         /**
37407         * @event check
37408         * Fires when the element is checked or unchecked.
37409         * @param {Roo.bootstrap.RadioSet} this This radio
37410         * @param {Roo.bootstrap.Radio} item The checked item
37411         */
37412        check : true,
37413        /**
37414         * @event click
37415         * Fires when the element is click.
37416         * @param {Roo.bootstrap.RadioSet} this This radio set
37417         * @param {Roo.bootstrap.Radio} item The checked item
37418         * @param {Roo.EventObject} e The event object
37419         */
37420        click : true
37421     });
37422     
37423 };
37424
37425 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37426
37427     radioes : false,
37428     
37429     inline : true,
37430     
37431     weight : '',
37432     
37433     indicatorpos : 'left',
37434     
37435     getAutoCreate : function()
37436     {
37437         var label = {
37438             tag : 'label',
37439             cls : 'roo-radio-set-label',
37440             cn : [
37441                 {
37442                     tag : 'span',
37443                     html : this.fieldLabel
37444                 }
37445             ]
37446         };
37447         if (Roo.bootstrap.version == 3) {
37448             
37449             
37450             if(this.indicatorpos == 'left'){
37451                 label.cn.unshift({
37452                     tag : 'i',
37453                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37454                     tooltip : 'This field is required'
37455                 });
37456             } else {
37457                 label.cn.push({
37458                     tag : 'i',
37459                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37460                     tooltip : 'This field is required'
37461                 });
37462             }
37463         }
37464         var items = {
37465             tag : 'div',
37466             cls : 'roo-radio-set-items'
37467         };
37468         
37469         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37470         
37471         if (align === 'left' && this.fieldLabel.length) {
37472             
37473             items = {
37474                 cls : "roo-radio-set-right", 
37475                 cn: [
37476                     items
37477                 ]
37478             };
37479             
37480             if(this.labelWidth > 12){
37481                 label.style = "width: " + this.labelWidth + 'px';
37482             }
37483             
37484             if(this.labelWidth < 13 && this.labelmd == 0){
37485                 this.labelmd = this.labelWidth;
37486             }
37487             
37488             if(this.labellg > 0){
37489                 label.cls += ' col-lg-' + this.labellg;
37490                 items.cls += ' col-lg-' + (12 - this.labellg);
37491             }
37492             
37493             if(this.labelmd > 0){
37494                 label.cls += ' col-md-' + this.labelmd;
37495                 items.cls += ' col-md-' + (12 - this.labelmd);
37496             }
37497             
37498             if(this.labelsm > 0){
37499                 label.cls += ' col-sm-' + this.labelsm;
37500                 items.cls += ' col-sm-' + (12 - this.labelsm);
37501             }
37502             
37503             if(this.labelxs > 0){
37504                 label.cls += ' col-xs-' + this.labelxs;
37505                 items.cls += ' col-xs-' + (12 - this.labelxs);
37506             }
37507         }
37508         
37509         var cfg = {
37510             tag : 'div',
37511             cls : 'roo-radio-set',
37512             cn : [
37513                 {
37514                     tag : 'input',
37515                     cls : 'roo-radio-set-input',
37516                     type : 'hidden',
37517                     name : this.name,
37518                     value : this.value ? this.value :  ''
37519                 },
37520                 label,
37521                 items
37522             ]
37523         };
37524         
37525         if(this.weight.length){
37526             cfg.cls += ' roo-radio-' + this.weight;
37527         }
37528         
37529         if(this.inline) {
37530             cfg.cls += ' roo-radio-set-inline';
37531         }
37532         
37533         var settings=this;
37534         ['xs','sm','md','lg'].map(function(size){
37535             if (settings[size]) {
37536                 cfg.cls += ' col-' + size + '-' + settings[size];
37537             }
37538         });
37539         
37540         return cfg;
37541         
37542     },
37543
37544     initEvents : function()
37545     {
37546         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37547         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37548         
37549         if(!this.fieldLabel.length){
37550             this.labelEl.hide();
37551         }
37552         
37553         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37554         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37555         
37556         this.indicator = this.indicatorEl();
37557         
37558         if(this.indicator){
37559             this.indicator.addClass('invisible');
37560         }
37561         
37562         this.originalValue = this.getValue();
37563         
37564     },
37565     
37566     inputEl: function ()
37567     {
37568         return this.el.select('.roo-radio-set-input', true).first();
37569     },
37570     
37571     getChildContainer : function()
37572     {
37573         return this.itemsEl;
37574     },
37575     
37576     register : function(item)
37577     {
37578         this.radioes.push(item);
37579         
37580     },
37581     
37582     validate : function()
37583     {   
37584         if(this.getVisibilityEl().hasClass('hidden')){
37585             return true;
37586         }
37587         
37588         var valid = false;
37589         
37590         Roo.each(this.radioes, function(i){
37591             if(!i.checked){
37592                 return;
37593             }
37594             
37595             valid = true;
37596             return false;
37597         });
37598         
37599         if(this.allowBlank) {
37600             return true;
37601         }
37602         
37603         if(this.disabled || valid){
37604             this.markValid();
37605             return true;
37606         }
37607         
37608         this.markInvalid();
37609         return false;
37610         
37611     },
37612     
37613     markValid : function()
37614     {
37615         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37616             this.indicatorEl().removeClass('visible');
37617             this.indicatorEl().addClass('invisible');
37618         }
37619         
37620         
37621         if (Roo.bootstrap.version == 3) {
37622             this.el.removeClass([this.invalidClass, this.validClass]);
37623             this.el.addClass(this.validClass);
37624         } else {
37625             this.el.removeClass(['is-invalid','is-valid']);
37626             this.el.addClass(['is-valid']);
37627         }
37628         this.fireEvent('valid', this);
37629     },
37630     
37631     markInvalid : function(msg)
37632     {
37633         if(this.allowBlank || this.disabled){
37634             return;
37635         }
37636         
37637         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37638             this.indicatorEl().removeClass('invisible');
37639             this.indicatorEl().addClass('visible');
37640         }
37641         if (Roo.bootstrap.version == 3) {
37642             this.el.removeClass([this.invalidClass, this.validClass]);
37643             this.el.addClass(this.invalidClass);
37644         } else {
37645             this.el.removeClass(['is-invalid','is-valid']);
37646             this.el.addClass(['is-invalid']);
37647         }
37648         
37649         this.fireEvent('invalid', this, msg);
37650         
37651     },
37652     
37653     setValue : function(v, suppressEvent)
37654     {   
37655         if(this.value === v){
37656             return;
37657         }
37658         
37659         this.value = v;
37660         
37661         if(this.rendered){
37662             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37663         }
37664         
37665         Roo.each(this.radioes, function(i){
37666             i.checked = false;
37667             i.el.removeClass('checked');
37668         });
37669         
37670         Roo.each(this.radioes, function(i){
37671             
37672             if(i.value === v || i.value.toString() === v.toString()){
37673                 i.checked = true;
37674                 i.el.addClass('checked');
37675                 
37676                 if(suppressEvent !== true){
37677                     this.fireEvent('check', this, i);
37678                 }
37679                 
37680                 return false;
37681             }
37682             
37683         }, this);
37684         
37685         this.validate();
37686     },
37687     
37688     clearInvalid : function(){
37689         
37690         if(!this.el || this.preventMark){
37691             return;
37692         }
37693         
37694         this.el.removeClass([this.invalidClass]);
37695         
37696         this.fireEvent('valid', this);
37697     }
37698     
37699 });
37700
37701 Roo.apply(Roo.bootstrap.RadioSet, {
37702     
37703     groups: {},
37704     
37705     register : function(set)
37706     {
37707         this.groups[set.name] = set;
37708     },
37709     
37710     get: function(name) 
37711     {
37712         if (typeof(this.groups[name]) == 'undefined') {
37713             return false;
37714         }
37715         
37716         return this.groups[name] ;
37717     }
37718     
37719 });
37720 /*
37721  * Based on:
37722  * Ext JS Library 1.1.1
37723  * Copyright(c) 2006-2007, Ext JS, LLC.
37724  *
37725  * Originally Released Under LGPL - original licence link has changed is not relivant.
37726  *
37727  * Fork - LGPL
37728  * <script type="text/javascript">
37729  */
37730
37731
37732 /**
37733  * @class Roo.bootstrap.SplitBar
37734  * @extends Roo.util.Observable
37735  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37736  * <br><br>
37737  * Usage:
37738  * <pre><code>
37739 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37740                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37741 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37742 split.minSize = 100;
37743 split.maxSize = 600;
37744 split.animate = true;
37745 split.on('moved', splitterMoved);
37746 </code></pre>
37747  * @constructor
37748  * Create a new SplitBar
37749  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37750  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37751  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37752  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37753                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37754                         position of the SplitBar).
37755  */
37756 Roo.bootstrap.SplitBar = function(cfg){
37757     
37758     /** @private */
37759     
37760     //{
37761     //  dragElement : elm
37762     //  resizingElement: el,
37763         // optional..
37764     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37765     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37766         // existingProxy ???
37767     //}
37768     
37769     this.el = Roo.get(cfg.dragElement, true);
37770     this.el.dom.unselectable = "on";
37771     /** @private */
37772     this.resizingEl = Roo.get(cfg.resizingElement, true);
37773
37774     /**
37775      * @private
37776      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37777      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37778      * @type Number
37779      */
37780     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37781     
37782     /**
37783      * The minimum size of the resizing element. (Defaults to 0)
37784      * @type Number
37785      */
37786     this.minSize = 0;
37787     
37788     /**
37789      * The maximum size of the resizing element. (Defaults to 2000)
37790      * @type Number
37791      */
37792     this.maxSize = 2000;
37793     
37794     /**
37795      * Whether to animate the transition to the new size
37796      * @type Boolean
37797      */
37798     this.animate = false;
37799     
37800     /**
37801      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37802      * @type Boolean
37803      */
37804     this.useShim = false;
37805     
37806     /** @private */
37807     this.shim = null;
37808     
37809     if(!cfg.existingProxy){
37810         /** @private */
37811         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37812     }else{
37813         this.proxy = Roo.get(cfg.existingProxy).dom;
37814     }
37815     /** @private */
37816     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37817     
37818     /** @private */
37819     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37820     
37821     /** @private */
37822     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37823     
37824     /** @private */
37825     this.dragSpecs = {};
37826     
37827     /**
37828      * @private The adapter to use to positon and resize elements
37829      */
37830     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37831     this.adapter.init(this);
37832     
37833     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37834         /** @private */
37835         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37836         this.el.addClass("roo-splitbar-h");
37837     }else{
37838         /** @private */
37839         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37840         this.el.addClass("roo-splitbar-v");
37841     }
37842     
37843     this.addEvents({
37844         /**
37845          * @event resize
37846          * Fires when the splitter is moved (alias for {@link #event-moved})
37847          * @param {Roo.bootstrap.SplitBar} this
37848          * @param {Number} newSize the new width or height
37849          */
37850         "resize" : true,
37851         /**
37852          * @event moved
37853          * Fires when the splitter is moved
37854          * @param {Roo.bootstrap.SplitBar} this
37855          * @param {Number} newSize the new width or height
37856          */
37857         "moved" : true,
37858         /**
37859          * @event beforeresize
37860          * Fires before the splitter is dragged
37861          * @param {Roo.bootstrap.SplitBar} this
37862          */
37863         "beforeresize" : true,
37864
37865         "beforeapply" : true
37866     });
37867
37868     Roo.util.Observable.call(this);
37869 };
37870
37871 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37872     onStartProxyDrag : function(x, y){
37873         this.fireEvent("beforeresize", this);
37874         if(!this.overlay){
37875             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37876             o.unselectable();
37877             o.enableDisplayMode("block");
37878             // all splitbars share the same overlay
37879             Roo.bootstrap.SplitBar.prototype.overlay = o;
37880         }
37881         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37882         this.overlay.show();
37883         Roo.get(this.proxy).setDisplayed("block");
37884         var size = this.adapter.getElementSize(this);
37885         this.activeMinSize = this.getMinimumSize();;
37886         this.activeMaxSize = this.getMaximumSize();;
37887         var c1 = size - this.activeMinSize;
37888         var c2 = Math.max(this.activeMaxSize - size, 0);
37889         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37890             this.dd.resetConstraints();
37891             this.dd.setXConstraint(
37892                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37893                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37894             );
37895             this.dd.setYConstraint(0, 0);
37896         }else{
37897             this.dd.resetConstraints();
37898             this.dd.setXConstraint(0, 0);
37899             this.dd.setYConstraint(
37900                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37901                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37902             );
37903          }
37904         this.dragSpecs.startSize = size;
37905         this.dragSpecs.startPoint = [x, y];
37906         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37907     },
37908     
37909     /** 
37910      * @private Called after the drag operation by the DDProxy
37911      */
37912     onEndProxyDrag : function(e){
37913         Roo.get(this.proxy).setDisplayed(false);
37914         var endPoint = Roo.lib.Event.getXY(e);
37915         if(this.overlay){
37916             this.overlay.hide();
37917         }
37918         var newSize;
37919         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37920             newSize = this.dragSpecs.startSize + 
37921                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37922                     endPoint[0] - this.dragSpecs.startPoint[0] :
37923                     this.dragSpecs.startPoint[0] - endPoint[0]
37924                 );
37925         }else{
37926             newSize = this.dragSpecs.startSize + 
37927                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37928                     endPoint[1] - this.dragSpecs.startPoint[1] :
37929                     this.dragSpecs.startPoint[1] - endPoint[1]
37930                 );
37931         }
37932         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37933         if(newSize != this.dragSpecs.startSize){
37934             if(this.fireEvent('beforeapply', this, newSize) !== false){
37935                 this.adapter.setElementSize(this, newSize);
37936                 this.fireEvent("moved", this, newSize);
37937                 this.fireEvent("resize", this, newSize);
37938             }
37939         }
37940     },
37941     
37942     /**
37943      * Get the adapter this SplitBar uses
37944      * @return The adapter object
37945      */
37946     getAdapter : function(){
37947         return this.adapter;
37948     },
37949     
37950     /**
37951      * Set the adapter this SplitBar uses
37952      * @param {Object} adapter A SplitBar adapter object
37953      */
37954     setAdapter : function(adapter){
37955         this.adapter = adapter;
37956         this.adapter.init(this);
37957     },
37958     
37959     /**
37960      * Gets the minimum size for the resizing element
37961      * @return {Number} The minimum size
37962      */
37963     getMinimumSize : function(){
37964         return this.minSize;
37965     },
37966     
37967     /**
37968      * Sets the minimum size for the resizing element
37969      * @param {Number} minSize The minimum size
37970      */
37971     setMinimumSize : function(minSize){
37972         this.minSize = minSize;
37973     },
37974     
37975     /**
37976      * Gets the maximum size for the resizing element
37977      * @return {Number} The maximum size
37978      */
37979     getMaximumSize : function(){
37980         return this.maxSize;
37981     },
37982     
37983     /**
37984      * Sets the maximum size for the resizing element
37985      * @param {Number} maxSize The maximum size
37986      */
37987     setMaximumSize : function(maxSize){
37988         this.maxSize = maxSize;
37989     },
37990     
37991     /**
37992      * Sets the initialize size for the resizing element
37993      * @param {Number} size The initial size
37994      */
37995     setCurrentSize : function(size){
37996         var oldAnimate = this.animate;
37997         this.animate = false;
37998         this.adapter.setElementSize(this, size);
37999         this.animate = oldAnimate;
38000     },
38001     
38002     /**
38003      * Destroy this splitbar. 
38004      * @param {Boolean} removeEl True to remove the element
38005      */
38006     destroy : function(removeEl){
38007         if(this.shim){
38008             this.shim.remove();
38009         }
38010         this.dd.unreg();
38011         this.proxy.parentNode.removeChild(this.proxy);
38012         if(removeEl){
38013             this.el.remove();
38014         }
38015     }
38016 });
38017
38018 /**
38019  * @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.
38020  */
38021 Roo.bootstrap.SplitBar.createProxy = function(dir){
38022     var proxy = new Roo.Element(document.createElement("div"));
38023     proxy.unselectable();
38024     var cls = 'roo-splitbar-proxy';
38025     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38026     document.body.appendChild(proxy.dom);
38027     return proxy.dom;
38028 };
38029
38030 /** 
38031  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38032  * Default Adapter. It assumes the splitter and resizing element are not positioned
38033  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38034  */
38035 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38036 };
38037
38038 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38039     // do nothing for now
38040     init : function(s){
38041     
38042     },
38043     /**
38044      * Called before drag operations to get the current size of the resizing element. 
38045      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38046      */
38047      getElementSize : function(s){
38048         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38049             return s.resizingEl.getWidth();
38050         }else{
38051             return s.resizingEl.getHeight();
38052         }
38053     },
38054     
38055     /**
38056      * Called after drag operations to set the size of the resizing element.
38057      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38058      * @param {Number} newSize The new size to set
38059      * @param {Function} onComplete A function to be invoked when resizing is complete
38060      */
38061     setElementSize : function(s, newSize, onComplete){
38062         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38063             if(!s.animate){
38064                 s.resizingEl.setWidth(newSize);
38065                 if(onComplete){
38066                     onComplete(s, newSize);
38067                 }
38068             }else{
38069                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38070             }
38071         }else{
38072             
38073             if(!s.animate){
38074                 s.resizingEl.setHeight(newSize);
38075                 if(onComplete){
38076                     onComplete(s, newSize);
38077                 }
38078             }else{
38079                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38080             }
38081         }
38082     }
38083 };
38084
38085 /** 
38086  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38087  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38088  * Adapter that  moves the splitter element to align with the resized sizing element. 
38089  * Used with an absolute positioned SplitBar.
38090  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38091  * document.body, make sure you assign an id to the body element.
38092  */
38093 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38094     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38095     this.container = Roo.get(container);
38096 };
38097
38098 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38099     init : function(s){
38100         this.basic.init(s);
38101     },
38102     
38103     getElementSize : function(s){
38104         return this.basic.getElementSize(s);
38105     },
38106     
38107     setElementSize : function(s, newSize, onComplete){
38108         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38109     },
38110     
38111     moveSplitter : function(s){
38112         var yes = Roo.bootstrap.SplitBar;
38113         switch(s.placement){
38114             case yes.LEFT:
38115                 s.el.setX(s.resizingEl.getRight());
38116                 break;
38117             case yes.RIGHT:
38118                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38119                 break;
38120             case yes.TOP:
38121                 s.el.setY(s.resizingEl.getBottom());
38122                 break;
38123             case yes.BOTTOM:
38124                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38125                 break;
38126         }
38127     }
38128 };
38129
38130 /**
38131  * Orientation constant - Create a vertical SplitBar
38132  * @static
38133  * @type Number
38134  */
38135 Roo.bootstrap.SplitBar.VERTICAL = 1;
38136
38137 /**
38138  * Orientation constant - Create a horizontal SplitBar
38139  * @static
38140  * @type Number
38141  */
38142 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38143
38144 /**
38145  * Placement constant - The resizing element is to the left of the splitter element
38146  * @static
38147  * @type Number
38148  */
38149 Roo.bootstrap.SplitBar.LEFT = 1;
38150
38151 /**
38152  * Placement constant - The resizing element is to the right of the splitter element
38153  * @static
38154  * @type Number
38155  */
38156 Roo.bootstrap.SplitBar.RIGHT = 2;
38157
38158 /**
38159  * Placement constant - The resizing element is positioned above the splitter element
38160  * @static
38161  * @type Number
38162  */
38163 Roo.bootstrap.SplitBar.TOP = 3;
38164
38165 /**
38166  * Placement constant - The resizing element is positioned under splitter element
38167  * @static
38168  * @type Number
38169  */
38170 Roo.bootstrap.SplitBar.BOTTOM = 4;
38171 Roo.namespace("Roo.bootstrap.layout");/*
38172  * Based on:
38173  * Ext JS Library 1.1.1
38174  * Copyright(c) 2006-2007, Ext JS, LLC.
38175  *
38176  * Originally Released Under LGPL - original licence link has changed is not relivant.
38177  *
38178  * Fork - LGPL
38179  * <script type="text/javascript">
38180  */
38181
38182 /**
38183  * @class Roo.bootstrap.layout.Manager
38184  * @extends Roo.bootstrap.Component
38185  * Base class for layout managers.
38186  */
38187 Roo.bootstrap.layout.Manager = function(config)
38188 {
38189     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38190
38191
38192
38193
38194
38195     /** false to disable window resize monitoring @type Boolean */
38196     this.monitorWindowResize = true;
38197     this.regions = {};
38198     this.addEvents({
38199         /**
38200          * @event layout
38201          * Fires when a layout is performed.
38202          * @param {Roo.LayoutManager} this
38203          */
38204         "layout" : true,
38205         /**
38206          * @event regionresized
38207          * Fires when the user resizes a region.
38208          * @param {Roo.LayoutRegion} region The resized region
38209          * @param {Number} newSize The new size (width for east/west, height for north/south)
38210          */
38211         "regionresized" : true,
38212         /**
38213          * @event regioncollapsed
38214          * Fires when a region is collapsed.
38215          * @param {Roo.LayoutRegion} region The collapsed region
38216          */
38217         "regioncollapsed" : true,
38218         /**
38219          * @event regionexpanded
38220          * Fires when a region is expanded.
38221          * @param {Roo.LayoutRegion} region The expanded region
38222          */
38223         "regionexpanded" : true
38224     });
38225     this.updating = false;
38226
38227     if (config.el) {
38228         this.el = Roo.get(config.el);
38229         this.initEvents();
38230     }
38231
38232 };
38233
38234 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38235
38236
38237     regions : null,
38238
38239     monitorWindowResize : true,
38240
38241
38242     updating : false,
38243
38244
38245     onRender : function(ct, position)
38246     {
38247         if(!this.el){
38248             this.el = Roo.get(ct);
38249             this.initEvents();
38250         }
38251         //this.fireEvent('render',this);
38252     },
38253
38254
38255     initEvents: function()
38256     {
38257
38258
38259         // ie scrollbar fix
38260         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38261             document.body.scroll = "no";
38262         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38263             this.el.position('relative');
38264         }
38265         this.id = this.el.id;
38266         this.el.addClass("roo-layout-container");
38267         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38268         if(this.el.dom != document.body ) {
38269             this.el.on('resize', this.layout,this);
38270             this.el.on('show', this.layout,this);
38271         }
38272
38273     },
38274
38275     /**
38276      * Returns true if this layout is currently being updated
38277      * @return {Boolean}
38278      */
38279     isUpdating : function(){
38280         return this.updating;
38281     },
38282
38283     /**
38284      * Suspend the LayoutManager from doing auto-layouts while
38285      * making multiple add or remove calls
38286      */
38287     beginUpdate : function(){
38288         this.updating = true;
38289     },
38290
38291     /**
38292      * Restore auto-layouts and optionally disable the manager from performing a layout
38293      * @param {Boolean} noLayout true to disable a layout update
38294      */
38295     endUpdate : function(noLayout){
38296         this.updating = false;
38297         if(!noLayout){
38298             this.layout();
38299         }
38300     },
38301
38302     layout: function(){
38303         // abstract...
38304     },
38305
38306     onRegionResized : function(region, newSize){
38307         this.fireEvent("regionresized", region, newSize);
38308         this.layout();
38309     },
38310
38311     onRegionCollapsed : function(region){
38312         this.fireEvent("regioncollapsed", region);
38313     },
38314
38315     onRegionExpanded : function(region){
38316         this.fireEvent("regionexpanded", region);
38317     },
38318
38319     /**
38320      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38321      * performs box-model adjustments.
38322      * @return {Object} The size as an object {width: (the width), height: (the height)}
38323      */
38324     getViewSize : function()
38325     {
38326         var size;
38327         if(this.el.dom != document.body){
38328             size = this.el.getSize();
38329         }else{
38330             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38331         }
38332         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38333         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38334         return size;
38335     },
38336
38337     /**
38338      * Returns the Element this layout is bound to.
38339      * @return {Roo.Element}
38340      */
38341     getEl : function(){
38342         return this.el;
38343     },
38344
38345     /**
38346      * Returns the specified region.
38347      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38348      * @return {Roo.LayoutRegion}
38349      */
38350     getRegion : function(target){
38351         return this.regions[target.toLowerCase()];
38352     },
38353
38354     onWindowResize : function(){
38355         if(this.monitorWindowResize){
38356             this.layout();
38357         }
38358     }
38359 });
38360 /*
38361  * Based on:
38362  * Ext JS Library 1.1.1
38363  * Copyright(c) 2006-2007, Ext JS, LLC.
38364  *
38365  * Originally Released Under LGPL - original licence link has changed is not relivant.
38366  *
38367  * Fork - LGPL
38368  * <script type="text/javascript">
38369  */
38370 /**
38371  * @class Roo.bootstrap.layout.Border
38372  * @extends Roo.bootstrap.layout.Manager
38373  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38374  * please see: examples/bootstrap/nested.html<br><br>
38375  
38376 <b>The container the layout is rendered into can be either the body element or any other element.
38377 If it is not the body element, the container needs to either be an absolute positioned element,
38378 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38379 the container size if it is not the body element.</b>
38380
38381 * @constructor
38382 * Create a new Border
38383 * @param {Object} config Configuration options
38384  */
38385 Roo.bootstrap.layout.Border = function(config){
38386     config = config || {};
38387     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38388     
38389     
38390     
38391     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38392         if(config[region]){
38393             config[region].region = region;
38394             this.addRegion(config[region]);
38395         }
38396     },this);
38397     
38398 };
38399
38400 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38401
38402 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38403     
38404     parent : false, // this might point to a 'nest' or a ???
38405     
38406     /**
38407      * Creates and adds a new region if it doesn't already exist.
38408      * @param {String} target The target region key (north, south, east, west or center).
38409      * @param {Object} config The regions config object
38410      * @return {BorderLayoutRegion} The new region
38411      */
38412     addRegion : function(config)
38413     {
38414         if(!this.regions[config.region]){
38415             var r = this.factory(config);
38416             this.bindRegion(r);
38417         }
38418         return this.regions[config.region];
38419     },
38420
38421     // private (kinda)
38422     bindRegion : function(r){
38423         this.regions[r.config.region] = r;
38424         
38425         r.on("visibilitychange",    this.layout, this);
38426         r.on("paneladded",          this.layout, this);
38427         r.on("panelremoved",        this.layout, this);
38428         r.on("invalidated",         this.layout, this);
38429         r.on("resized",             this.onRegionResized, this);
38430         r.on("collapsed",           this.onRegionCollapsed, this);
38431         r.on("expanded",            this.onRegionExpanded, this);
38432     },
38433
38434     /**
38435      * Performs a layout update.
38436      */
38437     layout : function()
38438     {
38439         if(this.updating) {
38440             return;
38441         }
38442         
38443         // render all the rebions if they have not been done alreayd?
38444         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38445             if(this.regions[region] && !this.regions[region].bodyEl){
38446                 this.regions[region].onRender(this.el)
38447             }
38448         },this);
38449         
38450         var size = this.getViewSize();
38451         var w = size.width;
38452         var h = size.height;
38453         var centerW = w;
38454         var centerH = h;
38455         var centerY = 0;
38456         var centerX = 0;
38457         //var x = 0, y = 0;
38458
38459         var rs = this.regions;
38460         var north = rs["north"];
38461         var south = rs["south"]; 
38462         var west = rs["west"];
38463         var east = rs["east"];
38464         var center = rs["center"];
38465         //if(this.hideOnLayout){ // not supported anymore
38466             //c.el.setStyle("display", "none");
38467         //}
38468         if(north && north.isVisible()){
38469             var b = north.getBox();
38470             var m = north.getMargins();
38471             b.width = w - (m.left+m.right);
38472             b.x = m.left;
38473             b.y = m.top;
38474             centerY = b.height + b.y + m.bottom;
38475             centerH -= centerY;
38476             north.updateBox(this.safeBox(b));
38477         }
38478         if(south && south.isVisible()){
38479             var b = south.getBox();
38480             var m = south.getMargins();
38481             b.width = w - (m.left+m.right);
38482             b.x = m.left;
38483             var totalHeight = (b.height + m.top + m.bottom);
38484             b.y = h - totalHeight + m.top;
38485             centerH -= totalHeight;
38486             south.updateBox(this.safeBox(b));
38487         }
38488         if(west && west.isVisible()){
38489             var b = west.getBox();
38490             var m = west.getMargins();
38491             b.height = centerH - (m.top+m.bottom);
38492             b.x = m.left;
38493             b.y = centerY + m.top;
38494             var totalWidth = (b.width + m.left + m.right);
38495             centerX += totalWidth;
38496             centerW -= totalWidth;
38497             west.updateBox(this.safeBox(b));
38498         }
38499         if(east && east.isVisible()){
38500             var b = east.getBox();
38501             var m = east.getMargins();
38502             b.height = centerH - (m.top+m.bottom);
38503             var totalWidth = (b.width + m.left + m.right);
38504             b.x = w - totalWidth + m.left;
38505             b.y = centerY + m.top;
38506             centerW -= totalWidth;
38507             east.updateBox(this.safeBox(b));
38508         }
38509         if(center){
38510             var m = center.getMargins();
38511             var centerBox = {
38512                 x: centerX + m.left,
38513                 y: centerY + m.top,
38514                 width: centerW - (m.left+m.right),
38515                 height: centerH - (m.top+m.bottom)
38516             };
38517             //if(this.hideOnLayout){
38518                 //center.el.setStyle("display", "block");
38519             //}
38520             center.updateBox(this.safeBox(centerBox));
38521         }
38522         this.el.repaint();
38523         this.fireEvent("layout", this);
38524     },
38525
38526     // private
38527     safeBox : function(box){
38528         box.width = Math.max(0, box.width);
38529         box.height = Math.max(0, box.height);
38530         return box;
38531     },
38532
38533     /**
38534      * Adds a ContentPanel (or subclass) to this layout.
38535      * @param {String} target The target region key (north, south, east, west or center).
38536      * @param {Roo.ContentPanel} panel The panel to add
38537      * @return {Roo.ContentPanel} The added panel
38538      */
38539     add : function(target, panel){
38540          
38541         target = target.toLowerCase();
38542         return this.regions[target].add(panel);
38543     },
38544
38545     /**
38546      * Remove a ContentPanel (or subclass) to this layout.
38547      * @param {String} target The target region key (north, south, east, west or center).
38548      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38549      * @return {Roo.ContentPanel} The removed panel
38550      */
38551     remove : function(target, panel){
38552         target = target.toLowerCase();
38553         return this.regions[target].remove(panel);
38554     },
38555
38556     /**
38557      * Searches all regions for a panel with the specified id
38558      * @param {String} panelId
38559      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38560      */
38561     findPanel : function(panelId){
38562         var rs = this.regions;
38563         for(var target in rs){
38564             if(typeof rs[target] != "function"){
38565                 var p = rs[target].getPanel(panelId);
38566                 if(p){
38567                     return p;
38568                 }
38569             }
38570         }
38571         return null;
38572     },
38573
38574     /**
38575      * Searches all regions for a panel with the specified id and activates (shows) it.
38576      * @param {String/ContentPanel} panelId The panels id or the panel itself
38577      * @return {Roo.ContentPanel} The shown panel or null
38578      */
38579     showPanel : function(panelId) {
38580       var rs = this.regions;
38581       for(var target in rs){
38582          var r = rs[target];
38583          if(typeof r != "function"){
38584             if(r.hasPanel(panelId)){
38585                return r.showPanel(panelId);
38586             }
38587          }
38588       }
38589       return null;
38590    },
38591
38592    /**
38593      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38594      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38595      */
38596    /*
38597     restoreState : function(provider){
38598         if(!provider){
38599             provider = Roo.state.Manager;
38600         }
38601         var sm = new Roo.LayoutStateManager();
38602         sm.init(this, provider);
38603     },
38604 */
38605  
38606  
38607     /**
38608      * Adds a xtype elements to the layout.
38609      * <pre><code>
38610
38611 layout.addxtype({
38612        xtype : 'ContentPanel',
38613        region: 'west',
38614        items: [ .... ]
38615    }
38616 );
38617
38618 layout.addxtype({
38619         xtype : 'NestedLayoutPanel',
38620         region: 'west',
38621         layout: {
38622            center: { },
38623            west: { }   
38624         },
38625         items : [ ... list of content panels or nested layout panels.. ]
38626    }
38627 );
38628 </code></pre>
38629      * @param {Object} cfg Xtype definition of item to add.
38630      */
38631     addxtype : function(cfg)
38632     {
38633         // basically accepts a pannel...
38634         // can accept a layout region..!?!?
38635         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38636         
38637         
38638         // theory?  children can only be panels??
38639         
38640         //if (!cfg.xtype.match(/Panel$/)) {
38641         //    return false;
38642         //}
38643         var ret = false;
38644         
38645         if (typeof(cfg.region) == 'undefined') {
38646             Roo.log("Failed to add Panel, region was not set");
38647             Roo.log(cfg);
38648             return false;
38649         }
38650         var region = cfg.region;
38651         delete cfg.region;
38652         
38653           
38654         var xitems = [];
38655         if (cfg.items) {
38656             xitems = cfg.items;
38657             delete cfg.items;
38658         }
38659         var nb = false;
38660         
38661         if ( region == 'center') {
38662             Roo.log("Center: " + cfg.title);
38663         }
38664         
38665         
38666         switch(cfg.xtype) 
38667         {
38668             case 'Content':  // ContentPanel (el, cfg)
38669             case 'Scroll':  // ContentPanel (el, cfg)
38670             case 'View': 
38671                 cfg.autoCreate = cfg.autoCreate || true;
38672                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38673                 //} else {
38674                 //    var el = this.el.createChild();
38675                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38676                 //}
38677                 
38678                 this.add(region, ret);
38679                 break;
38680             
38681             /*
38682             case 'TreePanel': // our new panel!
38683                 cfg.el = this.el.createChild();
38684                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38685                 this.add(region, ret);
38686                 break;
38687             */
38688             
38689             case 'Nest': 
38690                 // create a new Layout (which is  a Border Layout...
38691                 
38692                 var clayout = cfg.layout;
38693                 clayout.el  = this.el.createChild();
38694                 clayout.items   = clayout.items  || [];
38695                 
38696                 delete cfg.layout;
38697                 
38698                 // replace this exitems with the clayout ones..
38699                 xitems = clayout.items;
38700                  
38701                 // force background off if it's in center...
38702                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38703                     cfg.background = false;
38704                 }
38705                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38706                 
38707                 
38708                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38709                 //console.log('adding nested layout panel '  + cfg.toSource());
38710                 this.add(region, ret);
38711                 nb = {}; /// find first...
38712                 break;
38713             
38714             case 'Grid':
38715                 
38716                 // needs grid and region
38717                 
38718                 //var el = this.getRegion(region).el.createChild();
38719                 /*
38720                  *var el = this.el.createChild();
38721                 // create the grid first...
38722                 cfg.grid.container = el;
38723                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38724                 */
38725                 
38726                 if (region == 'center' && this.active ) {
38727                     cfg.background = false;
38728                 }
38729                 
38730                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38731                 
38732                 this.add(region, ret);
38733                 /*
38734                 if (cfg.background) {
38735                     // render grid on panel activation (if panel background)
38736                     ret.on('activate', function(gp) {
38737                         if (!gp.grid.rendered) {
38738                     //        gp.grid.render(el);
38739                         }
38740                     });
38741                 } else {
38742                   //  cfg.grid.render(el);
38743                 }
38744                 */
38745                 break;
38746            
38747            
38748             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38749                 // it was the old xcomponent building that caused this before.
38750                 // espeically if border is the top element in the tree.
38751                 ret = this;
38752                 break; 
38753                 
38754                     
38755                 
38756                 
38757                 
38758             default:
38759                 /*
38760                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38761                     
38762                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38763                     this.add(region, ret);
38764                 } else {
38765                 */
38766                     Roo.log(cfg);
38767                     throw "Can not add '" + cfg.xtype + "' to Border";
38768                     return null;
38769              
38770                                 
38771              
38772         }
38773         this.beginUpdate();
38774         // add children..
38775         var region = '';
38776         var abn = {};
38777         Roo.each(xitems, function(i)  {
38778             region = nb && i.region ? i.region : false;
38779             
38780             var add = ret.addxtype(i);
38781            
38782             if (region) {
38783                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38784                 if (!i.background) {
38785                     abn[region] = nb[region] ;
38786                 }
38787             }
38788             
38789         });
38790         this.endUpdate();
38791
38792         // make the last non-background panel active..
38793         //if (nb) { Roo.log(abn); }
38794         if (nb) {
38795             
38796             for(var r in abn) {
38797                 region = this.getRegion(r);
38798                 if (region) {
38799                     // tried using nb[r], but it does not work..
38800                      
38801                     region.showPanel(abn[r]);
38802                    
38803                 }
38804             }
38805         }
38806         return ret;
38807         
38808     },
38809     
38810     
38811 // private
38812     factory : function(cfg)
38813     {
38814         
38815         var validRegions = Roo.bootstrap.layout.Border.regions;
38816
38817         var target = cfg.region;
38818         cfg.mgr = this;
38819         
38820         var r = Roo.bootstrap.layout;
38821         Roo.log(target);
38822         switch(target){
38823             case "north":
38824                 return new r.North(cfg);
38825             case "south":
38826                 return new r.South(cfg);
38827             case "east":
38828                 return new r.East(cfg);
38829             case "west":
38830                 return new r.West(cfg);
38831             case "center":
38832                 return new r.Center(cfg);
38833         }
38834         throw 'Layout region "'+target+'" not supported.';
38835     }
38836     
38837     
38838 });
38839  /*
38840  * Based on:
38841  * Ext JS Library 1.1.1
38842  * Copyright(c) 2006-2007, Ext JS, LLC.
38843  *
38844  * Originally Released Under LGPL - original licence link has changed is not relivant.
38845  *
38846  * Fork - LGPL
38847  * <script type="text/javascript">
38848  */
38849  
38850 /**
38851  * @class Roo.bootstrap.layout.Basic
38852  * @extends Roo.util.Observable
38853  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38854  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38855  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38856  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38857  * @cfg {string}   region  the region that it inhabits..
38858  * @cfg {bool}   skipConfig skip config?
38859  * 
38860
38861  */
38862 Roo.bootstrap.layout.Basic = function(config){
38863     
38864     this.mgr = config.mgr;
38865     
38866     this.position = config.region;
38867     
38868     var skipConfig = config.skipConfig;
38869     
38870     this.events = {
38871         /**
38872          * @scope Roo.BasicLayoutRegion
38873          */
38874         
38875         /**
38876          * @event beforeremove
38877          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38878          * @param {Roo.LayoutRegion} this
38879          * @param {Roo.ContentPanel} panel The panel
38880          * @param {Object} e The cancel event object
38881          */
38882         "beforeremove" : true,
38883         /**
38884          * @event invalidated
38885          * Fires when the layout for this region is changed.
38886          * @param {Roo.LayoutRegion} this
38887          */
38888         "invalidated" : true,
38889         /**
38890          * @event visibilitychange
38891          * Fires when this region is shown or hidden 
38892          * @param {Roo.LayoutRegion} this
38893          * @param {Boolean} visibility true or false
38894          */
38895         "visibilitychange" : true,
38896         /**
38897          * @event paneladded
38898          * Fires when a panel is added. 
38899          * @param {Roo.LayoutRegion} this
38900          * @param {Roo.ContentPanel} panel The panel
38901          */
38902         "paneladded" : true,
38903         /**
38904          * @event panelremoved
38905          * Fires when a panel is removed. 
38906          * @param {Roo.LayoutRegion} this
38907          * @param {Roo.ContentPanel} panel The panel
38908          */
38909         "panelremoved" : true,
38910         /**
38911          * @event beforecollapse
38912          * Fires when this region before collapse.
38913          * @param {Roo.LayoutRegion} this
38914          */
38915         "beforecollapse" : true,
38916         /**
38917          * @event collapsed
38918          * Fires when this region is collapsed.
38919          * @param {Roo.LayoutRegion} this
38920          */
38921         "collapsed" : true,
38922         /**
38923          * @event expanded
38924          * Fires when this region is expanded.
38925          * @param {Roo.LayoutRegion} this
38926          */
38927         "expanded" : true,
38928         /**
38929          * @event slideshow
38930          * Fires when this region is slid into view.
38931          * @param {Roo.LayoutRegion} this
38932          */
38933         "slideshow" : true,
38934         /**
38935          * @event slidehide
38936          * Fires when this region slides out of view. 
38937          * @param {Roo.LayoutRegion} this
38938          */
38939         "slidehide" : true,
38940         /**
38941          * @event panelactivated
38942          * Fires when a panel is activated. 
38943          * @param {Roo.LayoutRegion} this
38944          * @param {Roo.ContentPanel} panel The activated panel
38945          */
38946         "panelactivated" : true,
38947         /**
38948          * @event resized
38949          * Fires when the user resizes this region. 
38950          * @param {Roo.LayoutRegion} this
38951          * @param {Number} newSize The new size (width for east/west, height for north/south)
38952          */
38953         "resized" : true
38954     };
38955     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38956     this.panels = new Roo.util.MixedCollection();
38957     this.panels.getKey = this.getPanelId.createDelegate(this);
38958     this.box = null;
38959     this.activePanel = null;
38960     // ensure listeners are added...
38961     
38962     if (config.listeners || config.events) {
38963         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38964             listeners : config.listeners || {},
38965             events : config.events || {}
38966         });
38967     }
38968     
38969     if(skipConfig !== true){
38970         this.applyConfig(config);
38971     }
38972 };
38973
38974 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38975 {
38976     getPanelId : function(p){
38977         return p.getId();
38978     },
38979     
38980     applyConfig : function(config){
38981         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38982         this.config = config;
38983         
38984     },
38985     
38986     /**
38987      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38988      * the width, for horizontal (north, south) the height.
38989      * @param {Number} newSize The new width or height
38990      */
38991     resizeTo : function(newSize){
38992         var el = this.el ? this.el :
38993                  (this.activePanel ? this.activePanel.getEl() : null);
38994         if(el){
38995             switch(this.position){
38996                 case "east":
38997                 case "west":
38998                     el.setWidth(newSize);
38999                     this.fireEvent("resized", this, newSize);
39000                 break;
39001                 case "north":
39002                 case "south":
39003                     el.setHeight(newSize);
39004                     this.fireEvent("resized", this, newSize);
39005                 break;                
39006             }
39007         }
39008     },
39009     
39010     getBox : function(){
39011         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39012     },
39013     
39014     getMargins : function(){
39015         return this.margins;
39016     },
39017     
39018     updateBox : function(box){
39019         this.box = box;
39020         var el = this.activePanel.getEl();
39021         el.dom.style.left = box.x + "px";
39022         el.dom.style.top = box.y + "px";
39023         this.activePanel.setSize(box.width, box.height);
39024     },
39025     
39026     /**
39027      * Returns the container element for this region.
39028      * @return {Roo.Element}
39029      */
39030     getEl : function(){
39031         return this.activePanel;
39032     },
39033     
39034     /**
39035      * Returns true if this region is currently visible.
39036      * @return {Boolean}
39037      */
39038     isVisible : function(){
39039         return this.activePanel ? true : false;
39040     },
39041     
39042     setActivePanel : function(panel){
39043         panel = this.getPanel(panel);
39044         if(this.activePanel && this.activePanel != panel){
39045             this.activePanel.setActiveState(false);
39046             this.activePanel.getEl().setLeftTop(-10000,-10000);
39047         }
39048         this.activePanel = panel;
39049         panel.setActiveState(true);
39050         if(this.box){
39051             panel.setSize(this.box.width, this.box.height);
39052         }
39053         this.fireEvent("panelactivated", this, panel);
39054         this.fireEvent("invalidated");
39055     },
39056     
39057     /**
39058      * Show the specified panel.
39059      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39060      * @return {Roo.ContentPanel} The shown panel or null
39061      */
39062     showPanel : function(panel){
39063         panel = this.getPanel(panel);
39064         if(panel){
39065             this.setActivePanel(panel);
39066         }
39067         return panel;
39068     },
39069     
39070     /**
39071      * Get the active panel for this region.
39072      * @return {Roo.ContentPanel} The active panel or null
39073      */
39074     getActivePanel : function(){
39075         return this.activePanel;
39076     },
39077     
39078     /**
39079      * Add the passed ContentPanel(s)
39080      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39081      * @return {Roo.ContentPanel} The panel added (if only one was added)
39082      */
39083     add : function(panel){
39084         if(arguments.length > 1){
39085             for(var i = 0, len = arguments.length; i < len; i++) {
39086                 this.add(arguments[i]);
39087             }
39088             return null;
39089         }
39090         if(this.hasPanel(panel)){
39091             this.showPanel(panel);
39092             return panel;
39093         }
39094         var el = panel.getEl();
39095         if(el.dom.parentNode != this.mgr.el.dom){
39096             this.mgr.el.dom.appendChild(el.dom);
39097         }
39098         if(panel.setRegion){
39099             panel.setRegion(this);
39100         }
39101         this.panels.add(panel);
39102         el.setStyle("position", "absolute");
39103         if(!panel.background){
39104             this.setActivePanel(panel);
39105             if(this.config.initialSize && this.panels.getCount()==1){
39106                 this.resizeTo(this.config.initialSize);
39107             }
39108         }
39109         this.fireEvent("paneladded", this, panel);
39110         return panel;
39111     },
39112     
39113     /**
39114      * Returns true if the panel is in this region.
39115      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39116      * @return {Boolean}
39117      */
39118     hasPanel : function(panel){
39119         if(typeof panel == "object"){ // must be panel obj
39120             panel = panel.getId();
39121         }
39122         return this.getPanel(panel) ? true : false;
39123     },
39124     
39125     /**
39126      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39127      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39128      * @param {Boolean} preservePanel Overrides the config preservePanel option
39129      * @return {Roo.ContentPanel} The panel that was removed
39130      */
39131     remove : function(panel, preservePanel){
39132         panel = this.getPanel(panel);
39133         if(!panel){
39134             return null;
39135         }
39136         var e = {};
39137         this.fireEvent("beforeremove", this, panel, e);
39138         if(e.cancel === true){
39139             return null;
39140         }
39141         var panelId = panel.getId();
39142         this.panels.removeKey(panelId);
39143         return panel;
39144     },
39145     
39146     /**
39147      * Returns the panel specified or null if it's not in this region.
39148      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39149      * @return {Roo.ContentPanel}
39150      */
39151     getPanel : function(id){
39152         if(typeof id == "object"){ // must be panel obj
39153             return id;
39154         }
39155         return this.panels.get(id);
39156     },
39157     
39158     /**
39159      * Returns this regions position (north/south/east/west/center).
39160      * @return {String} 
39161      */
39162     getPosition: function(){
39163         return this.position;    
39164     }
39165 });/*
39166  * Based on:
39167  * Ext JS Library 1.1.1
39168  * Copyright(c) 2006-2007, Ext JS, LLC.
39169  *
39170  * Originally Released Under LGPL - original licence link has changed is not relivant.
39171  *
39172  * Fork - LGPL
39173  * <script type="text/javascript">
39174  */
39175  
39176 /**
39177  * @class Roo.bootstrap.layout.Region
39178  * @extends Roo.bootstrap.layout.Basic
39179  * This class represents a region in a layout manager.
39180  
39181  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39182  * @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})
39183  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39184  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39185  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39186  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39187  * @cfg {String}    title           The title for the region (overrides panel titles)
39188  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39189  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39190  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39191  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39192  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39193  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39194  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39195  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39196  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39197  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39198
39199  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39200  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39201  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39202  * @cfg {Number}    width           For East/West panels
39203  * @cfg {Number}    height          For North/South panels
39204  * @cfg {Boolean}   split           To show the splitter
39205  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39206  * 
39207  * @cfg {string}   cls             Extra CSS classes to add to region
39208  * 
39209  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39210  * @cfg {string}   region  the region that it inhabits..
39211  *
39212
39213  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39214  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39215
39216  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39217  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39218  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39219  */
39220 Roo.bootstrap.layout.Region = function(config)
39221 {
39222     this.applyConfig(config);
39223
39224     var mgr = config.mgr;
39225     var pos = config.region;
39226     config.skipConfig = true;
39227     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39228     
39229     if (mgr.el) {
39230         this.onRender(mgr.el);   
39231     }
39232      
39233     this.visible = true;
39234     this.collapsed = false;
39235     this.unrendered_panels = [];
39236 };
39237
39238 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39239
39240     position: '', // set by wrapper (eg. north/south etc..)
39241     unrendered_panels : null,  // unrendered panels.
39242     
39243     tabPosition : false,
39244     
39245     mgr: false, // points to 'Border'
39246     
39247     
39248     createBody : function(){
39249         /** This region's body element 
39250         * @type Roo.Element */
39251         this.bodyEl = this.el.createChild({
39252                 tag: "div",
39253                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39254         });
39255     },
39256
39257     onRender: function(ctr, pos)
39258     {
39259         var dh = Roo.DomHelper;
39260         /** This region's container element 
39261         * @type Roo.Element */
39262         this.el = dh.append(ctr.dom, {
39263                 tag: "div",
39264                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39265             }, true);
39266         /** This region's title element 
39267         * @type Roo.Element */
39268     
39269         this.titleEl = dh.append(this.el.dom,  {
39270                 tag: "div",
39271                 unselectable: "on",
39272                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39273                 children:[
39274                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39275                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39276                 ]
39277             }, true);
39278         
39279         this.titleEl.enableDisplayMode();
39280         /** This region's title text element 
39281         * @type HTMLElement */
39282         this.titleTextEl = this.titleEl.dom.firstChild;
39283         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39284         /*
39285         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39286         this.closeBtn.enableDisplayMode();
39287         this.closeBtn.on("click", this.closeClicked, this);
39288         this.closeBtn.hide();
39289     */
39290         this.createBody(this.config);
39291         if(this.config.hideWhenEmpty){
39292             this.hide();
39293             this.on("paneladded", this.validateVisibility, this);
39294             this.on("panelremoved", this.validateVisibility, this);
39295         }
39296         if(this.autoScroll){
39297             this.bodyEl.setStyle("overflow", "auto");
39298         }else{
39299             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39300         }
39301         //if(c.titlebar !== false){
39302             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39303                 this.titleEl.hide();
39304             }else{
39305                 this.titleEl.show();
39306                 if(this.config.title){
39307                     this.titleTextEl.innerHTML = this.config.title;
39308                 }
39309             }
39310         //}
39311         if(this.config.collapsed){
39312             this.collapse(true);
39313         }
39314         if(this.config.hidden){
39315             this.hide();
39316         }
39317         
39318         if (this.unrendered_panels && this.unrendered_panels.length) {
39319             for (var i =0;i< this.unrendered_panels.length; i++) {
39320                 this.add(this.unrendered_panels[i]);
39321             }
39322             this.unrendered_panels = null;
39323             
39324         }
39325         
39326     },
39327     
39328     applyConfig : function(c)
39329     {
39330         /*
39331          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39332             var dh = Roo.DomHelper;
39333             if(c.titlebar !== false){
39334                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39335                 this.collapseBtn.on("click", this.collapse, this);
39336                 this.collapseBtn.enableDisplayMode();
39337                 /*
39338                 if(c.showPin === true || this.showPin){
39339                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39340                     this.stickBtn.enableDisplayMode();
39341                     this.stickBtn.on("click", this.expand, this);
39342                     this.stickBtn.hide();
39343                 }
39344                 
39345             }
39346             */
39347             /** This region's collapsed element
39348             * @type Roo.Element */
39349             /*
39350              *
39351             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39352                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39353             ]}, true);
39354             
39355             if(c.floatable !== false){
39356                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39357                this.collapsedEl.on("click", this.collapseClick, this);
39358             }
39359
39360             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39361                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39362                    id: "message", unselectable: "on", style:{"float":"left"}});
39363                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39364              }
39365             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39366             this.expandBtn.on("click", this.expand, this);
39367             
39368         }
39369         
39370         if(this.collapseBtn){
39371             this.collapseBtn.setVisible(c.collapsible == true);
39372         }
39373         
39374         this.cmargins = c.cmargins || this.cmargins ||
39375                          (this.position == "west" || this.position == "east" ?
39376                              {top: 0, left: 2, right:2, bottom: 0} :
39377                              {top: 2, left: 0, right:0, bottom: 2});
39378         */
39379         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39380         
39381         
39382         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39383         
39384         this.autoScroll = c.autoScroll || false;
39385         
39386         
39387        
39388         
39389         this.duration = c.duration || .30;
39390         this.slideDuration = c.slideDuration || .45;
39391         this.config = c;
39392        
39393     },
39394     /**
39395      * Returns true if this region is currently visible.
39396      * @return {Boolean}
39397      */
39398     isVisible : function(){
39399         return this.visible;
39400     },
39401
39402     /**
39403      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39404      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39405      */
39406     //setCollapsedTitle : function(title){
39407     //    title = title || "&#160;";
39408      //   if(this.collapsedTitleTextEl){
39409       //      this.collapsedTitleTextEl.innerHTML = title;
39410        // }
39411     //},
39412
39413     getBox : function(){
39414         var b;
39415       //  if(!this.collapsed){
39416             b = this.el.getBox(false, true);
39417        // }else{
39418           //  b = this.collapsedEl.getBox(false, true);
39419         //}
39420         return b;
39421     },
39422
39423     getMargins : function(){
39424         return this.margins;
39425         //return this.collapsed ? this.cmargins : this.margins;
39426     },
39427 /*
39428     highlight : function(){
39429         this.el.addClass("x-layout-panel-dragover");
39430     },
39431
39432     unhighlight : function(){
39433         this.el.removeClass("x-layout-panel-dragover");
39434     },
39435 */
39436     updateBox : function(box)
39437     {
39438         if (!this.bodyEl) {
39439             return; // not rendered yet..
39440         }
39441         
39442         this.box = box;
39443         if(!this.collapsed){
39444             this.el.dom.style.left = box.x + "px";
39445             this.el.dom.style.top = box.y + "px";
39446             this.updateBody(box.width, box.height);
39447         }else{
39448             this.collapsedEl.dom.style.left = box.x + "px";
39449             this.collapsedEl.dom.style.top = box.y + "px";
39450             this.collapsedEl.setSize(box.width, box.height);
39451         }
39452         if(this.tabs){
39453             this.tabs.autoSizeTabs();
39454         }
39455     },
39456
39457     updateBody : function(w, h)
39458     {
39459         if(w !== null){
39460             this.el.setWidth(w);
39461             w -= this.el.getBorderWidth("rl");
39462             if(this.config.adjustments){
39463                 w += this.config.adjustments[0];
39464             }
39465         }
39466         if(h !== null && h > 0){
39467             this.el.setHeight(h);
39468             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39469             h -= this.el.getBorderWidth("tb");
39470             if(this.config.adjustments){
39471                 h += this.config.adjustments[1];
39472             }
39473             this.bodyEl.setHeight(h);
39474             if(this.tabs){
39475                 h = this.tabs.syncHeight(h);
39476             }
39477         }
39478         if(this.panelSize){
39479             w = w !== null ? w : this.panelSize.width;
39480             h = h !== null ? h : this.panelSize.height;
39481         }
39482         if(this.activePanel){
39483             var el = this.activePanel.getEl();
39484             w = w !== null ? w : el.getWidth();
39485             h = h !== null ? h : el.getHeight();
39486             this.panelSize = {width: w, height: h};
39487             this.activePanel.setSize(w, h);
39488         }
39489         if(Roo.isIE && this.tabs){
39490             this.tabs.el.repaint();
39491         }
39492     },
39493
39494     /**
39495      * Returns the container element for this region.
39496      * @return {Roo.Element}
39497      */
39498     getEl : function(){
39499         return this.el;
39500     },
39501
39502     /**
39503      * Hides this region.
39504      */
39505     hide : function(){
39506         //if(!this.collapsed){
39507             this.el.dom.style.left = "-2000px";
39508             this.el.hide();
39509         //}else{
39510          //   this.collapsedEl.dom.style.left = "-2000px";
39511          //   this.collapsedEl.hide();
39512        // }
39513         this.visible = false;
39514         this.fireEvent("visibilitychange", this, false);
39515     },
39516
39517     /**
39518      * Shows this region if it was previously hidden.
39519      */
39520     show : function(){
39521         //if(!this.collapsed){
39522             this.el.show();
39523         //}else{
39524         //    this.collapsedEl.show();
39525        // }
39526         this.visible = true;
39527         this.fireEvent("visibilitychange", this, true);
39528     },
39529 /*
39530     closeClicked : function(){
39531         if(this.activePanel){
39532             this.remove(this.activePanel);
39533         }
39534     },
39535
39536     collapseClick : function(e){
39537         if(this.isSlid){
39538            e.stopPropagation();
39539            this.slideIn();
39540         }else{
39541            e.stopPropagation();
39542            this.slideOut();
39543         }
39544     },
39545 */
39546     /**
39547      * Collapses this region.
39548      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39549      */
39550     /*
39551     collapse : function(skipAnim, skipCheck = false){
39552         if(this.collapsed) {
39553             return;
39554         }
39555         
39556         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39557             
39558             this.collapsed = true;
39559             if(this.split){
39560                 this.split.el.hide();
39561             }
39562             if(this.config.animate && skipAnim !== true){
39563                 this.fireEvent("invalidated", this);
39564                 this.animateCollapse();
39565             }else{
39566                 this.el.setLocation(-20000,-20000);
39567                 this.el.hide();
39568                 this.collapsedEl.show();
39569                 this.fireEvent("collapsed", this);
39570                 this.fireEvent("invalidated", this);
39571             }
39572         }
39573         
39574     },
39575 */
39576     animateCollapse : function(){
39577         // overridden
39578     },
39579
39580     /**
39581      * Expands this region if it was previously collapsed.
39582      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39583      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39584      */
39585     /*
39586     expand : function(e, skipAnim){
39587         if(e) {
39588             e.stopPropagation();
39589         }
39590         if(!this.collapsed || this.el.hasActiveFx()) {
39591             return;
39592         }
39593         if(this.isSlid){
39594             this.afterSlideIn();
39595             skipAnim = true;
39596         }
39597         this.collapsed = false;
39598         if(this.config.animate && skipAnim !== true){
39599             this.animateExpand();
39600         }else{
39601             this.el.show();
39602             if(this.split){
39603                 this.split.el.show();
39604             }
39605             this.collapsedEl.setLocation(-2000,-2000);
39606             this.collapsedEl.hide();
39607             this.fireEvent("invalidated", this);
39608             this.fireEvent("expanded", this);
39609         }
39610     },
39611 */
39612     animateExpand : function(){
39613         // overridden
39614     },
39615
39616     initTabs : function()
39617     {
39618         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39619         
39620         var ts = new Roo.bootstrap.panel.Tabs({
39621             el: this.bodyEl.dom,
39622             region : this,
39623             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39624             disableTooltips: this.config.disableTabTips,
39625             toolbar : this.config.toolbar
39626         });
39627         
39628         if(this.config.hideTabs){
39629             ts.stripWrap.setDisplayed(false);
39630         }
39631         this.tabs = ts;
39632         ts.resizeTabs = this.config.resizeTabs === true;
39633         ts.minTabWidth = this.config.minTabWidth || 40;
39634         ts.maxTabWidth = this.config.maxTabWidth || 250;
39635         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39636         ts.monitorResize = false;
39637         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39638         ts.bodyEl.addClass('roo-layout-tabs-body');
39639         this.panels.each(this.initPanelAsTab, this);
39640     },
39641
39642     initPanelAsTab : function(panel){
39643         var ti = this.tabs.addTab(
39644             panel.getEl().id,
39645             panel.getTitle(),
39646             null,
39647             this.config.closeOnTab && panel.isClosable(),
39648             panel.tpl
39649         );
39650         if(panel.tabTip !== undefined){
39651             ti.setTooltip(panel.tabTip);
39652         }
39653         ti.on("activate", function(){
39654               this.setActivePanel(panel);
39655         }, this);
39656         
39657         if(this.config.closeOnTab){
39658             ti.on("beforeclose", function(t, e){
39659                 e.cancel = true;
39660                 this.remove(panel);
39661             }, this);
39662         }
39663         
39664         panel.tabItem = ti;
39665         
39666         return ti;
39667     },
39668
39669     updatePanelTitle : function(panel, title)
39670     {
39671         if(this.activePanel == panel){
39672             this.updateTitle(title);
39673         }
39674         if(this.tabs){
39675             var ti = this.tabs.getTab(panel.getEl().id);
39676             ti.setText(title);
39677             if(panel.tabTip !== undefined){
39678                 ti.setTooltip(panel.tabTip);
39679             }
39680         }
39681     },
39682
39683     updateTitle : function(title){
39684         if(this.titleTextEl && !this.config.title){
39685             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39686         }
39687     },
39688
39689     setActivePanel : function(panel)
39690     {
39691         panel = this.getPanel(panel);
39692         if(this.activePanel && this.activePanel != panel){
39693             if(this.activePanel.setActiveState(false) === false){
39694                 return;
39695             }
39696         }
39697         this.activePanel = panel;
39698         panel.setActiveState(true);
39699         if(this.panelSize){
39700             panel.setSize(this.panelSize.width, this.panelSize.height);
39701         }
39702         if(this.closeBtn){
39703             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39704         }
39705         this.updateTitle(panel.getTitle());
39706         if(this.tabs){
39707             this.fireEvent("invalidated", this);
39708         }
39709         this.fireEvent("panelactivated", this, panel);
39710     },
39711
39712     /**
39713      * Shows the specified panel.
39714      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39715      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39716      */
39717     showPanel : function(panel)
39718     {
39719         panel = this.getPanel(panel);
39720         if(panel){
39721             if(this.tabs){
39722                 var tab = this.tabs.getTab(panel.getEl().id);
39723                 if(tab.isHidden()){
39724                     this.tabs.unhideTab(tab.id);
39725                 }
39726                 tab.activate();
39727             }else{
39728                 this.setActivePanel(panel);
39729             }
39730         }
39731         return panel;
39732     },
39733
39734     /**
39735      * Get the active panel for this region.
39736      * @return {Roo.ContentPanel} The active panel or null
39737      */
39738     getActivePanel : function(){
39739         return this.activePanel;
39740     },
39741
39742     validateVisibility : function(){
39743         if(this.panels.getCount() < 1){
39744             this.updateTitle("&#160;");
39745             this.closeBtn.hide();
39746             this.hide();
39747         }else{
39748             if(!this.isVisible()){
39749                 this.show();
39750             }
39751         }
39752     },
39753
39754     /**
39755      * Adds the passed ContentPanel(s) to this region.
39756      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39757      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39758      */
39759     add : function(panel)
39760     {
39761         if(arguments.length > 1){
39762             for(var i = 0, len = arguments.length; i < len; i++) {
39763                 this.add(arguments[i]);
39764             }
39765             return null;
39766         }
39767         
39768         // if we have not been rendered yet, then we can not really do much of this..
39769         if (!this.bodyEl) {
39770             this.unrendered_panels.push(panel);
39771             return panel;
39772         }
39773         
39774         
39775         
39776         
39777         if(this.hasPanel(panel)){
39778             this.showPanel(panel);
39779             return panel;
39780         }
39781         panel.setRegion(this);
39782         this.panels.add(panel);
39783        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39784             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39785             // and hide them... ???
39786             this.bodyEl.dom.appendChild(panel.getEl().dom);
39787             if(panel.background !== true){
39788                 this.setActivePanel(panel);
39789             }
39790             this.fireEvent("paneladded", this, panel);
39791             return panel;
39792         }
39793         */
39794         if(!this.tabs){
39795             this.initTabs();
39796         }else{
39797             this.initPanelAsTab(panel);
39798         }
39799         
39800         
39801         if(panel.background !== true){
39802             this.tabs.activate(panel.getEl().id);
39803         }
39804         this.fireEvent("paneladded", this, panel);
39805         return panel;
39806     },
39807
39808     /**
39809      * Hides the tab for the specified panel.
39810      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39811      */
39812     hidePanel : function(panel){
39813         if(this.tabs && (panel = this.getPanel(panel))){
39814             this.tabs.hideTab(panel.getEl().id);
39815         }
39816     },
39817
39818     /**
39819      * Unhides the tab for a previously hidden panel.
39820      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39821      */
39822     unhidePanel : function(panel){
39823         if(this.tabs && (panel = this.getPanel(panel))){
39824             this.tabs.unhideTab(panel.getEl().id);
39825         }
39826     },
39827
39828     clearPanels : function(){
39829         while(this.panels.getCount() > 0){
39830              this.remove(this.panels.first());
39831         }
39832     },
39833
39834     /**
39835      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39836      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39837      * @param {Boolean} preservePanel Overrides the config preservePanel option
39838      * @return {Roo.ContentPanel} The panel that was removed
39839      */
39840     remove : function(panel, preservePanel)
39841     {
39842         panel = this.getPanel(panel);
39843         if(!panel){
39844             return null;
39845         }
39846         var e = {};
39847         this.fireEvent("beforeremove", this, panel, e);
39848         if(e.cancel === true){
39849             return null;
39850         }
39851         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39852         var panelId = panel.getId();
39853         this.panels.removeKey(panelId);
39854         if(preservePanel){
39855             document.body.appendChild(panel.getEl().dom);
39856         }
39857         if(this.tabs){
39858             this.tabs.removeTab(panel.getEl().id);
39859         }else if (!preservePanel){
39860             this.bodyEl.dom.removeChild(panel.getEl().dom);
39861         }
39862         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39863             var p = this.panels.first();
39864             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39865             tempEl.appendChild(p.getEl().dom);
39866             this.bodyEl.update("");
39867             this.bodyEl.dom.appendChild(p.getEl().dom);
39868             tempEl = null;
39869             this.updateTitle(p.getTitle());
39870             this.tabs = null;
39871             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39872             this.setActivePanel(p);
39873         }
39874         panel.setRegion(null);
39875         if(this.activePanel == panel){
39876             this.activePanel = null;
39877         }
39878         if(this.config.autoDestroy !== false && preservePanel !== true){
39879             try{panel.destroy();}catch(e){}
39880         }
39881         this.fireEvent("panelremoved", this, panel);
39882         return panel;
39883     },
39884
39885     /**
39886      * Returns the TabPanel component used by this region
39887      * @return {Roo.TabPanel}
39888      */
39889     getTabs : function(){
39890         return this.tabs;
39891     },
39892
39893     createTool : function(parentEl, className){
39894         var btn = Roo.DomHelper.append(parentEl, {
39895             tag: "div",
39896             cls: "x-layout-tools-button",
39897             children: [ {
39898                 tag: "div",
39899                 cls: "roo-layout-tools-button-inner " + className,
39900                 html: "&#160;"
39901             }]
39902         }, true);
39903         btn.addClassOnOver("roo-layout-tools-button-over");
39904         return btn;
39905     }
39906 });/*
39907  * Based on:
39908  * Ext JS Library 1.1.1
39909  * Copyright(c) 2006-2007, Ext JS, LLC.
39910  *
39911  * Originally Released Under LGPL - original licence link has changed is not relivant.
39912  *
39913  * Fork - LGPL
39914  * <script type="text/javascript">
39915  */
39916  
39917
39918
39919 /**
39920  * @class Roo.SplitLayoutRegion
39921  * @extends Roo.LayoutRegion
39922  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39923  */
39924 Roo.bootstrap.layout.Split = function(config){
39925     this.cursor = config.cursor;
39926     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39927 };
39928
39929 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39930 {
39931     splitTip : "Drag to resize.",
39932     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39933     useSplitTips : false,
39934
39935     applyConfig : function(config){
39936         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39937     },
39938     
39939     onRender : function(ctr,pos) {
39940         
39941         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39942         if(!this.config.split){
39943             return;
39944         }
39945         if(!this.split){
39946             
39947             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39948                             tag: "div",
39949                             id: this.el.id + "-split",
39950                             cls: "roo-layout-split roo-layout-split-"+this.position,
39951                             html: "&#160;"
39952             });
39953             /** The SplitBar for this region 
39954             * @type Roo.SplitBar */
39955             // does not exist yet...
39956             Roo.log([this.position, this.orientation]);
39957             
39958             this.split = new Roo.bootstrap.SplitBar({
39959                 dragElement : splitEl,
39960                 resizingElement: this.el,
39961                 orientation : this.orientation
39962             });
39963             
39964             this.split.on("moved", this.onSplitMove, this);
39965             this.split.useShim = this.config.useShim === true;
39966             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39967             if(this.useSplitTips){
39968                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39969             }
39970             //if(config.collapsible){
39971             //    this.split.el.on("dblclick", this.collapse,  this);
39972             //}
39973         }
39974         if(typeof this.config.minSize != "undefined"){
39975             this.split.minSize = this.config.minSize;
39976         }
39977         if(typeof this.config.maxSize != "undefined"){
39978             this.split.maxSize = this.config.maxSize;
39979         }
39980         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39981             this.hideSplitter();
39982         }
39983         
39984     },
39985
39986     getHMaxSize : function(){
39987          var cmax = this.config.maxSize || 10000;
39988          var center = this.mgr.getRegion("center");
39989          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39990     },
39991
39992     getVMaxSize : function(){
39993          var cmax = this.config.maxSize || 10000;
39994          var center = this.mgr.getRegion("center");
39995          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39996     },
39997
39998     onSplitMove : function(split, newSize){
39999         this.fireEvent("resized", this, newSize);
40000     },
40001     
40002     /** 
40003      * Returns the {@link Roo.SplitBar} for this region.
40004      * @return {Roo.SplitBar}
40005      */
40006     getSplitBar : function(){
40007         return this.split;
40008     },
40009     
40010     hide : function(){
40011         this.hideSplitter();
40012         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40013     },
40014
40015     hideSplitter : function(){
40016         if(this.split){
40017             this.split.el.setLocation(-2000,-2000);
40018             this.split.el.hide();
40019         }
40020     },
40021
40022     show : function(){
40023         if(this.split){
40024             this.split.el.show();
40025         }
40026         Roo.bootstrap.layout.Split.superclass.show.call(this);
40027     },
40028     
40029     beforeSlide: function(){
40030         if(Roo.isGecko){// firefox overflow auto bug workaround
40031             this.bodyEl.clip();
40032             if(this.tabs) {
40033                 this.tabs.bodyEl.clip();
40034             }
40035             if(this.activePanel){
40036                 this.activePanel.getEl().clip();
40037                 
40038                 if(this.activePanel.beforeSlide){
40039                     this.activePanel.beforeSlide();
40040                 }
40041             }
40042         }
40043     },
40044     
40045     afterSlide : function(){
40046         if(Roo.isGecko){// firefox overflow auto bug workaround
40047             this.bodyEl.unclip();
40048             if(this.tabs) {
40049                 this.tabs.bodyEl.unclip();
40050             }
40051             if(this.activePanel){
40052                 this.activePanel.getEl().unclip();
40053                 if(this.activePanel.afterSlide){
40054                     this.activePanel.afterSlide();
40055                 }
40056             }
40057         }
40058     },
40059
40060     initAutoHide : function(){
40061         if(this.autoHide !== false){
40062             if(!this.autoHideHd){
40063                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40064                 this.autoHideHd = {
40065                     "mouseout": function(e){
40066                         if(!e.within(this.el, true)){
40067                             st.delay(500);
40068                         }
40069                     },
40070                     "mouseover" : function(e){
40071                         st.cancel();
40072                     },
40073                     scope : this
40074                 };
40075             }
40076             this.el.on(this.autoHideHd);
40077         }
40078     },
40079
40080     clearAutoHide : function(){
40081         if(this.autoHide !== false){
40082             this.el.un("mouseout", this.autoHideHd.mouseout);
40083             this.el.un("mouseover", this.autoHideHd.mouseover);
40084         }
40085     },
40086
40087     clearMonitor : function(){
40088         Roo.get(document).un("click", this.slideInIf, this);
40089     },
40090
40091     // these names are backwards but not changed for compat
40092     slideOut : function(){
40093         if(this.isSlid || this.el.hasActiveFx()){
40094             return;
40095         }
40096         this.isSlid = true;
40097         if(this.collapseBtn){
40098             this.collapseBtn.hide();
40099         }
40100         this.closeBtnState = this.closeBtn.getStyle('display');
40101         this.closeBtn.hide();
40102         if(this.stickBtn){
40103             this.stickBtn.show();
40104         }
40105         this.el.show();
40106         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40107         this.beforeSlide();
40108         this.el.setStyle("z-index", 10001);
40109         this.el.slideIn(this.getSlideAnchor(), {
40110             callback: function(){
40111                 this.afterSlide();
40112                 this.initAutoHide();
40113                 Roo.get(document).on("click", this.slideInIf, this);
40114                 this.fireEvent("slideshow", this);
40115             },
40116             scope: this,
40117             block: true
40118         });
40119     },
40120
40121     afterSlideIn : function(){
40122         this.clearAutoHide();
40123         this.isSlid = false;
40124         this.clearMonitor();
40125         this.el.setStyle("z-index", "");
40126         if(this.collapseBtn){
40127             this.collapseBtn.show();
40128         }
40129         this.closeBtn.setStyle('display', this.closeBtnState);
40130         if(this.stickBtn){
40131             this.stickBtn.hide();
40132         }
40133         this.fireEvent("slidehide", this);
40134     },
40135
40136     slideIn : function(cb){
40137         if(!this.isSlid || this.el.hasActiveFx()){
40138             Roo.callback(cb);
40139             return;
40140         }
40141         this.isSlid = false;
40142         this.beforeSlide();
40143         this.el.slideOut(this.getSlideAnchor(), {
40144             callback: function(){
40145                 this.el.setLeftTop(-10000, -10000);
40146                 this.afterSlide();
40147                 this.afterSlideIn();
40148                 Roo.callback(cb);
40149             },
40150             scope: this,
40151             block: true
40152         });
40153     },
40154     
40155     slideInIf : function(e){
40156         if(!e.within(this.el)){
40157             this.slideIn();
40158         }
40159     },
40160
40161     animateCollapse : function(){
40162         this.beforeSlide();
40163         this.el.setStyle("z-index", 20000);
40164         var anchor = this.getSlideAnchor();
40165         this.el.slideOut(anchor, {
40166             callback : function(){
40167                 this.el.setStyle("z-index", "");
40168                 this.collapsedEl.slideIn(anchor, {duration:.3});
40169                 this.afterSlide();
40170                 this.el.setLocation(-10000,-10000);
40171                 this.el.hide();
40172                 this.fireEvent("collapsed", this);
40173             },
40174             scope: this,
40175             block: true
40176         });
40177     },
40178
40179     animateExpand : function(){
40180         this.beforeSlide();
40181         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40182         this.el.setStyle("z-index", 20000);
40183         this.collapsedEl.hide({
40184             duration:.1
40185         });
40186         this.el.slideIn(this.getSlideAnchor(), {
40187             callback : function(){
40188                 this.el.setStyle("z-index", "");
40189                 this.afterSlide();
40190                 if(this.split){
40191                     this.split.el.show();
40192                 }
40193                 this.fireEvent("invalidated", this);
40194                 this.fireEvent("expanded", this);
40195             },
40196             scope: this,
40197             block: true
40198         });
40199     },
40200
40201     anchors : {
40202         "west" : "left",
40203         "east" : "right",
40204         "north" : "top",
40205         "south" : "bottom"
40206     },
40207
40208     sanchors : {
40209         "west" : "l",
40210         "east" : "r",
40211         "north" : "t",
40212         "south" : "b"
40213     },
40214
40215     canchors : {
40216         "west" : "tl-tr",
40217         "east" : "tr-tl",
40218         "north" : "tl-bl",
40219         "south" : "bl-tl"
40220     },
40221
40222     getAnchor : function(){
40223         return this.anchors[this.position];
40224     },
40225
40226     getCollapseAnchor : function(){
40227         return this.canchors[this.position];
40228     },
40229
40230     getSlideAnchor : function(){
40231         return this.sanchors[this.position];
40232     },
40233
40234     getAlignAdj : function(){
40235         var cm = this.cmargins;
40236         switch(this.position){
40237             case "west":
40238                 return [0, 0];
40239             break;
40240             case "east":
40241                 return [0, 0];
40242             break;
40243             case "north":
40244                 return [0, 0];
40245             break;
40246             case "south":
40247                 return [0, 0];
40248             break;
40249         }
40250     },
40251
40252     getExpandAdj : function(){
40253         var c = this.collapsedEl, cm = this.cmargins;
40254         switch(this.position){
40255             case "west":
40256                 return [-(cm.right+c.getWidth()+cm.left), 0];
40257             break;
40258             case "east":
40259                 return [cm.right+c.getWidth()+cm.left, 0];
40260             break;
40261             case "north":
40262                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40263             break;
40264             case "south":
40265                 return [0, cm.top+cm.bottom+c.getHeight()];
40266             break;
40267         }
40268     }
40269 });/*
40270  * Based on:
40271  * Ext JS Library 1.1.1
40272  * Copyright(c) 2006-2007, Ext JS, LLC.
40273  *
40274  * Originally Released Under LGPL - original licence link has changed is not relivant.
40275  *
40276  * Fork - LGPL
40277  * <script type="text/javascript">
40278  */
40279 /*
40280  * These classes are private internal classes
40281  */
40282 Roo.bootstrap.layout.Center = function(config){
40283     config.region = "center";
40284     Roo.bootstrap.layout.Region.call(this, config);
40285     this.visible = true;
40286     this.minWidth = config.minWidth || 20;
40287     this.minHeight = config.minHeight || 20;
40288 };
40289
40290 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40291     hide : function(){
40292         // center panel can't be hidden
40293     },
40294     
40295     show : function(){
40296         // center panel can't be hidden
40297     },
40298     
40299     getMinWidth: function(){
40300         return this.minWidth;
40301     },
40302     
40303     getMinHeight: function(){
40304         return this.minHeight;
40305     }
40306 });
40307
40308
40309
40310
40311  
40312
40313
40314
40315
40316
40317
40318 Roo.bootstrap.layout.North = function(config)
40319 {
40320     config.region = 'north';
40321     config.cursor = 'n-resize';
40322     
40323     Roo.bootstrap.layout.Split.call(this, config);
40324     
40325     
40326     if(this.split){
40327         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40328         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40329         this.split.el.addClass("roo-layout-split-v");
40330     }
40331     //var size = config.initialSize || config.height;
40332     //if(this.el && typeof size != "undefined"){
40333     //    this.el.setHeight(size);
40334     //}
40335 };
40336 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40337 {
40338     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40339      
40340      
40341     onRender : function(ctr, pos)
40342     {
40343         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40344         var size = this.config.initialSize || this.config.height;
40345         if(this.el && typeof size != "undefined"){
40346             this.el.setHeight(size);
40347         }
40348     
40349     },
40350     
40351     getBox : function(){
40352         if(this.collapsed){
40353             return this.collapsedEl.getBox();
40354         }
40355         var box = this.el.getBox();
40356         if(this.split){
40357             box.height += this.split.el.getHeight();
40358         }
40359         return box;
40360     },
40361     
40362     updateBox : function(box){
40363         if(this.split && !this.collapsed){
40364             box.height -= this.split.el.getHeight();
40365             this.split.el.setLeft(box.x);
40366             this.split.el.setTop(box.y+box.height);
40367             this.split.el.setWidth(box.width);
40368         }
40369         if(this.collapsed){
40370             this.updateBody(box.width, null);
40371         }
40372         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40373     }
40374 });
40375
40376
40377
40378
40379
40380 Roo.bootstrap.layout.South = function(config){
40381     config.region = 'south';
40382     config.cursor = 's-resize';
40383     Roo.bootstrap.layout.Split.call(this, config);
40384     if(this.split){
40385         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40386         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40387         this.split.el.addClass("roo-layout-split-v");
40388     }
40389     
40390 };
40391
40392 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40393     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40394     
40395     onRender : function(ctr, pos)
40396     {
40397         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40398         var size = this.config.initialSize || this.config.height;
40399         if(this.el && typeof size != "undefined"){
40400             this.el.setHeight(size);
40401         }
40402     
40403     },
40404     
40405     getBox : function(){
40406         if(this.collapsed){
40407             return this.collapsedEl.getBox();
40408         }
40409         var box = this.el.getBox();
40410         if(this.split){
40411             var sh = this.split.el.getHeight();
40412             box.height += sh;
40413             box.y -= sh;
40414         }
40415         return box;
40416     },
40417     
40418     updateBox : function(box){
40419         if(this.split && !this.collapsed){
40420             var sh = this.split.el.getHeight();
40421             box.height -= sh;
40422             box.y += sh;
40423             this.split.el.setLeft(box.x);
40424             this.split.el.setTop(box.y-sh);
40425             this.split.el.setWidth(box.width);
40426         }
40427         if(this.collapsed){
40428             this.updateBody(box.width, null);
40429         }
40430         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40431     }
40432 });
40433
40434 Roo.bootstrap.layout.East = function(config){
40435     config.region = "east";
40436     config.cursor = "e-resize";
40437     Roo.bootstrap.layout.Split.call(this, config);
40438     if(this.split){
40439         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40440         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40441         this.split.el.addClass("roo-layout-split-h");
40442     }
40443     
40444 };
40445 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40446     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40447     
40448     onRender : function(ctr, pos)
40449     {
40450         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40451         var size = this.config.initialSize || this.config.width;
40452         if(this.el && typeof size != "undefined"){
40453             this.el.setWidth(size);
40454         }
40455     
40456     },
40457     
40458     getBox : function(){
40459         if(this.collapsed){
40460             return this.collapsedEl.getBox();
40461         }
40462         var box = this.el.getBox();
40463         if(this.split){
40464             var sw = this.split.el.getWidth();
40465             box.width += sw;
40466             box.x -= sw;
40467         }
40468         return box;
40469     },
40470
40471     updateBox : function(box){
40472         if(this.split && !this.collapsed){
40473             var sw = this.split.el.getWidth();
40474             box.width -= sw;
40475             this.split.el.setLeft(box.x);
40476             this.split.el.setTop(box.y);
40477             this.split.el.setHeight(box.height);
40478             box.x += sw;
40479         }
40480         if(this.collapsed){
40481             this.updateBody(null, box.height);
40482         }
40483         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40484     }
40485 });
40486
40487 Roo.bootstrap.layout.West = function(config){
40488     config.region = "west";
40489     config.cursor = "w-resize";
40490     
40491     Roo.bootstrap.layout.Split.call(this, config);
40492     if(this.split){
40493         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40494         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40495         this.split.el.addClass("roo-layout-split-h");
40496     }
40497     
40498 };
40499 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40500     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40501     
40502     onRender: function(ctr, pos)
40503     {
40504         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40505         var size = this.config.initialSize || this.config.width;
40506         if(typeof size != "undefined"){
40507             this.el.setWidth(size);
40508         }
40509     },
40510     
40511     getBox : function(){
40512         if(this.collapsed){
40513             return this.collapsedEl.getBox();
40514         }
40515         var box = this.el.getBox();
40516         if (box.width == 0) {
40517             box.width = this.config.width; // kludge?
40518         }
40519         if(this.split){
40520             box.width += this.split.el.getWidth();
40521         }
40522         return box;
40523     },
40524     
40525     updateBox : function(box){
40526         if(this.split && !this.collapsed){
40527             var sw = this.split.el.getWidth();
40528             box.width -= sw;
40529             this.split.el.setLeft(box.x+box.width);
40530             this.split.el.setTop(box.y);
40531             this.split.el.setHeight(box.height);
40532         }
40533         if(this.collapsed){
40534             this.updateBody(null, box.height);
40535         }
40536         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40537     }
40538 });Roo.namespace("Roo.bootstrap.panel");/*
40539  * Based on:
40540  * Ext JS Library 1.1.1
40541  * Copyright(c) 2006-2007, Ext JS, LLC.
40542  *
40543  * Originally Released Under LGPL - original licence link has changed is not relivant.
40544  *
40545  * Fork - LGPL
40546  * <script type="text/javascript">
40547  */
40548 /**
40549  * @class Roo.ContentPanel
40550  * @extends Roo.util.Observable
40551  * A basic ContentPanel element.
40552  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40553  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40554  * @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
40555  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40556  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40557  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40558  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40559  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40560  * @cfg {String} title          The title for this panel
40561  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40562  * @cfg {String} url            Calls {@link #setUrl} with this value
40563  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40564  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40565  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40566  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40567  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40568  * @cfg {Boolean} badges render the badges
40569  * @cfg {String} cls  extra classes to use  
40570  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40571
40572  * @constructor
40573  * Create a new ContentPanel.
40574  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40575  * @param {String/Object} config A string to set only the title or a config object
40576  * @param {String} content (optional) Set the HTML content for this panel
40577  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40578  */
40579 Roo.bootstrap.panel.Content = function( config){
40580     
40581     this.tpl = config.tpl || false;
40582     
40583     var el = config.el;
40584     var content = config.content;
40585
40586     if(config.autoCreate){ // xtype is available if this is called from factory
40587         el = Roo.id();
40588     }
40589     this.el = Roo.get(el);
40590     if(!this.el && config && config.autoCreate){
40591         if(typeof config.autoCreate == "object"){
40592             if(!config.autoCreate.id){
40593                 config.autoCreate.id = config.id||el;
40594             }
40595             this.el = Roo.DomHelper.append(document.body,
40596                         config.autoCreate, true);
40597         }else{
40598             var elcfg =  {
40599                 tag: "div",
40600                 cls: (config.cls || '') +
40601                     (config.background ? ' bg-' + config.background : '') +
40602                     " roo-layout-inactive-content",
40603                 id: config.id||el
40604             };
40605             if (config.iframe) {
40606                 elcfg.cn = [
40607                     {
40608                         tag : 'iframe',
40609                         style : 'border: 0px',
40610                         src : 'about:blank'
40611                     }
40612                 ];
40613             }
40614               
40615             if (config.html) {
40616                 elcfg.html = config.html;
40617                 
40618             }
40619                         
40620             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40621             if (config.iframe) {
40622                 this.iframeEl = this.el.select('iframe',true).first();
40623             }
40624             
40625         }
40626     } 
40627     this.closable = false;
40628     this.loaded = false;
40629     this.active = false;
40630    
40631       
40632     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40633         
40634         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40635         
40636         this.wrapEl = this.el; //this.el.wrap();
40637         var ti = [];
40638         if (config.toolbar.items) {
40639             ti = config.toolbar.items ;
40640             delete config.toolbar.items ;
40641         }
40642         
40643         var nitems = [];
40644         this.toolbar.render(this.wrapEl, 'before');
40645         for(var i =0;i < ti.length;i++) {
40646           //  Roo.log(['add child', items[i]]);
40647             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40648         }
40649         this.toolbar.items = nitems;
40650         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40651         delete config.toolbar;
40652         
40653     }
40654     /*
40655     // xtype created footer. - not sure if will work as we normally have to render first..
40656     if (this.footer && !this.footer.el && this.footer.xtype) {
40657         if (!this.wrapEl) {
40658             this.wrapEl = this.el.wrap();
40659         }
40660     
40661         this.footer.container = this.wrapEl.createChild();
40662          
40663         this.footer = Roo.factory(this.footer, Roo);
40664         
40665     }
40666     */
40667     
40668      if(typeof config == "string"){
40669         this.title = config;
40670     }else{
40671         Roo.apply(this, config);
40672     }
40673     
40674     if(this.resizeEl){
40675         this.resizeEl = Roo.get(this.resizeEl, true);
40676     }else{
40677         this.resizeEl = this.el;
40678     }
40679     // handle view.xtype
40680     
40681  
40682     
40683     
40684     this.addEvents({
40685         /**
40686          * @event activate
40687          * Fires when this panel is activated. 
40688          * @param {Roo.ContentPanel} this
40689          */
40690         "activate" : true,
40691         /**
40692          * @event deactivate
40693          * Fires when this panel is activated. 
40694          * @param {Roo.ContentPanel} this
40695          */
40696         "deactivate" : true,
40697
40698         /**
40699          * @event resize
40700          * Fires when this panel is resized if fitToFrame is true.
40701          * @param {Roo.ContentPanel} this
40702          * @param {Number} width The width after any component adjustments
40703          * @param {Number} height The height after any component adjustments
40704          */
40705         "resize" : true,
40706         
40707          /**
40708          * @event render
40709          * Fires when this tab is created
40710          * @param {Roo.ContentPanel} this
40711          */
40712         "render" : true,
40713         
40714           /**
40715          * @event scroll
40716          * Fires when this content is scrolled
40717          * @param {Roo.ContentPanel} this
40718          * @param {Event} scrollEvent
40719          */
40720         "scroll" : true
40721         
40722         
40723         
40724     });
40725     
40726
40727     
40728     
40729     if(this.autoScroll && !this.iframe){
40730         this.resizeEl.setStyle("overflow", "auto");
40731         this.resizeEl.on('scroll', this.onScroll, this);
40732     } else {
40733         // fix randome scrolling
40734         //this.el.on('scroll', function() {
40735         //    Roo.log('fix random scolling');
40736         //    this.scrollTo('top',0); 
40737         //});
40738     }
40739     content = content || this.content;
40740     if(content){
40741         this.setContent(content);
40742     }
40743     if(config && config.url){
40744         this.setUrl(this.url, this.params, this.loadOnce);
40745     }
40746     
40747     
40748     
40749     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40750     
40751     if (this.view && typeof(this.view.xtype) != 'undefined') {
40752         this.view.el = this.el.appendChild(document.createElement("div"));
40753         this.view = Roo.factory(this.view); 
40754         this.view.render  &&  this.view.render(false, '');  
40755     }
40756     
40757     
40758     this.fireEvent('render', this);
40759 };
40760
40761 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40762     
40763     cls : '',
40764     background : '',
40765     
40766     tabTip : '',
40767     
40768     iframe : false,
40769     iframeEl : false,
40770     
40771     /* Resize Element - use this to work out scroll etc. */
40772     resizeEl : false,
40773     
40774     setRegion : function(region){
40775         this.region = region;
40776         this.setActiveClass(region && !this.background);
40777     },
40778     
40779     
40780     setActiveClass: function(state)
40781     {
40782         if(state){
40783            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40784            this.el.setStyle('position','relative');
40785         }else{
40786            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40787            this.el.setStyle('position', 'absolute');
40788         } 
40789     },
40790     
40791     /**
40792      * Returns the toolbar for this Panel if one was configured. 
40793      * @return {Roo.Toolbar} 
40794      */
40795     getToolbar : function(){
40796         return this.toolbar;
40797     },
40798     
40799     setActiveState : function(active)
40800     {
40801         this.active = active;
40802         this.setActiveClass(active);
40803         if(!active){
40804             if(this.fireEvent("deactivate", this) === false){
40805                 return false;
40806             }
40807             return true;
40808         }
40809         this.fireEvent("activate", this);
40810         return true;
40811     },
40812     /**
40813      * Updates this panel's element (not for iframe)
40814      * @param {String} content The new content
40815      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40816     */
40817     setContent : function(content, loadScripts){
40818         if (this.iframe) {
40819             return;
40820         }
40821         
40822         this.el.update(content, loadScripts);
40823     },
40824
40825     ignoreResize : function(w, h){
40826         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40827             return true;
40828         }else{
40829             this.lastSize = {width: w, height: h};
40830             return false;
40831         }
40832     },
40833     /**
40834      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40835      * @return {Roo.UpdateManager} The UpdateManager
40836      */
40837     getUpdateManager : function(){
40838         if (this.iframe) {
40839             return false;
40840         }
40841         return this.el.getUpdateManager();
40842     },
40843      /**
40844      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40845      * Does not work with IFRAME contents
40846      * @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:
40847 <pre><code>
40848 panel.load({
40849     url: "your-url.php",
40850     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40851     callback: yourFunction,
40852     scope: yourObject, //(optional scope)
40853     discardUrl: false,
40854     nocache: false,
40855     text: "Loading...",
40856     timeout: 30,
40857     scripts: false
40858 });
40859 </code></pre>
40860      
40861      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40862      * 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.
40863      * @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}
40864      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40865      * @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.
40866      * @return {Roo.ContentPanel} this
40867      */
40868     load : function(){
40869         
40870         if (this.iframe) {
40871             return this;
40872         }
40873         
40874         var um = this.el.getUpdateManager();
40875         um.update.apply(um, arguments);
40876         return this;
40877     },
40878
40879
40880     /**
40881      * 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.
40882      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40883      * @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)
40884      * @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)
40885      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40886      */
40887     setUrl : function(url, params, loadOnce){
40888         if (this.iframe) {
40889             this.iframeEl.dom.src = url;
40890             return false;
40891         }
40892         
40893         if(this.refreshDelegate){
40894             this.removeListener("activate", this.refreshDelegate);
40895         }
40896         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40897         this.on("activate", this.refreshDelegate);
40898         return this.el.getUpdateManager();
40899     },
40900     
40901     _handleRefresh : function(url, params, loadOnce){
40902         if(!loadOnce || !this.loaded){
40903             var updater = this.el.getUpdateManager();
40904             updater.update(url, params, this._setLoaded.createDelegate(this));
40905         }
40906     },
40907     
40908     _setLoaded : function(){
40909         this.loaded = true;
40910     }, 
40911     
40912     /**
40913      * Returns this panel's id
40914      * @return {String} 
40915      */
40916     getId : function(){
40917         return this.el.id;
40918     },
40919     
40920     /** 
40921      * Returns this panel's element - used by regiosn to add.
40922      * @return {Roo.Element} 
40923      */
40924     getEl : function(){
40925         return this.wrapEl || this.el;
40926     },
40927     
40928    
40929     
40930     adjustForComponents : function(width, height)
40931     {
40932         //Roo.log('adjustForComponents ');
40933         if(this.resizeEl != this.el){
40934             width -= this.el.getFrameWidth('lr');
40935             height -= this.el.getFrameWidth('tb');
40936         }
40937         if(this.toolbar){
40938             var te = this.toolbar.getEl();
40939             te.setWidth(width);
40940             height -= te.getHeight();
40941         }
40942         if(this.footer){
40943             var te = this.footer.getEl();
40944             te.setWidth(width);
40945             height -= te.getHeight();
40946         }
40947         
40948         
40949         if(this.adjustments){
40950             width += this.adjustments[0];
40951             height += this.adjustments[1];
40952         }
40953         return {"width": width, "height": height};
40954     },
40955     
40956     setSize : function(width, height){
40957         if(this.fitToFrame && !this.ignoreResize(width, height)){
40958             if(this.fitContainer && this.resizeEl != this.el){
40959                 this.el.setSize(width, height);
40960             }
40961             var size = this.adjustForComponents(width, height);
40962             if (this.iframe) {
40963                 this.iframeEl.setSize(width,height);
40964             }
40965             
40966             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40967             this.fireEvent('resize', this, size.width, size.height);
40968             
40969             
40970         }
40971     },
40972     
40973     /**
40974      * Returns this panel's title
40975      * @return {String} 
40976      */
40977     getTitle : function(){
40978         
40979         if (typeof(this.title) != 'object') {
40980             return this.title;
40981         }
40982         
40983         var t = '';
40984         for (var k in this.title) {
40985             if (!this.title.hasOwnProperty(k)) {
40986                 continue;
40987             }
40988             
40989             if (k.indexOf('-') >= 0) {
40990                 var s = k.split('-');
40991                 for (var i = 0; i<s.length; i++) {
40992                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40993                 }
40994             } else {
40995                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40996             }
40997         }
40998         return t;
40999     },
41000     
41001     /**
41002      * Set this panel's title
41003      * @param {String} title
41004      */
41005     setTitle : function(title){
41006         this.title = title;
41007         if(this.region){
41008             this.region.updatePanelTitle(this, title);
41009         }
41010     },
41011     
41012     /**
41013      * Returns true is this panel was configured to be closable
41014      * @return {Boolean} 
41015      */
41016     isClosable : function(){
41017         return this.closable;
41018     },
41019     
41020     beforeSlide : function(){
41021         this.el.clip();
41022         this.resizeEl.clip();
41023     },
41024     
41025     afterSlide : function(){
41026         this.el.unclip();
41027         this.resizeEl.unclip();
41028     },
41029     
41030     /**
41031      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41032      *   Will fail silently if the {@link #setUrl} method has not been called.
41033      *   This does not activate the panel, just updates its content.
41034      */
41035     refresh : function(){
41036         if(this.refreshDelegate){
41037            this.loaded = false;
41038            this.refreshDelegate();
41039         }
41040     },
41041     
41042     /**
41043      * Destroys this panel
41044      */
41045     destroy : function(){
41046         this.el.removeAllListeners();
41047         var tempEl = document.createElement("span");
41048         tempEl.appendChild(this.el.dom);
41049         tempEl.innerHTML = "";
41050         this.el.remove();
41051         this.el = null;
41052     },
41053     
41054     /**
41055      * form - if the content panel contains a form - this is a reference to it.
41056      * @type {Roo.form.Form}
41057      */
41058     form : false,
41059     /**
41060      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41061      *    This contains a reference to it.
41062      * @type {Roo.View}
41063      */
41064     view : false,
41065     
41066       /**
41067      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41068      * <pre><code>
41069
41070 layout.addxtype({
41071        xtype : 'Form',
41072        items: [ .... ]
41073    }
41074 );
41075
41076 </code></pre>
41077      * @param {Object} cfg Xtype definition of item to add.
41078      */
41079     
41080     
41081     getChildContainer: function () {
41082         return this.getEl();
41083     },
41084     
41085     
41086     onScroll : function(e)
41087     {
41088         this.fireEvent('scroll', this, e);
41089     }
41090     
41091     
41092     /*
41093         var  ret = new Roo.factory(cfg);
41094         return ret;
41095         
41096         
41097         // add form..
41098         if (cfg.xtype.match(/^Form$/)) {
41099             
41100             var el;
41101             //if (this.footer) {
41102             //    el = this.footer.container.insertSibling(false, 'before');
41103             //} else {
41104                 el = this.el.createChild();
41105             //}
41106
41107             this.form = new  Roo.form.Form(cfg);
41108             
41109             
41110             if ( this.form.allItems.length) {
41111                 this.form.render(el.dom);
41112             }
41113             return this.form;
41114         }
41115         // should only have one of theses..
41116         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41117             // views.. should not be just added - used named prop 'view''
41118             
41119             cfg.el = this.el.appendChild(document.createElement("div"));
41120             // factory?
41121             
41122             var ret = new Roo.factory(cfg);
41123              
41124              ret.render && ret.render(false, ''); // render blank..
41125             this.view = ret;
41126             return ret;
41127         }
41128         return false;
41129     }
41130     \*/
41131 });
41132  
41133 /**
41134  * @class Roo.bootstrap.panel.Grid
41135  * @extends Roo.bootstrap.panel.Content
41136  * @constructor
41137  * Create a new GridPanel.
41138  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41139  * @param {Object} config A the config object
41140   
41141  */
41142
41143
41144
41145 Roo.bootstrap.panel.Grid = function(config)
41146 {
41147     
41148       
41149     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41150         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41151
41152     config.el = this.wrapper;
41153     //this.el = this.wrapper;
41154     
41155       if (config.container) {
41156         // ctor'ed from a Border/panel.grid
41157         
41158         
41159         this.wrapper.setStyle("overflow", "hidden");
41160         this.wrapper.addClass('roo-grid-container');
41161
41162     }
41163     
41164     
41165     if(config.toolbar){
41166         var tool_el = this.wrapper.createChild();    
41167         this.toolbar = Roo.factory(config.toolbar);
41168         var ti = [];
41169         if (config.toolbar.items) {
41170             ti = config.toolbar.items ;
41171             delete config.toolbar.items ;
41172         }
41173         
41174         var nitems = [];
41175         this.toolbar.render(tool_el);
41176         for(var i =0;i < ti.length;i++) {
41177           //  Roo.log(['add child', items[i]]);
41178             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41179         }
41180         this.toolbar.items = nitems;
41181         
41182         delete config.toolbar;
41183     }
41184     
41185     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41186     config.grid.scrollBody = true;;
41187     config.grid.monitorWindowResize = false; // turn off autosizing
41188     config.grid.autoHeight = false;
41189     config.grid.autoWidth = false;
41190     
41191     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41192     
41193     if (config.background) {
41194         // render grid on panel activation (if panel background)
41195         this.on('activate', function(gp) {
41196             if (!gp.grid.rendered) {
41197                 gp.grid.render(this.wrapper);
41198                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41199             }
41200         });
41201             
41202     } else {
41203         this.grid.render(this.wrapper);
41204         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41205
41206     }
41207     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41208     // ??? needed ??? config.el = this.wrapper;
41209     
41210     
41211     
41212   
41213     // xtype created footer. - not sure if will work as we normally have to render first..
41214     if (this.footer && !this.footer.el && this.footer.xtype) {
41215         
41216         var ctr = this.grid.getView().getFooterPanel(true);
41217         this.footer.dataSource = this.grid.dataSource;
41218         this.footer = Roo.factory(this.footer, Roo);
41219         this.footer.render(ctr);
41220         
41221     }
41222     
41223     
41224     
41225     
41226      
41227 };
41228
41229 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41230     getId : function(){
41231         return this.grid.id;
41232     },
41233     
41234     /**
41235      * Returns the grid for this panel
41236      * @return {Roo.bootstrap.Table} 
41237      */
41238     getGrid : function(){
41239         return this.grid;    
41240     },
41241     
41242     setSize : function(width, height){
41243         if(!this.ignoreResize(width, height)){
41244             var grid = this.grid;
41245             var size = this.adjustForComponents(width, height);
41246             // tfoot is not a footer?
41247           
41248             
41249             var gridel = grid.getGridEl();
41250             gridel.setSize(size.width, size.height);
41251             
41252             var tbd = grid.getGridEl().select('tbody', true).first();
41253             var thd = grid.getGridEl().select('thead',true).first();
41254             var tbf= grid.getGridEl().select('tfoot', true).first();
41255
41256             if (tbf) {
41257                 size.height -= tbf.getHeight();
41258             }
41259             if (thd) {
41260                 size.height -= thd.getHeight();
41261             }
41262             
41263             tbd.setSize(size.width, size.height );
41264             // this is for the account management tab -seems to work there.
41265             var thd = grid.getGridEl().select('thead',true).first();
41266             //if (tbd) {
41267             //    tbd.setSize(size.width, size.height - thd.getHeight());
41268             //}
41269              
41270             grid.autoSize();
41271         }
41272     },
41273      
41274     
41275     
41276     beforeSlide : function(){
41277         this.grid.getView().scroller.clip();
41278     },
41279     
41280     afterSlide : function(){
41281         this.grid.getView().scroller.unclip();
41282     },
41283     
41284     destroy : function(){
41285         this.grid.destroy();
41286         delete this.grid;
41287         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41288     }
41289 });
41290
41291 /**
41292  * @class Roo.bootstrap.panel.Nest
41293  * @extends Roo.bootstrap.panel.Content
41294  * @constructor
41295  * Create a new Panel, that can contain a layout.Border.
41296  * 
41297  * 
41298  * @param {Roo.BorderLayout} layout The layout for this panel
41299  * @param {String/Object} config A string to set only the title or a config object
41300  */
41301 Roo.bootstrap.panel.Nest = function(config)
41302 {
41303     // construct with only one argument..
41304     /* FIXME - implement nicer consturctors
41305     if (layout.layout) {
41306         config = layout;
41307         layout = config.layout;
41308         delete config.layout;
41309     }
41310     if (layout.xtype && !layout.getEl) {
41311         // then layout needs constructing..
41312         layout = Roo.factory(layout, Roo);
41313     }
41314     */
41315     
41316     config.el =  config.layout.getEl();
41317     
41318     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41319     
41320     config.layout.monitorWindowResize = false; // turn off autosizing
41321     this.layout = config.layout;
41322     this.layout.getEl().addClass("roo-layout-nested-layout");
41323     this.layout.parent = this;
41324     
41325     
41326     
41327     
41328 };
41329
41330 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41331
41332     setSize : function(width, height){
41333         if(!this.ignoreResize(width, height)){
41334             var size = this.adjustForComponents(width, height);
41335             var el = this.layout.getEl();
41336             if (size.height < 1) {
41337                 el.setWidth(size.width);   
41338             } else {
41339                 el.setSize(size.width, size.height);
41340             }
41341             var touch = el.dom.offsetWidth;
41342             this.layout.layout();
41343             // ie requires a double layout on the first pass
41344             if(Roo.isIE && !this.initialized){
41345                 this.initialized = true;
41346                 this.layout.layout();
41347             }
41348         }
41349     },
41350     
41351     // activate all subpanels if not currently active..
41352     
41353     setActiveState : function(active){
41354         this.active = active;
41355         this.setActiveClass(active);
41356         
41357         if(!active){
41358             this.fireEvent("deactivate", this);
41359             return;
41360         }
41361         
41362         this.fireEvent("activate", this);
41363         // not sure if this should happen before or after..
41364         if (!this.layout) {
41365             return; // should not happen..
41366         }
41367         var reg = false;
41368         for (var r in this.layout.regions) {
41369             reg = this.layout.getRegion(r);
41370             if (reg.getActivePanel()) {
41371                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41372                 reg.setActivePanel(reg.getActivePanel());
41373                 continue;
41374             }
41375             if (!reg.panels.length) {
41376                 continue;
41377             }
41378             reg.showPanel(reg.getPanel(0));
41379         }
41380         
41381         
41382         
41383         
41384     },
41385     
41386     /**
41387      * Returns the nested BorderLayout for this panel
41388      * @return {Roo.BorderLayout} 
41389      */
41390     getLayout : function(){
41391         return this.layout;
41392     },
41393     
41394      /**
41395      * Adds a xtype elements to the layout of the nested panel
41396      * <pre><code>
41397
41398 panel.addxtype({
41399        xtype : 'ContentPanel',
41400        region: 'west',
41401        items: [ .... ]
41402    }
41403 );
41404
41405 panel.addxtype({
41406         xtype : 'NestedLayoutPanel',
41407         region: 'west',
41408         layout: {
41409            center: { },
41410            west: { }   
41411         },
41412         items : [ ... list of content panels or nested layout panels.. ]
41413    }
41414 );
41415 </code></pre>
41416      * @param {Object} cfg Xtype definition of item to add.
41417      */
41418     addxtype : function(cfg) {
41419         return this.layout.addxtype(cfg);
41420     
41421     }
41422 });/*
41423  * Based on:
41424  * Ext JS Library 1.1.1
41425  * Copyright(c) 2006-2007, Ext JS, LLC.
41426  *
41427  * Originally Released Under LGPL - original licence link has changed is not relivant.
41428  *
41429  * Fork - LGPL
41430  * <script type="text/javascript">
41431  */
41432 /**
41433  * @class Roo.TabPanel
41434  * @extends Roo.util.Observable
41435  * A lightweight tab container.
41436  * <br><br>
41437  * Usage:
41438  * <pre><code>
41439 // basic tabs 1, built from existing content
41440 var tabs = new Roo.TabPanel("tabs1");
41441 tabs.addTab("script", "View Script");
41442 tabs.addTab("markup", "View Markup");
41443 tabs.activate("script");
41444
41445 // more advanced tabs, built from javascript
41446 var jtabs = new Roo.TabPanel("jtabs");
41447 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41448
41449 // set up the UpdateManager
41450 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41451 var updater = tab2.getUpdateManager();
41452 updater.setDefaultUrl("ajax1.htm");
41453 tab2.on('activate', updater.refresh, updater, true);
41454
41455 // Use setUrl for Ajax loading
41456 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41457 tab3.setUrl("ajax2.htm", null, true);
41458
41459 // Disabled tab
41460 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41461 tab4.disable();
41462
41463 jtabs.activate("jtabs-1");
41464  * </code></pre>
41465  * @constructor
41466  * Create a new TabPanel.
41467  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41468  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41469  */
41470 Roo.bootstrap.panel.Tabs = function(config){
41471     /**
41472     * The container element for this TabPanel.
41473     * @type Roo.Element
41474     */
41475     this.el = Roo.get(config.el);
41476     delete config.el;
41477     if(config){
41478         if(typeof config == "boolean"){
41479             this.tabPosition = config ? "bottom" : "top";
41480         }else{
41481             Roo.apply(this, config);
41482         }
41483     }
41484     
41485     if(this.tabPosition == "bottom"){
41486         // if tabs are at the bottom = create the body first.
41487         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41488         this.el.addClass("roo-tabs-bottom");
41489     }
41490     // next create the tabs holders
41491     
41492     if (this.tabPosition == "west"){
41493         
41494         var reg = this.region; // fake it..
41495         while (reg) {
41496             if (!reg.mgr.parent) {
41497                 break;
41498             }
41499             reg = reg.mgr.parent.region;
41500         }
41501         Roo.log("got nest?");
41502         Roo.log(reg);
41503         if (reg.mgr.getRegion('west')) {
41504             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41505             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41506             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41507             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41508             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41509         
41510             
41511         }
41512         
41513         
41514     } else {
41515      
41516         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41517         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41518         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41519         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41520     }
41521     
41522     
41523     if(Roo.isIE){
41524         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41525     }
41526     
41527     // finally - if tabs are at the top, then create the body last..
41528     if(this.tabPosition != "bottom"){
41529         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41530          * @type Roo.Element
41531          */
41532         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41533         this.el.addClass("roo-tabs-top");
41534     }
41535     this.items = [];
41536
41537     this.bodyEl.setStyle("position", "relative");
41538
41539     this.active = null;
41540     this.activateDelegate = this.activate.createDelegate(this);
41541
41542     this.addEvents({
41543         /**
41544          * @event tabchange
41545          * Fires when the active tab changes
41546          * @param {Roo.TabPanel} this
41547          * @param {Roo.TabPanelItem} activePanel The new active tab
41548          */
41549         "tabchange": true,
41550         /**
41551          * @event beforetabchange
41552          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41553          * @param {Roo.TabPanel} this
41554          * @param {Object} e Set cancel to true on this object to cancel the tab change
41555          * @param {Roo.TabPanelItem} tab The tab being changed to
41556          */
41557         "beforetabchange" : true
41558     });
41559
41560     Roo.EventManager.onWindowResize(this.onResize, this);
41561     this.cpad = this.el.getPadding("lr");
41562     this.hiddenCount = 0;
41563
41564
41565     // toolbar on the tabbar support...
41566     if (this.toolbar) {
41567         alert("no toolbar support yet");
41568         this.toolbar  = false;
41569         /*
41570         var tcfg = this.toolbar;
41571         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41572         this.toolbar = new Roo.Toolbar(tcfg);
41573         if (Roo.isSafari) {
41574             var tbl = tcfg.container.child('table', true);
41575             tbl.setAttribute('width', '100%');
41576         }
41577         */
41578         
41579     }
41580    
41581
41582
41583     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41584 };
41585
41586 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41587     /*
41588      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41589      */
41590     tabPosition : "top",
41591     /*
41592      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41593      */
41594     currentTabWidth : 0,
41595     /*
41596      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41597      */
41598     minTabWidth : 40,
41599     /*
41600      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41601      */
41602     maxTabWidth : 250,
41603     /*
41604      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41605      */
41606     preferredTabWidth : 175,
41607     /*
41608      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41609      */
41610     resizeTabs : false,
41611     /*
41612      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41613      */
41614     monitorResize : true,
41615     /*
41616      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41617      */
41618     toolbar : false,  // set by caller..
41619     
41620     region : false, /// set by caller
41621     
41622     disableTooltips : true, // not used yet...
41623
41624     /**
41625      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41626      * @param {String} id The id of the div to use <b>or create</b>
41627      * @param {String} text The text for the tab
41628      * @param {String} content (optional) Content to put in the TabPanelItem body
41629      * @param {Boolean} closable (optional) True to create a close icon on the tab
41630      * @return {Roo.TabPanelItem} The created TabPanelItem
41631      */
41632     addTab : function(id, text, content, closable, tpl)
41633     {
41634         var item = new Roo.bootstrap.panel.TabItem({
41635             panel: this,
41636             id : id,
41637             text : text,
41638             closable : closable,
41639             tpl : tpl
41640         });
41641         this.addTabItem(item);
41642         if(content){
41643             item.setContent(content);
41644         }
41645         return item;
41646     },
41647
41648     /**
41649      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41650      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41651      * @return {Roo.TabPanelItem}
41652      */
41653     getTab : function(id){
41654         return this.items[id];
41655     },
41656
41657     /**
41658      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41659      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41660      */
41661     hideTab : function(id){
41662         var t = this.items[id];
41663         if(!t.isHidden()){
41664            t.setHidden(true);
41665            this.hiddenCount++;
41666            this.autoSizeTabs();
41667         }
41668     },
41669
41670     /**
41671      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41672      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41673      */
41674     unhideTab : function(id){
41675         var t = this.items[id];
41676         if(t.isHidden()){
41677            t.setHidden(false);
41678            this.hiddenCount--;
41679            this.autoSizeTabs();
41680         }
41681     },
41682
41683     /**
41684      * Adds an existing {@link Roo.TabPanelItem}.
41685      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41686      */
41687     addTabItem : function(item)
41688     {
41689         this.items[item.id] = item;
41690         this.items.push(item);
41691         this.autoSizeTabs();
41692       //  if(this.resizeTabs){
41693     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41694   //         this.autoSizeTabs();
41695 //        }else{
41696 //            item.autoSize();
41697        // }
41698     },
41699
41700     /**
41701      * Removes a {@link Roo.TabPanelItem}.
41702      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41703      */
41704     removeTab : function(id){
41705         var items = this.items;
41706         var tab = items[id];
41707         if(!tab) { return; }
41708         var index = items.indexOf(tab);
41709         if(this.active == tab && items.length > 1){
41710             var newTab = this.getNextAvailable(index);
41711             if(newTab) {
41712                 newTab.activate();
41713             }
41714         }
41715         this.stripEl.dom.removeChild(tab.pnode.dom);
41716         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41717             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41718         }
41719         items.splice(index, 1);
41720         delete this.items[tab.id];
41721         tab.fireEvent("close", tab);
41722         tab.purgeListeners();
41723         this.autoSizeTabs();
41724     },
41725
41726     getNextAvailable : function(start){
41727         var items = this.items;
41728         var index = start;
41729         // look for a next tab that will slide over to
41730         // replace the one being removed
41731         while(index < items.length){
41732             var item = items[++index];
41733             if(item && !item.isHidden()){
41734                 return item;
41735             }
41736         }
41737         // if one isn't found select the previous tab (on the left)
41738         index = start;
41739         while(index >= 0){
41740             var item = items[--index];
41741             if(item && !item.isHidden()){
41742                 return item;
41743             }
41744         }
41745         return null;
41746     },
41747
41748     /**
41749      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41750      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41751      */
41752     disableTab : function(id){
41753         var tab = this.items[id];
41754         if(tab && this.active != tab){
41755             tab.disable();
41756         }
41757     },
41758
41759     /**
41760      * Enables a {@link Roo.TabPanelItem} that is disabled.
41761      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41762      */
41763     enableTab : function(id){
41764         var tab = this.items[id];
41765         tab.enable();
41766     },
41767
41768     /**
41769      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41770      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41771      * @return {Roo.TabPanelItem} The TabPanelItem.
41772      */
41773     activate : function(id)
41774     {
41775         //Roo.log('activite:'  + id);
41776         
41777         var tab = this.items[id];
41778         if(!tab){
41779             return null;
41780         }
41781         if(tab == this.active || tab.disabled){
41782             return tab;
41783         }
41784         var e = {};
41785         this.fireEvent("beforetabchange", this, e, tab);
41786         if(e.cancel !== true && !tab.disabled){
41787             if(this.active){
41788                 this.active.hide();
41789             }
41790             this.active = this.items[id];
41791             this.active.show();
41792             this.fireEvent("tabchange", this, this.active);
41793         }
41794         return tab;
41795     },
41796
41797     /**
41798      * Gets the active {@link Roo.TabPanelItem}.
41799      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41800      */
41801     getActiveTab : function(){
41802         return this.active;
41803     },
41804
41805     /**
41806      * Updates the tab body element to fit the height of the container element
41807      * for overflow scrolling
41808      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41809      */
41810     syncHeight : function(targetHeight){
41811         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41812         var bm = this.bodyEl.getMargins();
41813         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41814         this.bodyEl.setHeight(newHeight);
41815         return newHeight;
41816     },
41817
41818     onResize : function(){
41819         if(this.monitorResize){
41820             this.autoSizeTabs();
41821         }
41822     },
41823
41824     /**
41825      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41826      */
41827     beginUpdate : function(){
41828         this.updating = true;
41829     },
41830
41831     /**
41832      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41833      */
41834     endUpdate : function(){
41835         this.updating = false;
41836         this.autoSizeTabs();
41837     },
41838
41839     /**
41840      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41841      */
41842     autoSizeTabs : function()
41843     {
41844         var count = this.items.length;
41845         var vcount = count - this.hiddenCount;
41846         
41847         if (vcount < 2) {
41848             this.stripEl.hide();
41849         } else {
41850             this.stripEl.show();
41851         }
41852         
41853         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41854             return;
41855         }
41856         
41857         
41858         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41859         var availWidth = Math.floor(w / vcount);
41860         var b = this.stripBody;
41861         if(b.getWidth() > w){
41862             var tabs = this.items;
41863             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41864             if(availWidth < this.minTabWidth){
41865                 /*if(!this.sleft){    // incomplete scrolling code
41866                     this.createScrollButtons();
41867                 }
41868                 this.showScroll();
41869                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41870             }
41871         }else{
41872             if(this.currentTabWidth < this.preferredTabWidth){
41873                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41874             }
41875         }
41876     },
41877
41878     /**
41879      * Returns the number of tabs in this TabPanel.
41880      * @return {Number}
41881      */
41882      getCount : function(){
41883          return this.items.length;
41884      },
41885
41886     /**
41887      * Resizes all the tabs to the passed width
41888      * @param {Number} The new width
41889      */
41890     setTabWidth : function(width){
41891         this.currentTabWidth = width;
41892         for(var i = 0, len = this.items.length; i < len; i++) {
41893                 if(!this.items[i].isHidden()) {
41894                 this.items[i].setWidth(width);
41895             }
41896         }
41897     },
41898
41899     /**
41900      * Destroys this TabPanel
41901      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41902      */
41903     destroy : function(removeEl){
41904         Roo.EventManager.removeResizeListener(this.onResize, this);
41905         for(var i = 0, len = this.items.length; i < len; i++){
41906             this.items[i].purgeListeners();
41907         }
41908         if(removeEl === true){
41909             this.el.update("");
41910             this.el.remove();
41911         }
41912     },
41913     
41914     createStrip : function(container)
41915     {
41916         var strip = document.createElement("nav");
41917         strip.className = Roo.bootstrap.version == 4 ?
41918             "navbar-light bg-light" : 
41919             "navbar navbar-default"; //"x-tabs-wrap";
41920         container.appendChild(strip);
41921         return strip;
41922     },
41923     
41924     createStripList : function(strip)
41925     {
41926         // div wrapper for retard IE
41927         // returns the "tr" element.
41928         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41929         //'<div class="x-tabs-strip-wrap">'+
41930           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41931           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41932         return strip.firstChild; //.firstChild.firstChild.firstChild;
41933     },
41934     createBody : function(container)
41935     {
41936         var body = document.createElement("div");
41937         Roo.id(body, "tab-body");
41938         //Roo.fly(body).addClass("x-tabs-body");
41939         Roo.fly(body).addClass("tab-content");
41940         container.appendChild(body);
41941         return body;
41942     },
41943     createItemBody :function(bodyEl, id){
41944         var body = Roo.getDom(id);
41945         if(!body){
41946             body = document.createElement("div");
41947             body.id = id;
41948         }
41949         //Roo.fly(body).addClass("x-tabs-item-body");
41950         Roo.fly(body).addClass("tab-pane");
41951          bodyEl.insertBefore(body, bodyEl.firstChild);
41952         return body;
41953     },
41954     /** @private */
41955     createStripElements :  function(stripEl, text, closable, tpl)
41956     {
41957         var td = document.createElement("li"); // was td..
41958         td.className = 'nav-item';
41959         
41960         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41961         
41962         
41963         stripEl.appendChild(td);
41964         /*if(closable){
41965             td.className = "x-tabs-closable";
41966             if(!this.closeTpl){
41967                 this.closeTpl = new Roo.Template(
41968                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41969                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41970                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41971                 );
41972             }
41973             var el = this.closeTpl.overwrite(td, {"text": text});
41974             var close = el.getElementsByTagName("div")[0];
41975             var inner = el.getElementsByTagName("em")[0];
41976             return {"el": el, "close": close, "inner": inner};
41977         } else {
41978         */
41979         // not sure what this is..
41980 //            if(!this.tabTpl){
41981                 //this.tabTpl = new Roo.Template(
41982                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41983                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41984                 //);
41985 //                this.tabTpl = new Roo.Template(
41986 //                   '<a href="#">' +
41987 //                   '<span unselectable="on"' +
41988 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41989 //                            ' >{text}</span></a>'
41990 //                );
41991 //                
41992 //            }
41993
41994
41995             var template = tpl || this.tabTpl || false;
41996             
41997             if(!template){
41998                 template =  new Roo.Template(
41999                         Roo.bootstrap.version == 4 ? 
42000                             (
42001                                 '<a class="nav-link" href="#" unselectable="on"' +
42002                                      (this.disableTooltips ? '' : ' title="{text}"') +
42003                                      ' >{text}</a>'
42004                             ) : (
42005                                 '<a class="nav-link" href="#">' +
42006                                 '<span unselectable="on"' +
42007                                          (this.disableTooltips ? '' : ' title="{text}"') +
42008                                     ' >{text}</span></a>'
42009                             )
42010                 );
42011             }
42012             
42013             switch (typeof(template)) {
42014                 case 'object' :
42015                     break;
42016                 case 'string' :
42017                     template = new Roo.Template(template);
42018                     break;
42019                 default :
42020                     break;
42021             }
42022             
42023             var el = template.overwrite(td, {"text": text});
42024             
42025             var inner = el.getElementsByTagName("span")[0];
42026             
42027             return {"el": el, "inner": inner};
42028             
42029     }
42030         
42031     
42032 });
42033
42034 /**
42035  * @class Roo.TabPanelItem
42036  * @extends Roo.util.Observable
42037  * Represents an individual item (tab plus body) in a TabPanel.
42038  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42039  * @param {String} id The id of this TabPanelItem
42040  * @param {String} text The text for the tab of this TabPanelItem
42041  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42042  */
42043 Roo.bootstrap.panel.TabItem = function(config){
42044     /**
42045      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42046      * @type Roo.TabPanel
42047      */
42048     this.tabPanel = config.panel;
42049     /**
42050      * The id for this TabPanelItem
42051      * @type String
42052      */
42053     this.id = config.id;
42054     /** @private */
42055     this.disabled = false;
42056     /** @private */
42057     this.text = config.text;
42058     /** @private */
42059     this.loaded = false;
42060     this.closable = config.closable;
42061
42062     /**
42063      * The body element for this TabPanelItem.
42064      * @type Roo.Element
42065      */
42066     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42067     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42068     this.bodyEl.setStyle("display", "block");
42069     this.bodyEl.setStyle("zoom", "1");
42070     //this.hideAction();
42071
42072     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42073     /** @private */
42074     this.el = Roo.get(els.el);
42075     this.inner = Roo.get(els.inner, true);
42076      this.textEl = Roo.bootstrap.version == 4 ?
42077         this.el : Roo.get(this.el.dom.firstChild, true);
42078
42079     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42080     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42081
42082     
42083 //    this.el.on("mousedown", this.onTabMouseDown, this);
42084     this.el.on("click", this.onTabClick, this);
42085     /** @private */
42086     if(config.closable){
42087         var c = Roo.get(els.close, true);
42088         c.dom.title = this.closeText;
42089         c.addClassOnOver("close-over");
42090         c.on("click", this.closeClick, this);
42091      }
42092
42093     this.addEvents({
42094          /**
42095          * @event activate
42096          * Fires when this tab becomes the active tab.
42097          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42098          * @param {Roo.TabPanelItem} this
42099          */
42100         "activate": true,
42101         /**
42102          * @event beforeclose
42103          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42104          * @param {Roo.TabPanelItem} this
42105          * @param {Object} e Set cancel to true on this object to cancel the close.
42106          */
42107         "beforeclose": true,
42108         /**
42109          * @event close
42110          * Fires when this tab is closed.
42111          * @param {Roo.TabPanelItem} this
42112          */
42113          "close": true,
42114         /**
42115          * @event deactivate
42116          * Fires when this tab is no longer the active tab.
42117          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42118          * @param {Roo.TabPanelItem} this
42119          */
42120          "deactivate" : true
42121     });
42122     this.hidden = false;
42123
42124     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42125 };
42126
42127 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42128            {
42129     purgeListeners : function(){
42130        Roo.util.Observable.prototype.purgeListeners.call(this);
42131        this.el.removeAllListeners();
42132     },
42133     /**
42134      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42135      */
42136     show : function(){
42137         this.status_node.addClass("active");
42138         this.showAction();
42139         if(Roo.isOpera){
42140             this.tabPanel.stripWrap.repaint();
42141         }
42142         this.fireEvent("activate", this.tabPanel, this);
42143     },
42144
42145     /**
42146      * Returns true if this tab is the active tab.
42147      * @return {Boolean}
42148      */
42149     isActive : function(){
42150         return this.tabPanel.getActiveTab() == this;
42151     },
42152
42153     /**
42154      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42155      */
42156     hide : function(){
42157         this.status_node.removeClass("active");
42158         this.hideAction();
42159         this.fireEvent("deactivate", this.tabPanel, this);
42160     },
42161
42162     hideAction : function(){
42163         this.bodyEl.hide();
42164         this.bodyEl.setStyle("position", "absolute");
42165         this.bodyEl.setLeft("-20000px");
42166         this.bodyEl.setTop("-20000px");
42167     },
42168
42169     showAction : function(){
42170         this.bodyEl.setStyle("position", "relative");
42171         this.bodyEl.setTop("");
42172         this.bodyEl.setLeft("");
42173         this.bodyEl.show();
42174     },
42175
42176     /**
42177      * Set the tooltip for the tab.
42178      * @param {String} tooltip The tab's tooltip
42179      */
42180     setTooltip : function(text){
42181         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42182             this.textEl.dom.qtip = text;
42183             this.textEl.dom.removeAttribute('title');
42184         }else{
42185             this.textEl.dom.title = text;
42186         }
42187     },
42188
42189     onTabClick : function(e){
42190         e.preventDefault();
42191         this.tabPanel.activate(this.id);
42192     },
42193
42194     onTabMouseDown : function(e){
42195         e.preventDefault();
42196         this.tabPanel.activate(this.id);
42197     },
42198 /*
42199     getWidth : function(){
42200         return this.inner.getWidth();
42201     },
42202
42203     setWidth : function(width){
42204         var iwidth = width - this.linode.getPadding("lr");
42205         this.inner.setWidth(iwidth);
42206         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42207         this.linode.setWidth(width);
42208     },
42209 */
42210     /**
42211      * Show or hide the tab
42212      * @param {Boolean} hidden True to hide or false to show.
42213      */
42214     setHidden : function(hidden){
42215         this.hidden = hidden;
42216         this.linode.setStyle("display", hidden ? "none" : "");
42217     },
42218
42219     /**
42220      * Returns true if this tab is "hidden"
42221      * @return {Boolean}
42222      */
42223     isHidden : function(){
42224         return this.hidden;
42225     },
42226
42227     /**
42228      * Returns the text for this tab
42229      * @return {String}
42230      */
42231     getText : function(){
42232         return this.text;
42233     },
42234     /*
42235     autoSize : function(){
42236         //this.el.beginMeasure();
42237         this.textEl.setWidth(1);
42238         /*
42239          *  #2804 [new] Tabs in Roojs
42240          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42241          */
42242         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42243         //this.el.endMeasure();
42244     //},
42245
42246     /**
42247      * Sets the text for the tab (Note: this also sets the tooltip text)
42248      * @param {String} text The tab's text and tooltip
42249      */
42250     setText : function(text){
42251         this.text = text;
42252         this.textEl.update(text);
42253         this.setTooltip(text);
42254         //if(!this.tabPanel.resizeTabs){
42255         //    this.autoSize();
42256         //}
42257     },
42258     /**
42259      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42260      */
42261     activate : function(){
42262         this.tabPanel.activate(this.id);
42263     },
42264
42265     /**
42266      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42267      */
42268     disable : function(){
42269         if(this.tabPanel.active != this){
42270             this.disabled = true;
42271             this.status_node.addClass("disabled");
42272         }
42273     },
42274
42275     /**
42276      * Enables this TabPanelItem if it was previously disabled.
42277      */
42278     enable : function(){
42279         this.disabled = false;
42280         this.status_node.removeClass("disabled");
42281     },
42282
42283     /**
42284      * Sets the content for this TabPanelItem.
42285      * @param {String} content The content
42286      * @param {Boolean} loadScripts true to look for and load scripts
42287      */
42288     setContent : function(content, loadScripts){
42289         this.bodyEl.update(content, loadScripts);
42290     },
42291
42292     /**
42293      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42294      * @return {Roo.UpdateManager} The UpdateManager
42295      */
42296     getUpdateManager : function(){
42297         return this.bodyEl.getUpdateManager();
42298     },
42299
42300     /**
42301      * Set a URL to be used to load the content for this TabPanelItem.
42302      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42303      * @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)
42304      * @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)
42305      * @return {Roo.UpdateManager} The UpdateManager
42306      */
42307     setUrl : function(url, params, loadOnce){
42308         if(this.refreshDelegate){
42309             this.un('activate', this.refreshDelegate);
42310         }
42311         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42312         this.on("activate", this.refreshDelegate);
42313         return this.bodyEl.getUpdateManager();
42314     },
42315
42316     /** @private */
42317     _handleRefresh : function(url, params, loadOnce){
42318         if(!loadOnce || !this.loaded){
42319             var updater = this.bodyEl.getUpdateManager();
42320             updater.update(url, params, this._setLoaded.createDelegate(this));
42321         }
42322     },
42323
42324     /**
42325      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42326      *   Will fail silently if the setUrl method has not been called.
42327      *   This does not activate the panel, just updates its content.
42328      */
42329     refresh : function(){
42330         if(this.refreshDelegate){
42331            this.loaded = false;
42332            this.refreshDelegate();
42333         }
42334     },
42335
42336     /** @private */
42337     _setLoaded : function(){
42338         this.loaded = true;
42339     },
42340
42341     /** @private */
42342     closeClick : function(e){
42343         var o = {};
42344         e.stopEvent();
42345         this.fireEvent("beforeclose", this, o);
42346         if(o.cancel !== true){
42347             this.tabPanel.removeTab(this.id);
42348         }
42349     },
42350     /**
42351      * The text displayed in the tooltip for the close icon.
42352      * @type String
42353      */
42354     closeText : "Close this tab"
42355 });
42356 /**
42357 *    This script refer to:
42358 *    Title: International Telephone Input
42359 *    Author: Jack O'Connor
42360 *    Code version:  v12.1.12
42361 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42362 **/
42363
42364 Roo.bootstrap.PhoneInputData = function() {
42365     var d = [
42366       [
42367         "Afghanistan (‫افغانستان‬‎)",
42368         "af",
42369         "93"
42370       ],
42371       [
42372         "Albania (Shqipëri)",
42373         "al",
42374         "355"
42375       ],
42376       [
42377         "Algeria (‫الجزائر‬‎)",
42378         "dz",
42379         "213"
42380       ],
42381       [
42382         "American Samoa",
42383         "as",
42384         "1684"
42385       ],
42386       [
42387         "Andorra",
42388         "ad",
42389         "376"
42390       ],
42391       [
42392         "Angola",
42393         "ao",
42394         "244"
42395       ],
42396       [
42397         "Anguilla",
42398         "ai",
42399         "1264"
42400       ],
42401       [
42402         "Antigua and Barbuda",
42403         "ag",
42404         "1268"
42405       ],
42406       [
42407         "Argentina",
42408         "ar",
42409         "54"
42410       ],
42411       [
42412         "Armenia (Հայաստան)",
42413         "am",
42414         "374"
42415       ],
42416       [
42417         "Aruba",
42418         "aw",
42419         "297"
42420       ],
42421       [
42422         "Australia",
42423         "au",
42424         "61",
42425         0
42426       ],
42427       [
42428         "Austria (Österreich)",
42429         "at",
42430         "43"
42431       ],
42432       [
42433         "Azerbaijan (Azərbaycan)",
42434         "az",
42435         "994"
42436       ],
42437       [
42438         "Bahamas",
42439         "bs",
42440         "1242"
42441       ],
42442       [
42443         "Bahrain (‫البحرين‬‎)",
42444         "bh",
42445         "973"
42446       ],
42447       [
42448         "Bangladesh (বাংলাদেশ)",
42449         "bd",
42450         "880"
42451       ],
42452       [
42453         "Barbados",
42454         "bb",
42455         "1246"
42456       ],
42457       [
42458         "Belarus (Беларусь)",
42459         "by",
42460         "375"
42461       ],
42462       [
42463         "Belgium (België)",
42464         "be",
42465         "32"
42466       ],
42467       [
42468         "Belize",
42469         "bz",
42470         "501"
42471       ],
42472       [
42473         "Benin (Bénin)",
42474         "bj",
42475         "229"
42476       ],
42477       [
42478         "Bermuda",
42479         "bm",
42480         "1441"
42481       ],
42482       [
42483         "Bhutan (འབྲུག)",
42484         "bt",
42485         "975"
42486       ],
42487       [
42488         "Bolivia",
42489         "bo",
42490         "591"
42491       ],
42492       [
42493         "Bosnia and Herzegovina (Босна и Херцеговина)",
42494         "ba",
42495         "387"
42496       ],
42497       [
42498         "Botswana",
42499         "bw",
42500         "267"
42501       ],
42502       [
42503         "Brazil (Brasil)",
42504         "br",
42505         "55"
42506       ],
42507       [
42508         "British Indian Ocean Territory",
42509         "io",
42510         "246"
42511       ],
42512       [
42513         "British Virgin Islands",
42514         "vg",
42515         "1284"
42516       ],
42517       [
42518         "Brunei",
42519         "bn",
42520         "673"
42521       ],
42522       [
42523         "Bulgaria (България)",
42524         "bg",
42525         "359"
42526       ],
42527       [
42528         "Burkina Faso",
42529         "bf",
42530         "226"
42531       ],
42532       [
42533         "Burundi (Uburundi)",
42534         "bi",
42535         "257"
42536       ],
42537       [
42538         "Cambodia (កម្ពុជា)",
42539         "kh",
42540         "855"
42541       ],
42542       [
42543         "Cameroon (Cameroun)",
42544         "cm",
42545         "237"
42546       ],
42547       [
42548         "Canada",
42549         "ca",
42550         "1",
42551         1,
42552         ["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"]
42553       ],
42554       [
42555         "Cape Verde (Kabu Verdi)",
42556         "cv",
42557         "238"
42558       ],
42559       [
42560         "Caribbean Netherlands",
42561         "bq",
42562         "599",
42563         1
42564       ],
42565       [
42566         "Cayman Islands",
42567         "ky",
42568         "1345"
42569       ],
42570       [
42571         "Central African Republic (République centrafricaine)",
42572         "cf",
42573         "236"
42574       ],
42575       [
42576         "Chad (Tchad)",
42577         "td",
42578         "235"
42579       ],
42580       [
42581         "Chile",
42582         "cl",
42583         "56"
42584       ],
42585       [
42586         "China (中国)",
42587         "cn",
42588         "86"
42589       ],
42590       [
42591         "Christmas Island",
42592         "cx",
42593         "61",
42594         2
42595       ],
42596       [
42597         "Cocos (Keeling) Islands",
42598         "cc",
42599         "61",
42600         1
42601       ],
42602       [
42603         "Colombia",
42604         "co",
42605         "57"
42606       ],
42607       [
42608         "Comoros (‫جزر القمر‬‎)",
42609         "km",
42610         "269"
42611       ],
42612       [
42613         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42614         "cd",
42615         "243"
42616       ],
42617       [
42618         "Congo (Republic) (Congo-Brazzaville)",
42619         "cg",
42620         "242"
42621       ],
42622       [
42623         "Cook Islands",
42624         "ck",
42625         "682"
42626       ],
42627       [
42628         "Costa Rica",
42629         "cr",
42630         "506"
42631       ],
42632       [
42633         "Côte d’Ivoire",
42634         "ci",
42635         "225"
42636       ],
42637       [
42638         "Croatia (Hrvatska)",
42639         "hr",
42640         "385"
42641       ],
42642       [
42643         "Cuba",
42644         "cu",
42645         "53"
42646       ],
42647       [
42648         "Curaçao",
42649         "cw",
42650         "599",
42651         0
42652       ],
42653       [
42654         "Cyprus (Κύπρος)",
42655         "cy",
42656         "357"
42657       ],
42658       [
42659         "Czech Republic (Česká republika)",
42660         "cz",
42661         "420"
42662       ],
42663       [
42664         "Denmark (Danmark)",
42665         "dk",
42666         "45"
42667       ],
42668       [
42669         "Djibouti",
42670         "dj",
42671         "253"
42672       ],
42673       [
42674         "Dominica",
42675         "dm",
42676         "1767"
42677       ],
42678       [
42679         "Dominican Republic (República Dominicana)",
42680         "do",
42681         "1",
42682         2,
42683         ["809", "829", "849"]
42684       ],
42685       [
42686         "Ecuador",
42687         "ec",
42688         "593"
42689       ],
42690       [
42691         "Egypt (‫مصر‬‎)",
42692         "eg",
42693         "20"
42694       ],
42695       [
42696         "El Salvador",
42697         "sv",
42698         "503"
42699       ],
42700       [
42701         "Equatorial Guinea (Guinea Ecuatorial)",
42702         "gq",
42703         "240"
42704       ],
42705       [
42706         "Eritrea",
42707         "er",
42708         "291"
42709       ],
42710       [
42711         "Estonia (Eesti)",
42712         "ee",
42713         "372"
42714       ],
42715       [
42716         "Ethiopia",
42717         "et",
42718         "251"
42719       ],
42720       [
42721         "Falkland Islands (Islas Malvinas)",
42722         "fk",
42723         "500"
42724       ],
42725       [
42726         "Faroe Islands (Føroyar)",
42727         "fo",
42728         "298"
42729       ],
42730       [
42731         "Fiji",
42732         "fj",
42733         "679"
42734       ],
42735       [
42736         "Finland (Suomi)",
42737         "fi",
42738         "358",
42739         0
42740       ],
42741       [
42742         "France",
42743         "fr",
42744         "33"
42745       ],
42746       [
42747         "French Guiana (Guyane française)",
42748         "gf",
42749         "594"
42750       ],
42751       [
42752         "French Polynesia (Polynésie française)",
42753         "pf",
42754         "689"
42755       ],
42756       [
42757         "Gabon",
42758         "ga",
42759         "241"
42760       ],
42761       [
42762         "Gambia",
42763         "gm",
42764         "220"
42765       ],
42766       [
42767         "Georgia (საქართველო)",
42768         "ge",
42769         "995"
42770       ],
42771       [
42772         "Germany (Deutschland)",
42773         "de",
42774         "49"
42775       ],
42776       [
42777         "Ghana (Gaana)",
42778         "gh",
42779         "233"
42780       ],
42781       [
42782         "Gibraltar",
42783         "gi",
42784         "350"
42785       ],
42786       [
42787         "Greece (Ελλάδα)",
42788         "gr",
42789         "30"
42790       ],
42791       [
42792         "Greenland (Kalaallit Nunaat)",
42793         "gl",
42794         "299"
42795       ],
42796       [
42797         "Grenada",
42798         "gd",
42799         "1473"
42800       ],
42801       [
42802         "Guadeloupe",
42803         "gp",
42804         "590",
42805         0
42806       ],
42807       [
42808         "Guam",
42809         "gu",
42810         "1671"
42811       ],
42812       [
42813         "Guatemala",
42814         "gt",
42815         "502"
42816       ],
42817       [
42818         "Guernsey",
42819         "gg",
42820         "44",
42821         1
42822       ],
42823       [
42824         "Guinea (Guinée)",
42825         "gn",
42826         "224"
42827       ],
42828       [
42829         "Guinea-Bissau (Guiné Bissau)",
42830         "gw",
42831         "245"
42832       ],
42833       [
42834         "Guyana",
42835         "gy",
42836         "592"
42837       ],
42838       [
42839         "Haiti",
42840         "ht",
42841         "509"
42842       ],
42843       [
42844         "Honduras",
42845         "hn",
42846         "504"
42847       ],
42848       [
42849         "Hong Kong (香港)",
42850         "hk",
42851         "852"
42852       ],
42853       [
42854         "Hungary (Magyarország)",
42855         "hu",
42856         "36"
42857       ],
42858       [
42859         "Iceland (Ísland)",
42860         "is",
42861         "354"
42862       ],
42863       [
42864         "India (भारत)",
42865         "in",
42866         "91"
42867       ],
42868       [
42869         "Indonesia",
42870         "id",
42871         "62"
42872       ],
42873       [
42874         "Iran (‫ایران‬‎)",
42875         "ir",
42876         "98"
42877       ],
42878       [
42879         "Iraq (‫العراق‬‎)",
42880         "iq",
42881         "964"
42882       ],
42883       [
42884         "Ireland",
42885         "ie",
42886         "353"
42887       ],
42888       [
42889         "Isle of Man",
42890         "im",
42891         "44",
42892         2
42893       ],
42894       [
42895         "Israel (‫ישראל‬‎)",
42896         "il",
42897         "972"
42898       ],
42899       [
42900         "Italy (Italia)",
42901         "it",
42902         "39",
42903         0
42904       ],
42905       [
42906         "Jamaica",
42907         "jm",
42908         "1876"
42909       ],
42910       [
42911         "Japan (日本)",
42912         "jp",
42913         "81"
42914       ],
42915       [
42916         "Jersey",
42917         "je",
42918         "44",
42919         3
42920       ],
42921       [
42922         "Jordan (‫الأردن‬‎)",
42923         "jo",
42924         "962"
42925       ],
42926       [
42927         "Kazakhstan (Казахстан)",
42928         "kz",
42929         "7",
42930         1
42931       ],
42932       [
42933         "Kenya",
42934         "ke",
42935         "254"
42936       ],
42937       [
42938         "Kiribati",
42939         "ki",
42940         "686"
42941       ],
42942       [
42943         "Kosovo",
42944         "xk",
42945         "383"
42946       ],
42947       [
42948         "Kuwait (‫الكويت‬‎)",
42949         "kw",
42950         "965"
42951       ],
42952       [
42953         "Kyrgyzstan (Кыргызстан)",
42954         "kg",
42955         "996"
42956       ],
42957       [
42958         "Laos (ລາວ)",
42959         "la",
42960         "856"
42961       ],
42962       [
42963         "Latvia (Latvija)",
42964         "lv",
42965         "371"
42966       ],
42967       [
42968         "Lebanon (‫لبنان‬‎)",
42969         "lb",
42970         "961"
42971       ],
42972       [
42973         "Lesotho",
42974         "ls",
42975         "266"
42976       ],
42977       [
42978         "Liberia",
42979         "lr",
42980         "231"
42981       ],
42982       [
42983         "Libya (‫ليبيا‬‎)",
42984         "ly",
42985         "218"
42986       ],
42987       [
42988         "Liechtenstein",
42989         "li",
42990         "423"
42991       ],
42992       [
42993         "Lithuania (Lietuva)",
42994         "lt",
42995         "370"
42996       ],
42997       [
42998         "Luxembourg",
42999         "lu",
43000         "352"
43001       ],
43002       [
43003         "Macau (澳門)",
43004         "mo",
43005         "853"
43006       ],
43007       [
43008         "Macedonia (FYROM) (Македонија)",
43009         "mk",
43010         "389"
43011       ],
43012       [
43013         "Madagascar (Madagasikara)",
43014         "mg",
43015         "261"
43016       ],
43017       [
43018         "Malawi",
43019         "mw",
43020         "265"
43021       ],
43022       [
43023         "Malaysia",
43024         "my",
43025         "60"
43026       ],
43027       [
43028         "Maldives",
43029         "mv",
43030         "960"
43031       ],
43032       [
43033         "Mali",
43034         "ml",
43035         "223"
43036       ],
43037       [
43038         "Malta",
43039         "mt",
43040         "356"
43041       ],
43042       [
43043         "Marshall Islands",
43044         "mh",
43045         "692"
43046       ],
43047       [
43048         "Martinique",
43049         "mq",
43050         "596"
43051       ],
43052       [
43053         "Mauritania (‫موريتانيا‬‎)",
43054         "mr",
43055         "222"
43056       ],
43057       [
43058         "Mauritius (Moris)",
43059         "mu",
43060         "230"
43061       ],
43062       [
43063         "Mayotte",
43064         "yt",
43065         "262",
43066         1
43067       ],
43068       [
43069         "Mexico (México)",
43070         "mx",
43071         "52"
43072       ],
43073       [
43074         "Micronesia",
43075         "fm",
43076         "691"
43077       ],
43078       [
43079         "Moldova (Republica Moldova)",
43080         "md",
43081         "373"
43082       ],
43083       [
43084         "Monaco",
43085         "mc",
43086         "377"
43087       ],
43088       [
43089         "Mongolia (Монгол)",
43090         "mn",
43091         "976"
43092       ],
43093       [
43094         "Montenegro (Crna Gora)",
43095         "me",
43096         "382"
43097       ],
43098       [
43099         "Montserrat",
43100         "ms",
43101         "1664"
43102       ],
43103       [
43104         "Morocco (‫المغرب‬‎)",
43105         "ma",
43106         "212",
43107         0
43108       ],
43109       [
43110         "Mozambique (Moçambique)",
43111         "mz",
43112         "258"
43113       ],
43114       [
43115         "Myanmar (Burma) (မြန်မာ)",
43116         "mm",
43117         "95"
43118       ],
43119       [
43120         "Namibia (Namibië)",
43121         "na",
43122         "264"
43123       ],
43124       [
43125         "Nauru",
43126         "nr",
43127         "674"
43128       ],
43129       [
43130         "Nepal (नेपाल)",
43131         "np",
43132         "977"
43133       ],
43134       [
43135         "Netherlands (Nederland)",
43136         "nl",
43137         "31"
43138       ],
43139       [
43140         "New Caledonia (Nouvelle-Calédonie)",
43141         "nc",
43142         "687"
43143       ],
43144       [
43145         "New Zealand",
43146         "nz",
43147         "64"
43148       ],
43149       [
43150         "Nicaragua",
43151         "ni",
43152         "505"
43153       ],
43154       [
43155         "Niger (Nijar)",
43156         "ne",
43157         "227"
43158       ],
43159       [
43160         "Nigeria",
43161         "ng",
43162         "234"
43163       ],
43164       [
43165         "Niue",
43166         "nu",
43167         "683"
43168       ],
43169       [
43170         "Norfolk Island",
43171         "nf",
43172         "672"
43173       ],
43174       [
43175         "North Korea (조선 민주주의 인민 공화국)",
43176         "kp",
43177         "850"
43178       ],
43179       [
43180         "Northern Mariana Islands",
43181         "mp",
43182         "1670"
43183       ],
43184       [
43185         "Norway (Norge)",
43186         "no",
43187         "47",
43188         0
43189       ],
43190       [
43191         "Oman (‫عُمان‬‎)",
43192         "om",
43193         "968"
43194       ],
43195       [
43196         "Pakistan (‫پاکستان‬‎)",
43197         "pk",
43198         "92"
43199       ],
43200       [
43201         "Palau",
43202         "pw",
43203         "680"
43204       ],
43205       [
43206         "Palestine (‫فلسطين‬‎)",
43207         "ps",
43208         "970"
43209       ],
43210       [
43211         "Panama (Panamá)",
43212         "pa",
43213         "507"
43214       ],
43215       [
43216         "Papua New Guinea",
43217         "pg",
43218         "675"
43219       ],
43220       [
43221         "Paraguay",
43222         "py",
43223         "595"
43224       ],
43225       [
43226         "Peru (Perú)",
43227         "pe",
43228         "51"
43229       ],
43230       [
43231         "Philippines",
43232         "ph",
43233         "63"
43234       ],
43235       [
43236         "Poland (Polska)",
43237         "pl",
43238         "48"
43239       ],
43240       [
43241         "Portugal",
43242         "pt",
43243         "351"
43244       ],
43245       [
43246         "Puerto Rico",
43247         "pr",
43248         "1",
43249         3,
43250         ["787", "939"]
43251       ],
43252       [
43253         "Qatar (‫قطر‬‎)",
43254         "qa",
43255         "974"
43256       ],
43257       [
43258         "Réunion (La Réunion)",
43259         "re",
43260         "262",
43261         0
43262       ],
43263       [
43264         "Romania (România)",
43265         "ro",
43266         "40"
43267       ],
43268       [
43269         "Russia (Россия)",
43270         "ru",
43271         "7",
43272         0
43273       ],
43274       [
43275         "Rwanda",
43276         "rw",
43277         "250"
43278       ],
43279       [
43280         "Saint Barthélemy",
43281         "bl",
43282         "590",
43283         1
43284       ],
43285       [
43286         "Saint Helena",
43287         "sh",
43288         "290"
43289       ],
43290       [
43291         "Saint Kitts and Nevis",
43292         "kn",
43293         "1869"
43294       ],
43295       [
43296         "Saint Lucia",
43297         "lc",
43298         "1758"
43299       ],
43300       [
43301         "Saint Martin (Saint-Martin (partie française))",
43302         "mf",
43303         "590",
43304         2
43305       ],
43306       [
43307         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43308         "pm",
43309         "508"
43310       ],
43311       [
43312         "Saint Vincent and the Grenadines",
43313         "vc",
43314         "1784"
43315       ],
43316       [
43317         "Samoa",
43318         "ws",
43319         "685"
43320       ],
43321       [
43322         "San Marino",
43323         "sm",
43324         "378"
43325       ],
43326       [
43327         "São Tomé and Príncipe (São Tomé e Príncipe)",
43328         "st",
43329         "239"
43330       ],
43331       [
43332         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43333         "sa",
43334         "966"
43335       ],
43336       [
43337         "Senegal (Sénégal)",
43338         "sn",
43339         "221"
43340       ],
43341       [
43342         "Serbia (Србија)",
43343         "rs",
43344         "381"
43345       ],
43346       [
43347         "Seychelles",
43348         "sc",
43349         "248"
43350       ],
43351       [
43352         "Sierra Leone",
43353         "sl",
43354         "232"
43355       ],
43356       [
43357         "Singapore",
43358         "sg",
43359         "65"
43360       ],
43361       [
43362         "Sint Maarten",
43363         "sx",
43364         "1721"
43365       ],
43366       [
43367         "Slovakia (Slovensko)",
43368         "sk",
43369         "421"
43370       ],
43371       [
43372         "Slovenia (Slovenija)",
43373         "si",
43374         "386"
43375       ],
43376       [
43377         "Solomon Islands",
43378         "sb",
43379         "677"
43380       ],
43381       [
43382         "Somalia (Soomaaliya)",
43383         "so",
43384         "252"
43385       ],
43386       [
43387         "South Africa",
43388         "za",
43389         "27"
43390       ],
43391       [
43392         "South Korea (대한민국)",
43393         "kr",
43394         "82"
43395       ],
43396       [
43397         "South Sudan (‫جنوب السودان‬‎)",
43398         "ss",
43399         "211"
43400       ],
43401       [
43402         "Spain (España)",
43403         "es",
43404         "34"
43405       ],
43406       [
43407         "Sri Lanka (ශ්‍රී ලංකාව)",
43408         "lk",
43409         "94"
43410       ],
43411       [
43412         "Sudan (‫السودان‬‎)",
43413         "sd",
43414         "249"
43415       ],
43416       [
43417         "Suriname",
43418         "sr",
43419         "597"
43420       ],
43421       [
43422         "Svalbard and Jan Mayen",
43423         "sj",
43424         "47",
43425         1
43426       ],
43427       [
43428         "Swaziland",
43429         "sz",
43430         "268"
43431       ],
43432       [
43433         "Sweden (Sverige)",
43434         "se",
43435         "46"
43436       ],
43437       [
43438         "Switzerland (Schweiz)",
43439         "ch",
43440         "41"
43441       ],
43442       [
43443         "Syria (‫سوريا‬‎)",
43444         "sy",
43445         "963"
43446       ],
43447       [
43448         "Taiwan (台灣)",
43449         "tw",
43450         "886"
43451       ],
43452       [
43453         "Tajikistan",
43454         "tj",
43455         "992"
43456       ],
43457       [
43458         "Tanzania",
43459         "tz",
43460         "255"
43461       ],
43462       [
43463         "Thailand (ไทย)",
43464         "th",
43465         "66"
43466       ],
43467       [
43468         "Timor-Leste",
43469         "tl",
43470         "670"
43471       ],
43472       [
43473         "Togo",
43474         "tg",
43475         "228"
43476       ],
43477       [
43478         "Tokelau",
43479         "tk",
43480         "690"
43481       ],
43482       [
43483         "Tonga",
43484         "to",
43485         "676"
43486       ],
43487       [
43488         "Trinidad and Tobago",
43489         "tt",
43490         "1868"
43491       ],
43492       [
43493         "Tunisia (‫تونس‬‎)",
43494         "tn",
43495         "216"
43496       ],
43497       [
43498         "Turkey (Türkiye)",
43499         "tr",
43500         "90"
43501       ],
43502       [
43503         "Turkmenistan",
43504         "tm",
43505         "993"
43506       ],
43507       [
43508         "Turks and Caicos Islands",
43509         "tc",
43510         "1649"
43511       ],
43512       [
43513         "Tuvalu",
43514         "tv",
43515         "688"
43516       ],
43517       [
43518         "U.S. Virgin Islands",
43519         "vi",
43520         "1340"
43521       ],
43522       [
43523         "Uganda",
43524         "ug",
43525         "256"
43526       ],
43527       [
43528         "Ukraine (Україна)",
43529         "ua",
43530         "380"
43531       ],
43532       [
43533         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43534         "ae",
43535         "971"
43536       ],
43537       [
43538         "United Kingdom",
43539         "gb",
43540         "44",
43541         0
43542       ],
43543       [
43544         "United States",
43545         "us",
43546         "1",
43547         0
43548       ],
43549       [
43550         "Uruguay",
43551         "uy",
43552         "598"
43553       ],
43554       [
43555         "Uzbekistan (Oʻzbekiston)",
43556         "uz",
43557         "998"
43558       ],
43559       [
43560         "Vanuatu",
43561         "vu",
43562         "678"
43563       ],
43564       [
43565         "Vatican City (Città del Vaticano)",
43566         "va",
43567         "39",
43568         1
43569       ],
43570       [
43571         "Venezuela",
43572         "ve",
43573         "58"
43574       ],
43575       [
43576         "Vietnam (Việt Nam)",
43577         "vn",
43578         "84"
43579       ],
43580       [
43581         "Wallis and Futuna (Wallis-et-Futuna)",
43582         "wf",
43583         "681"
43584       ],
43585       [
43586         "Western Sahara (‫الصحراء الغربية‬‎)",
43587         "eh",
43588         "212",
43589         1
43590       ],
43591       [
43592         "Yemen (‫اليمن‬‎)",
43593         "ye",
43594         "967"
43595       ],
43596       [
43597         "Zambia",
43598         "zm",
43599         "260"
43600       ],
43601       [
43602         "Zimbabwe",
43603         "zw",
43604         "263"
43605       ],
43606       [
43607         "Åland Islands",
43608         "ax",
43609         "358",
43610         1
43611       ]
43612   ];
43613   
43614   return d;
43615 }/**
43616 *    This script refer to:
43617 *    Title: International Telephone Input
43618 *    Author: Jack O'Connor
43619 *    Code version:  v12.1.12
43620 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43621 **/
43622
43623 /**
43624  * @class Roo.bootstrap.PhoneInput
43625  * @extends Roo.bootstrap.TriggerField
43626  * An input with International dial-code selection
43627  
43628  * @cfg {String} defaultDialCode default '+852'
43629  * @cfg {Array} preferedCountries default []
43630   
43631  * @constructor
43632  * Create a new PhoneInput.
43633  * @param {Object} config Configuration options
43634  */
43635
43636 Roo.bootstrap.PhoneInput = function(config) {
43637     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43638 };
43639
43640 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43641         /**
43642         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43643         */
43644         listWidth: undefined,
43645         
43646         selectedClass: 'active',
43647         
43648         invalidClass : "has-warning",
43649         
43650         validClass: 'has-success',
43651         
43652         allowed: '0123456789',
43653         
43654         max_length: 15,
43655         
43656         /**
43657          * @cfg {String} defaultDialCode The default dial code when initializing the input
43658          */
43659         defaultDialCode: '+852',
43660         
43661         /**
43662          * @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
43663          */
43664         preferedCountries: false,
43665         
43666         getAutoCreate : function()
43667         {
43668             var data = Roo.bootstrap.PhoneInputData();
43669             var align = this.labelAlign || this.parentLabelAlign();
43670             var id = Roo.id();
43671             
43672             this.allCountries = [];
43673             this.dialCodeMapping = [];
43674             
43675             for (var i = 0; i < data.length; i++) {
43676               var c = data[i];
43677               this.allCountries[i] = {
43678                 name: c[0],
43679                 iso2: c[1],
43680                 dialCode: c[2],
43681                 priority: c[3] || 0,
43682                 areaCodes: c[4] || null
43683               };
43684               this.dialCodeMapping[c[2]] = {
43685                   name: c[0],
43686                   iso2: c[1],
43687                   priority: c[3] || 0,
43688                   areaCodes: c[4] || null
43689               };
43690             }
43691             
43692             var cfg = {
43693                 cls: 'form-group',
43694                 cn: []
43695             };
43696             
43697             var input =  {
43698                 tag: 'input',
43699                 id : id,
43700                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43701                 maxlength: this.max_length,
43702                 cls : 'form-control tel-input',
43703                 autocomplete: 'new-password'
43704             };
43705             
43706             var hiddenInput = {
43707                 tag: 'input',
43708                 type: 'hidden',
43709                 cls: 'hidden-tel-input'
43710             };
43711             
43712             if (this.name) {
43713                 hiddenInput.name = this.name;
43714             }
43715             
43716             if (this.disabled) {
43717                 input.disabled = true;
43718             }
43719             
43720             var flag_container = {
43721                 tag: 'div',
43722                 cls: 'flag-box',
43723                 cn: [
43724                     {
43725                         tag: 'div',
43726                         cls: 'flag'
43727                     },
43728                     {
43729                         tag: 'div',
43730                         cls: 'caret'
43731                     }
43732                 ]
43733             };
43734             
43735             var box = {
43736                 tag: 'div',
43737                 cls: this.hasFeedback ? 'has-feedback' : '',
43738                 cn: [
43739                     hiddenInput,
43740                     input,
43741                     {
43742                         tag: 'input',
43743                         cls: 'dial-code-holder',
43744                         disabled: true
43745                     }
43746                 ]
43747             };
43748             
43749             var container = {
43750                 cls: 'roo-select2-container input-group',
43751                 cn: [
43752                     flag_container,
43753                     box
43754                 ]
43755             };
43756             
43757             if (this.fieldLabel.length) {
43758                 var indicator = {
43759                     tag: 'i',
43760                     tooltip: 'This field is required'
43761                 };
43762                 
43763                 var label = {
43764                     tag: 'label',
43765                     'for':  id,
43766                     cls: 'control-label',
43767                     cn: []
43768                 };
43769                 
43770                 var label_text = {
43771                     tag: 'span',
43772                     html: this.fieldLabel
43773                 };
43774                 
43775                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43776                 label.cn = [
43777                     indicator,
43778                     label_text
43779                 ];
43780                 
43781                 if(this.indicatorpos == 'right') {
43782                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43783                     label.cn = [
43784                         label_text,
43785                         indicator
43786                     ];
43787                 }
43788                 
43789                 if(align == 'left') {
43790                     container = {
43791                         tag: 'div',
43792                         cn: [
43793                             container
43794                         ]
43795                     };
43796                     
43797                     if(this.labelWidth > 12){
43798                         label.style = "width: " + this.labelWidth + 'px';
43799                     }
43800                     if(this.labelWidth < 13 && this.labelmd == 0){
43801                         this.labelmd = this.labelWidth;
43802                     }
43803                     if(this.labellg > 0){
43804                         label.cls += ' col-lg-' + this.labellg;
43805                         input.cls += ' col-lg-' + (12 - this.labellg);
43806                     }
43807                     if(this.labelmd > 0){
43808                         label.cls += ' col-md-' + this.labelmd;
43809                         container.cls += ' col-md-' + (12 - this.labelmd);
43810                     }
43811                     if(this.labelsm > 0){
43812                         label.cls += ' col-sm-' + this.labelsm;
43813                         container.cls += ' col-sm-' + (12 - this.labelsm);
43814                     }
43815                     if(this.labelxs > 0){
43816                         label.cls += ' col-xs-' + this.labelxs;
43817                         container.cls += ' col-xs-' + (12 - this.labelxs);
43818                     }
43819                 }
43820             }
43821             
43822             cfg.cn = [
43823                 label,
43824                 container
43825             ];
43826             
43827             var settings = this;
43828             
43829             ['xs','sm','md','lg'].map(function(size){
43830                 if (settings[size]) {
43831                     cfg.cls += ' col-' + size + '-' + settings[size];
43832                 }
43833             });
43834             
43835             this.store = new Roo.data.Store({
43836                 proxy : new Roo.data.MemoryProxy({}),
43837                 reader : new Roo.data.JsonReader({
43838                     fields : [
43839                         {
43840                             'name' : 'name',
43841                             'type' : 'string'
43842                         },
43843                         {
43844                             'name' : 'iso2',
43845                             'type' : 'string'
43846                         },
43847                         {
43848                             'name' : 'dialCode',
43849                             'type' : 'string'
43850                         },
43851                         {
43852                             'name' : 'priority',
43853                             'type' : 'string'
43854                         },
43855                         {
43856                             'name' : 'areaCodes',
43857                             'type' : 'string'
43858                         }
43859                     ]
43860                 })
43861             });
43862             
43863             if(!this.preferedCountries) {
43864                 this.preferedCountries = [
43865                     'hk',
43866                     'gb',
43867                     'us'
43868                 ];
43869             }
43870             
43871             var p = this.preferedCountries.reverse();
43872             
43873             if(p) {
43874                 for (var i = 0; i < p.length; i++) {
43875                     for (var j = 0; j < this.allCountries.length; j++) {
43876                         if(this.allCountries[j].iso2 == p[i]) {
43877                             var t = this.allCountries[j];
43878                             this.allCountries.splice(j,1);
43879                             this.allCountries.unshift(t);
43880                         }
43881                     } 
43882                 }
43883             }
43884             
43885             this.store.proxy.data = {
43886                 success: true,
43887                 data: this.allCountries
43888             };
43889             
43890             return cfg;
43891         },
43892         
43893         initEvents : function()
43894         {
43895             this.createList();
43896             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43897             
43898             this.indicator = this.indicatorEl();
43899             this.flag = this.flagEl();
43900             this.dialCodeHolder = this.dialCodeHolderEl();
43901             
43902             this.trigger = this.el.select('div.flag-box',true).first();
43903             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43904             
43905             var _this = this;
43906             
43907             (function(){
43908                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43909                 _this.list.setWidth(lw);
43910             }).defer(100);
43911             
43912             this.list.on('mouseover', this.onViewOver, this);
43913             this.list.on('mousemove', this.onViewMove, this);
43914             this.inputEl().on("keyup", this.onKeyUp, this);
43915             this.inputEl().on("keypress", this.onKeyPress, this);
43916             
43917             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43918
43919             this.view = new Roo.View(this.list, this.tpl, {
43920                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43921             });
43922             
43923             this.view.on('click', this.onViewClick, this);
43924             this.setValue(this.defaultDialCode);
43925         },
43926         
43927         onTriggerClick : function(e)
43928         {
43929             Roo.log('trigger click');
43930             if(this.disabled){
43931                 return;
43932             }
43933             
43934             if(this.isExpanded()){
43935                 this.collapse();
43936                 this.hasFocus = false;
43937             }else {
43938                 this.store.load({});
43939                 this.hasFocus = true;
43940                 this.expand();
43941             }
43942         },
43943         
43944         isExpanded : function()
43945         {
43946             return this.list.isVisible();
43947         },
43948         
43949         collapse : function()
43950         {
43951             if(!this.isExpanded()){
43952                 return;
43953             }
43954             this.list.hide();
43955             Roo.get(document).un('mousedown', this.collapseIf, this);
43956             Roo.get(document).un('mousewheel', this.collapseIf, this);
43957             this.fireEvent('collapse', this);
43958             this.validate();
43959         },
43960         
43961         expand : function()
43962         {
43963             Roo.log('expand');
43964
43965             if(this.isExpanded() || !this.hasFocus){
43966                 return;
43967             }
43968             
43969             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43970             this.list.setWidth(lw);
43971             
43972             this.list.show();
43973             this.restrictHeight();
43974             
43975             Roo.get(document).on('mousedown', this.collapseIf, this);
43976             Roo.get(document).on('mousewheel', this.collapseIf, this);
43977             
43978             this.fireEvent('expand', this);
43979         },
43980         
43981         restrictHeight : function()
43982         {
43983             this.list.alignTo(this.inputEl(), this.listAlign);
43984             this.list.alignTo(this.inputEl(), this.listAlign);
43985         },
43986         
43987         onViewOver : function(e, t)
43988         {
43989             if(this.inKeyMode){
43990                 return;
43991             }
43992             var item = this.view.findItemFromChild(t);
43993             
43994             if(item){
43995                 var index = this.view.indexOf(item);
43996                 this.select(index, false);
43997             }
43998         },
43999
44000         // private
44001         onViewClick : function(view, doFocus, el, e)
44002         {
44003             var index = this.view.getSelectedIndexes()[0];
44004             
44005             var r = this.store.getAt(index);
44006             
44007             if(r){
44008                 this.onSelect(r, index);
44009             }
44010             if(doFocus !== false && !this.blockFocus){
44011                 this.inputEl().focus();
44012             }
44013         },
44014         
44015         onViewMove : function(e, t)
44016         {
44017             this.inKeyMode = false;
44018         },
44019         
44020         select : function(index, scrollIntoView)
44021         {
44022             this.selectedIndex = index;
44023             this.view.select(index);
44024             if(scrollIntoView !== false){
44025                 var el = this.view.getNode(index);
44026                 if(el){
44027                     this.list.scrollChildIntoView(el, false);
44028                 }
44029             }
44030         },
44031         
44032         createList : function()
44033         {
44034             this.list = Roo.get(document.body).createChild({
44035                 tag: 'ul',
44036                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44037                 style: 'display:none'
44038             });
44039             
44040             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44041         },
44042         
44043         collapseIf : function(e)
44044         {
44045             var in_combo  = e.within(this.el);
44046             var in_list =  e.within(this.list);
44047             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44048             
44049             if (in_combo || in_list || is_list) {
44050                 return;
44051             }
44052             this.collapse();
44053         },
44054         
44055         onSelect : function(record, index)
44056         {
44057             if(this.fireEvent('beforeselect', this, record, index) !== false){
44058                 
44059                 this.setFlagClass(record.data.iso2);
44060                 this.setDialCode(record.data.dialCode);
44061                 this.hasFocus = false;
44062                 this.collapse();
44063                 this.fireEvent('select', this, record, index);
44064             }
44065         },
44066         
44067         flagEl : function()
44068         {
44069             var flag = this.el.select('div.flag',true).first();
44070             if(!flag){
44071                 return false;
44072             }
44073             return flag;
44074         },
44075         
44076         dialCodeHolderEl : function()
44077         {
44078             var d = this.el.select('input.dial-code-holder',true).first();
44079             if(!d){
44080                 return false;
44081             }
44082             return d;
44083         },
44084         
44085         setDialCode : function(v)
44086         {
44087             this.dialCodeHolder.dom.value = '+'+v;
44088         },
44089         
44090         setFlagClass : function(n)
44091         {
44092             this.flag.dom.className = 'flag '+n;
44093         },
44094         
44095         getValue : function()
44096         {
44097             var v = this.inputEl().getValue();
44098             if(this.dialCodeHolder) {
44099                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44100             }
44101             return v;
44102         },
44103         
44104         setValue : function(v)
44105         {
44106             var d = this.getDialCode(v);
44107             
44108             //invalid dial code
44109             if(v.length == 0 || !d || d.length == 0) {
44110                 if(this.rendered){
44111                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44112                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44113                 }
44114                 return;
44115             }
44116             
44117             //valid dial code
44118             this.setFlagClass(this.dialCodeMapping[d].iso2);
44119             this.setDialCode(d);
44120             this.inputEl().dom.value = v.replace('+'+d,'');
44121             this.hiddenEl().dom.value = this.getValue();
44122             
44123             this.validate();
44124         },
44125         
44126         getDialCode : function(v)
44127         {
44128             v = v ||  '';
44129             
44130             if (v.length == 0) {
44131                 return this.dialCodeHolder.dom.value;
44132             }
44133             
44134             var dialCode = "";
44135             if (v.charAt(0) != "+") {
44136                 return false;
44137             }
44138             var numericChars = "";
44139             for (var i = 1; i < v.length; i++) {
44140               var c = v.charAt(i);
44141               if (!isNaN(c)) {
44142                 numericChars += c;
44143                 if (this.dialCodeMapping[numericChars]) {
44144                   dialCode = v.substr(1, i);
44145                 }
44146                 if (numericChars.length == 4) {
44147                   break;
44148                 }
44149               }
44150             }
44151             return dialCode;
44152         },
44153         
44154         reset : function()
44155         {
44156             this.setValue(this.defaultDialCode);
44157             this.validate();
44158         },
44159         
44160         hiddenEl : function()
44161         {
44162             return this.el.select('input.hidden-tel-input',true).first();
44163         },
44164         
44165         // after setting val
44166         onKeyUp : function(e){
44167             this.setValue(this.getValue());
44168         },
44169         
44170         onKeyPress : function(e){
44171             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44172                 e.stopEvent();
44173             }
44174         }
44175         
44176 });
44177 /**
44178  * @class Roo.bootstrap.MoneyField
44179  * @extends Roo.bootstrap.ComboBox
44180  * Bootstrap MoneyField class
44181  * 
44182  * @constructor
44183  * Create a new MoneyField.
44184  * @param {Object} config Configuration options
44185  */
44186
44187 Roo.bootstrap.MoneyField = function(config) {
44188     
44189     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44190     
44191 };
44192
44193 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44194     
44195     /**
44196      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44197      */
44198     allowDecimals : true,
44199     /**
44200      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44201      */
44202     decimalSeparator : ".",
44203     /**
44204      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44205      */
44206     decimalPrecision : 0,
44207     /**
44208      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44209      */
44210     allowNegative : true,
44211     /**
44212      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44213      */
44214     allowZero: true,
44215     /**
44216      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44217      */
44218     minValue : Number.NEGATIVE_INFINITY,
44219     /**
44220      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44221      */
44222     maxValue : Number.MAX_VALUE,
44223     /**
44224      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44225      */
44226     minText : "The minimum value for this field is {0}",
44227     /**
44228      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44229      */
44230     maxText : "The maximum value for this field is {0}",
44231     /**
44232      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44233      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44234      */
44235     nanText : "{0} is not a valid number",
44236     /**
44237      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44238      */
44239     castInt : true,
44240     /**
44241      * @cfg {String} defaults currency of the MoneyField
44242      * value should be in lkey
44243      */
44244     defaultCurrency : false,
44245     /**
44246      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44247      */
44248     thousandsDelimiter : false,
44249     /**
44250      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44251      */
44252     max_length: false,
44253     
44254     inputlg : 9,
44255     inputmd : 9,
44256     inputsm : 9,
44257     inputxs : 6,
44258     
44259     store : false,
44260     
44261     getAutoCreate : function()
44262     {
44263         var align = this.labelAlign || this.parentLabelAlign();
44264         
44265         var id = Roo.id();
44266
44267         var cfg = {
44268             cls: 'form-group',
44269             cn: []
44270         };
44271
44272         var input =  {
44273             tag: 'input',
44274             id : id,
44275             cls : 'form-control roo-money-amount-input',
44276             autocomplete: 'new-password'
44277         };
44278         
44279         var hiddenInput = {
44280             tag: 'input',
44281             type: 'hidden',
44282             id: Roo.id(),
44283             cls: 'hidden-number-input'
44284         };
44285         
44286         if(this.max_length) {
44287             input.maxlength = this.max_length; 
44288         }
44289         
44290         if (this.name) {
44291             hiddenInput.name = this.name;
44292         }
44293
44294         if (this.disabled) {
44295             input.disabled = true;
44296         }
44297
44298         var clg = 12 - this.inputlg;
44299         var cmd = 12 - this.inputmd;
44300         var csm = 12 - this.inputsm;
44301         var cxs = 12 - this.inputxs;
44302         
44303         var container = {
44304             tag : 'div',
44305             cls : 'row roo-money-field',
44306             cn : [
44307                 {
44308                     tag : 'div',
44309                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44310                     cn : [
44311                         {
44312                             tag : 'div',
44313                             cls: 'roo-select2-container input-group',
44314                             cn: [
44315                                 {
44316                                     tag : 'input',
44317                                     cls : 'form-control roo-money-currency-input',
44318                                     autocomplete: 'new-password',
44319                                     readOnly : 1,
44320                                     name : this.currencyName
44321                                 },
44322                                 {
44323                                     tag :'span',
44324                                     cls : 'input-group-addon',
44325                                     cn : [
44326                                         {
44327                                             tag: 'span',
44328                                             cls: 'caret'
44329                                         }
44330                                     ]
44331                                 }
44332                             ]
44333                         }
44334                     ]
44335                 },
44336                 {
44337                     tag : 'div',
44338                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44339                     cn : [
44340                         {
44341                             tag: 'div',
44342                             cls: this.hasFeedback ? 'has-feedback' : '',
44343                             cn: [
44344                                 input
44345                             ]
44346                         }
44347                     ]
44348                 }
44349             ]
44350             
44351         };
44352         
44353         if (this.fieldLabel.length) {
44354             var indicator = {
44355                 tag: 'i',
44356                 tooltip: 'This field is required'
44357             };
44358
44359             var label = {
44360                 tag: 'label',
44361                 'for':  id,
44362                 cls: 'control-label',
44363                 cn: []
44364             };
44365
44366             var label_text = {
44367                 tag: 'span',
44368                 html: this.fieldLabel
44369             };
44370
44371             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44372             label.cn = [
44373                 indicator,
44374                 label_text
44375             ];
44376
44377             if(this.indicatorpos == 'right') {
44378                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44379                 label.cn = [
44380                     label_text,
44381                     indicator
44382                 ];
44383             }
44384
44385             if(align == 'left') {
44386                 container = {
44387                     tag: 'div',
44388                     cn: [
44389                         container
44390                     ]
44391                 };
44392
44393                 if(this.labelWidth > 12){
44394                     label.style = "width: " + this.labelWidth + 'px';
44395                 }
44396                 if(this.labelWidth < 13 && this.labelmd == 0){
44397                     this.labelmd = this.labelWidth;
44398                 }
44399                 if(this.labellg > 0){
44400                     label.cls += ' col-lg-' + this.labellg;
44401                     input.cls += ' col-lg-' + (12 - this.labellg);
44402                 }
44403                 if(this.labelmd > 0){
44404                     label.cls += ' col-md-' + this.labelmd;
44405                     container.cls += ' col-md-' + (12 - this.labelmd);
44406                 }
44407                 if(this.labelsm > 0){
44408                     label.cls += ' col-sm-' + this.labelsm;
44409                     container.cls += ' col-sm-' + (12 - this.labelsm);
44410                 }
44411                 if(this.labelxs > 0){
44412                     label.cls += ' col-xs-' + this.labelxs;
44413                     container.cls += ' col-xs-' + (12 - this.labelxs);
44414                 }
44415             }
44416         }
44417
44418         cfg.cn = [
44419             label,
44420             container,
44421             hiddenInput
44422         ];
44423         
44424         var settings = this;
44425
44426         ['xs','sm','md','lg'].map(function(size){
44427             if (settings[size]) {
44428                 cfg.cls += ' col-' + size + '-' + settings[size];
44429             }
44430         });
44431         
44432         return cfg;
44433     },
44434     
44435     initEvents : function()
44436     {
44437         this.indicator = this.indicatorEl();
44438         
44439         this.initCurrencyEvent();
44440         
44441         this.initNumberEvent();
44442     },
44443     
44444     initCurrencyEvent : function()
44445     {
44446         if (!this.store) {
44447             throw "can not find store for combo";
44448         }
44449         
44450         this.store = Roo.factory(this.store, Roo.data);
44451         this.store.parent = this;
44452         
44453         this.createList();
44454         
44455         this.triggerEl = this.el.select('.input-group-addon', true).first();
44456         
44457         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44458         
44459         var _this = this;
44460         
44461         (function(){
44462             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44463             _this.list.setWidth(lw);
44464         }).defer(100);
44465         
44466         this.list.on('mouseover', this.onViewOver, this);
44467         this.list.on('mousemove', this.onViewMove, this);
44468         this.list.on('scroll', this.onViewScroll, this);
44469         
44470         if(!this.tpl){
44471             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44472         }
44473         
44474         this.view = new Roo.View(this.list, this.tpl, {
44475             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44476         });
44477         
44478         this.view.on('click', this.onViewClick, this);
44479         
44480         this.store.on('beforeload', this.onBeforeLoad, this);
44481         this.store.on('load', this.onLoad, this);
44482         this.store.on('loadexception', this.onLoadException, this);
44483         
44484         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44485             "up" : function(e){
44486                 this.inKeyMode = true;
44487                 this.selectPrev();
44488             },
44489
44490             "down" : function(e){
44491                 if(!this.isExpanded()){
44492                     this.onTriggerClick();
44493                 }else{
44494                     this.inKeyMode = true;
44495                     this.selectNext();
44496                 }
44497             },
44498
44499             "enter" : function(e){
44500                 this.collapse();
44501                 
44502                 if(this.fireEvent("specialkey", this, e)){
44503                     this.onViewClick(false);
44504                 }
44505                 
44506                 return true;
44507             },
44508
44509             "esc" : function(e){
44510                 this.collapse();
44511             },
44512
44513             "tab" : function(e){
44514                 this.collapse();
44515                 
44516                 if(this.fireEvent("specialkey", this, e)){
44517                     this.onViewClick(false);
44518                 }
44519                 
44520                 return true;
44521             },
44522
44523             scope : this,
44524
44525             doRelay : function(foo, bar, hname){
44526                 if(hname == 'down' || this.scope.isExpanded()){
44527                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44528                 }
44529                 return true;
44530             },
44531
44532             forceKeyDown: true
44533         });
44534         
44535         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44536         
44537     },
44538     
44539     initNumberEvent : function(e)
44540     {
44541         this.inputEl().on("keydown" , this.fireKey,  this);
44542         this.inputEl().on("focus", this.onFocus,  this);
44543         this.inputEl().on("blur", this.onBlur,  this);
44544         
44545         this.inputEl().relayEvent('keyup', this);
44546         
44547         if(this.indicator){
44548             this.indicator.addClass('invisible');
44549         }
44550  
44551         this.originalValue = this.getValue();
44552         
44553         if(this.validationEvent == 'keyup'){
44554             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44555             this.inputEl().on('keyup', this.filterValidation, this);
44556         }
44557         else if(this.validationEvent !== false){
44558             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44559         }
44560         
44561         if(this.selectOnFocus){
44562             this.on("focus", this.preFocus, this);
44563             
44564         }
44565         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44566             this.inputEl().on("keypress", this.filterKeys, this);
44567         } else {
44568             this.inputEl().relayEvent('keypress', this);
44569         }
44570         
44571         var allowed = "0123456789";
44572         
44573         if(this.allowDecimals){
44574             allowed += this.decimalSeparator;
44575         }
44576         
44577         if(this.allowNegative){
44578             allowed += "-";
44579         }
44580         
44581         if(this.thousandsDelimiter) {
44582             allowed += ",";
44583         }
44584         
44585         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44586         
44587         var keyPress = function(e){
44588             
44589             var k = e.getKey();
44590             
44591             var c = e.getCharCode();
44592             
44593             if(
44594                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44595                     allowed.indexOf(String.fromCharCode(c)) === -1
44596             ){
44597                 e.stopEvent();
44598                 return;
44599             }
44600             
44601             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44602                 return;
44603             }
44604             
44605             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44606                 e.stopEvent();
44607             }
44608         };
44609         
44610         this.inputEl().on("keypress", keyPress, this);
44611         
44612     },
44613     
44614     onTriggerClick : function(e)
44615     {   
44616         if(this.disabled){
44617             return;
44618         }
44619         
44620         this.page = 0;
44621         this.loadNext = false;
44622         
44623         if(this.isExpanded()){
44624             this.collapse();
44625             return;
44626         }
44627         
44628         this.hasFocus = true;
44629         
44630         if(this.triggerAction == 'all') {
44631             this.doQuery(this.allQuery, true);
44632             return;
44633         }
44634         
44635         this.doQuery(this.getRawValue());
44636     },
44637     
44638     getCurrency : function()
44639     {   
44640         var v = this.currencyEl().getValue();
44641         
44642         return v;
44643     },
44644     
44645     restrictHeight : function()
44646     {
44647         this.list.alignTo(this.currencyEl(), this.listAlign);
44648         this.list.alignTo(this.currencyEl(), this.listAlign);
44649     },
44650     
44651     onViewClick : function(view, doFocus, el, e)
44652     {
44653         var index = this.view.getSelectedIndexes()[0];
44654         
44655         var r = this.store.getAt(index);
44656         
44657         if(r){
44658             this.onSelect(r, index);
44659         }
44660     },
44661     
44662     onSelect : function(record, index){
44663         
44664         if(this.fireEvent('beforeselect', this, record, index) !== false){
44665         
44666             this.setFromCurrencyData(index > -1 ? record.data : false);
44667             
44668             this.collapse();
44669             
44670             this.fireEvent('select', this, record, index);
44671         }
44672     },
44673     
44674     setFromCurrencyData : function(o)
44675     {
44676         var currency = '';
44677         
44678         this.lastCurrency = o;
44679         
44680         if (this.currencyField) {
44681             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44682         } else {
44683             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44684         }
44685         
44686         this.lastSelectionText = currency;
44687         
44688         //setting default currency
44689         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44690             this.setCurrency(this.defaultCurrency);
44691             return;
44692         }
44693         
44694         this.setCurrency(currency);
44695     },
44696     
44697     setFromData : function(o)
44698     {
44699         var c = {};
44700         
44701         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44702         
44703         this.setFromCurrencyData(c);
44704         
44705         var value = '';
44706         
44707         if (this.name) {
44708             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44709         } else {
44710             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44711         }
44712         
44713         this.setValue(value);
44714         
44715     },
44716     
44717     setCurrency : function(v)
44718     {   
44719         this.currencyValue = v;
44720         
44721         if(this.rendered){
44722             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44723             this.validate();
44724         }
44725     },
44726     
44727     setValue : function(v)
44728     {
44729         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44730         
44731         this.value = v;
44732         
44733         if(this.rendered){
44734             
44735             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44736             
44737             this.inputEl().dom.value = (v == '') ? '' :
44738                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44739             
44740             if(!this.allowZero && v === '0') {
44741                 this.hiddenEl().dom.value = '';
44742                 this.inputEl().dom.value = '';
44743             }
44744             
44745             this.validate();
44746         }
44747     },
44748     
44749     getRawValue : function()
44750     {
44751         var v = this.inputEl().getValue();
44752         
44753         return v;
44754     },
44755     
44756     getValue : function()
44757     {
44758         return this.fixPrecision(this.parseValue(this.getRawValue()));
44759     },
44760     
44761     parseValue : function(value)
44762     {
44763         if(this.thousandsDelimiter) {
44764             value += "";
44765             r = new RegExp(",", "g");
44766             value = value.replace(r, "");
44767         }
44768         
44769         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44770         return isNaN(value) ? '' : value;
44771         
44772     },
44773     
44774     fixPrecision : function(value)
44775     {
44776         if(this.thousandsDelimiter) {
44777             value += "";
44778             r = new RegExp(",", "g");
44779             value = value.replace(r, "");
44780         }
44781         
44782         var nan = isNaN(value);
44783         
44784         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44785             return nan ? '' : value;
44786         }
44787         return parseFloat(value).toFixed(this.decimalPrecision);
44788     },
44789     
44790     decimalPrecisionFcn : function(v)
44791     {
44792         return Math.floor(v);
44793     },
44794     
44795     validateValue : function(value)
44796     {
44797         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44798             return false;
44799         }
44800         
44801         var num = this.parseValue(value);
44802         
44803         if(isNaN(num)){
44804             this.markInvalid(String.format(this.nanText, value));
44805             return false;
44806         }
44807         
44808         if(num < this.minValue){
44809             this.markInvalid(String.format(this.minText, this.minValue));
44810             return false;
44811         }
44812         
44813         if(num > this.maxValue){
44814             this.markInvalid(String.format(this.maxText, this.maxValue));
44815             return false;
44816         }
44817         
44818         return true;
44819     },
44820     
44821     validate : function()
44822     {
44823         if(this.disabled || this.allowBlank){
44824             this.markValid();
44825             return true;
44826         }
44827         
44828         var currency = this.getCurrency();
44829         
44830         if(this.validateValue(this.getRawValue()) && currency.length){
44831             this.markValid();
44832             return true;
44833         }
44834         
44835         this.markInvalid();
44836         return false;
44837     },
44838     
44839     getName: function()
44840     {
44841         return this.name;
44842     },
44843     
44844     beforeBlur : function()
44845     {
44846         if(!this.castInt){
44847             return;
44848         }
44849         
44850         var v = this.parseValue(this.getRawValue());
44851         
44852         if(v || v == 0){
44853             this.setValue(v);
44854         }
44855     },
44856     
44857     onBlur : function()
44858     {
44859         this.beforeBlur();
44860         
44861         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44862             //this.el.removeClass(this.focusClass);
44863         }
44864         
44865         this.hasFocus = false;
44866         
44867         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44868             this.validate();
44869         }
44870         
44871         var v = this.getValue();
44872         
44873         if(String(v) !== String(this.startValue)){
44874             this.fireEvent('change', this, v, this.startValue);
44875         }
44876         
44877         this.fireEvent("blur", this);
44878     },
44879     
44880     inputEl : function()
44881     {
44882         return this.el.select('.roo-money-amount-input', true).first();
44883     },
44884     
44885     currencyEl : function()
44886     {
44887         return this.el.select('.roo-money-currency-input', true).first();
44888     },
44889     
44890     hiddenEl : function()
44891     {
44892         return this.el.select('input.hidden-number-input',true).first();
44893     }
44894     
44895 });/**
44896  * @class Roo.bootstrap.BezierSignature
44897  * @extends Roo.bootstrap.Component
44898  * Bootstrap BezierSignature class
44899  * This script refer to:
44900  *    Title: Signature Pad
44901  *    Author: szimek
44902  *    Availability: https://github.com/szimek/signature_pad
44903  *
44904  * @constructor
44905  * Create a new BezierSignature
44906  * @param {Object} config The config object
44907  */
44908
44909 Roo.bootstrap.BezierSignature = function(config){
44910     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44911     this.addEvents({
44912         "resize" : true
44913     });
44914 };
44915
44916 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44917 {
44918      
44919     curve_data: [],
44920     
44921     is_empty: true,
44922     
44923     mouse_btn_down: true,
44924     
44925     /**
44926      * @cfg {int} canvas height
44927      */
44928     canvas_height: '200px',
44929     
44930     /**
44931      * @cfg {float|function} Radius of a single dot.
44932      */ 
44933     dot_size: false,
44934     
44935     /**
44936      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44937      */
44938     min_width: 0.5,
44939     
44940     /**
44941      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44942      */
44943     max_width: 2.5,
44944     
44945     /**
44946      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44947      */
44948     throttle: 16,
44949     
44950     /**
44951      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44952      */
44953     min_distance: 5,
44954     
44955     /**
44956      * @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.
44957      */
44958     bg_color: 'rgba(0, 0, 0, 0)',
44959     
44960     /**
44961      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44962      */
44963     dot_color: 'black',
44964     
44965     /**
44966      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44967      */ 
44968     velocity_filter_weight: 0.7,
44969     
44970     /**
44971      * @cfg {function} Callback when stroke begin. 
44972      */
44973     onBegin: false,
44974     
44975     /**
44976      * @cfg {function} Callback when stroke end.
44977      */
44978     onEnd: false,
44979     
44980     getAutoCreate : function()
44981     {
44982         var cls = 'roo-signature column';
44983         
44984         if(this.cls){
44985             cls += ' ' + this.cls;
44986         }
44987         
44988         var col_sizes = [
44989             'lg',
44990             'md',
44991             'sm',
44992             'xs'
44993         ];
44994         
44995         for(var i = 0; i < col_sizes.length; i++) {
44996             if(this[col_sizes[i]]) {
44997                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44998             }
44999         }
45000         
45001         var cfg = {
45002             tag: 'div',
45003             cls: cls,
45004             cn: [
45005                 {
45006                     tag: 'div',
45007                     cls: 'roo-signature-body',
45008                     cn: [
45009                         {
45010                             tag: 'canvas',
45011                             cls: 'roo-signature-body-canvas',
45012                             height: this.canvas_height,
45013                             width: this.canvas_width
45014                         }
45015                     ]
45016                 },
45017                 {
45018                     tag: 'input',
45019                     type: 'file',
45020                     style: 'display: none'
45021                 }
45022             ]
45023         };
45024         
45025         return cfg;
45026     },
45027     
45028     initEvents: function() 
45029     {
45030         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45031         
45032         var canvas = this.canvasEl();
45033         
45034         // mouse && touch event swapping...
45035         canvas.dom.style.touchAction = 'none';
45036         canvas.dom.style.msTouchAction = 'none';
45037         
45038         this.mouse_btn_down = false;
45039         canvas.on('mousedown', this._handleMouseDown, this);
45040         canvas.on('mousemove', this._handleMouseMove, this);
45041         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45042         
45043         if (window.PointerEvent) {
45044             canvas.on('pointerdown', this._handleMouseDown, this);
45045             canvas.on('pointermove', this._handleMouseMove, this);
45046             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45047         }
45048         
45049         if ('ontouchstart' in window) {
45050             canvas.on('touchstart', this._handleTouchStart, this);
45051             canvas.on('touchmove', this._handleTouchMove, this);
45052             canvas.on('touchend', this._handleTouchEnd, this);
45053         }
45054         
45055         Roo.EventManager.onWindowResize(this.resize, this, true);
45056         
45057         // file input event
45058         this.fileEl().on('change', this.uploadImage, this);
45059         
45060         this.clear();
45061         
45062         this.resize();
45063     },
45064     
45065     resize: function(){
45066         
45067         var canvas = this.canvasEl().dom;
45068         var ctx = this.canvasElCtx();
45069         var img_data = false;
45070         
45071         if(canvas.width > 0) {
45072             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45073         }
45074         // setting canvas width will clean img data
45075         canvas.width = 0;
45076         
45077         var style = window.getComputedStyle ? 
45078             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45079             
45080         var padding_left = parseInt(style.paddingLeft) || 0;
45081         var padding_right = parseInt(style.paddingRight) || 0;
45082         
45083         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45084         
45085         if(img_data) {
45086             ctx.putImageData(img_data, 0, 0);
45087         }
45088     },
45089     
45090     _handleMouseDown: function(e)
45091     {
45092         if (e.browserEvent.which === 1) {
45093             this.mouse_btn_down = true;
45094             this.strokeBegin(e);
45095         }
45096     },
45097     
45098     _handleMouseMove: function (e)
45099     {
45100         if (this.mouse_btn_down) {
45101             this.strokeMoveUpdate(e);
45102         }
45103     },
45104     
45105     _handleMouseUp: function (e)
45106     {
45107         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45108             this.mouse_btn_down = false;
45109             this.strokeEnd(e);
45110         }
45111     },
45112     
45113     _handleTouchStart: function (e) {
45114         
45115         e.preventDefault();
45116         if (e.browserEvent.targetTouches.length === 1) {
45117             // var touch = e.browserEvent.changedTouches[0];
45118             // this.strokeBegin(touch);
45119             
45120              this.strokeBegin(e); // assume e catching the correct xy...
45121         }
45122     },
45123     
45124     _handleTouchMove: function (e) {
45125         e.preventDefault();
45126         // var touch = event.targetTouches[0];
45127         // _this._strokeMoveUpdate(touch);
45128         this.strokeMoveUpdate(e);
45129     },
45130     
45131     _handleTouchEnd: function (e) {
45132         var wasCanvasTouched = e.target === this.canvasEl().dom;
45133         if (wasCanvasTouched) {
45134             e.preventDefault();
45135             // var touch = event.changedTouches[0];
45136             // _this._strokeEnd(touch);
45137             this.strokeEnd(e);
45138         }
45139     },
45140     
45141     reset: function () {
45142         this._lastPoints = [];
45143         this._lastVelocity = 0;
45144         this._lastWidth = (this.min_width + this.max_width) / 2;
45145         this.canvasElCtx().fillStyle = this.dot_color;
45146     },
45147     
45148     strokeMoveUpdate: function(e)
45149     {
45150         this.strokeUpdate(e);
45151         
45152         if (this.throttle) {
45153             this.throttleStroke(this.strokeUpdate, this.throttle);
45154         }
45155         else {
45156             this.strokeUpdate(e);
45157         }
45158     },
45159     
45160     strokeBegin: function(e)
45161     {
45162         var newPointGroup = {
45163             color: this.dot_color,
45164             points: []
45165         };
45166         
45167         if (typeof this.onBegin === 'function') {
45168             this.onBegin(e);
45169         }
45170         
45171         this.curve_data.push(newPointGroup);
45172         this.reset();
45173         this.strokeUpdate(e);
45174     },
45175     
45176     strokeUpdate: function(e)
45177     {
45178         var rect = this.canvasEl().dom.getBoundingClientRect();
45179         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45180         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45181         var lastPoints = lastPointGroup.points;
45182         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45183         var isLastPointTooClose = lastPoint
45184             ? point.distanceTo(lastPoint) <= this.min_distance
45185             : false;
45186         var color = lastPointGroup.color;
45187         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45188             var curve = this.addPoint(point);
45189             if (!lastPoint) {
45190                 this.drawDot({color: color, point: point});
45191             }
45192             else if (curve) {
45193                 this.drawCurve({color: color, curve: curve});
45194             }
45195             lastPoints.push({
45196                 time: point.time,
45197                 x: point.x,
45198                 y: point.y
45199             });
45200         }
45201     },
45202     
45203     strokeEnd: function(e)
45204     {
45205         this.strokeUpdate(e);
45206         if (typeof this.onEnd === 'function') {
45207             this.onEnd(e);
45208         }
45209     },
45210     
45211     addPoint:  function (point) {
45212         var _lastPoints = this._lastPoints;
45213         _lastPoints.push(point);
45214         if (_lastPoints.length > 2) {
45215             if (_lastPoints.length === 3) {
45216                 _lastPoints.unshift(_lastPoints[0]);
45217             }
45218             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45219             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45220             _lastPoints.shift();
45221             return curve;
45222         }
45223         return null;
45224     },
45225     
45226     calculateCurveWidths: function (startPoint, endPoint) {
45227         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45228             (1 - this.velocity_filter_weight) * this._lastVelocity;
45229
45230         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45231         var widths = {
45232             end: newWidth,
45233             start: this._lastWidth
45234         };
45235         
45236         this._lastVelocity = velocity;
45237         this._lastWidth = newWidth;
45238         return widths;
45239     },
45240     
45241     drawDot: function (_a) {
45242         var color = _a.color, point = _a.point;
45243         var ctx = this.canvasElCtx();
45244         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45245         ctx.beginPath();
45246         this.drawCurveSegment(point.x, point.y, width);
45247         ctx.closePath();
45248         ctx.fillStyle = color;
45249         ctx.fill();
45250     },
45251     
45252     drawCurve: function (_a) {
45253         var color = _a.color, curve = _a.curve;
45254         var ctx = this.canvasElCtx();
45255         var widthDelta = curve.endWidth - curve.startWidth;
45256         var drawSteps = Math.floor(curve.length()) * 2;
45257         ctx.beginPath();
45258         ctx.fillStyle = color;
45259         for (var i = 0; i < drawSteps; i += 1) {
45260         var t = i / drawSteps;
45261         var tt = t * t;
45262         var ttt = tt * t;
45263         var u = 1 - t;
45264         var uu = u * u;
45265         var uuu = uu * u;
45266         var x = uuu * curve.startPoint.x;
45267         x += 3 * uu * t * curve.control1.x;
45268         x += 3 * u * tt * curve.control2.x;
45269         x += ttt * curve.endPoint.x;
45270         var y = uuu * curve.startPoint.y;
45271         y += 3 * uu * t * curve.control1.y;
45272         y += 3 * u * tt * curve.control2.y;
45273         y += ttt * curve.endPoint.y;
45274         var width = curve.startWidth + ttt * widthDelta;
45275         this.drawCurveSegment(x, y, width);
45276         }
45277         ctx.closePath();
45278         ctx.fill();
45279     },
45280     
45281     drawCurveSegment: function (x, y, width) {
45282         var ctx = this.canvasElCtx();
45283         ctx.moveTo(x, y);
45284         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45285         this.is_empty = false;
45286     },
45287     
45288     clear: function()
45289     {
45290         var ctx = this.canvasElCtx();
45291         var canvas = this.canvasEl().dom;
45292         ctx.fillStyle = this.bg_color;
45293         ctx.clearRect(0, 0, canvas.width, canvas.height);
45294         ctx.fillRect(0, 0, canvas.width, canvas.height);
45295         this.curve_data = [];
45296         this.reset();
45297         this.is_empty = true;
45298     },
45299     
45300     fileEl: function()
45301     {
45302         return  this.el.select('input',true).first();
45303     },
45304     
45305     canvasEl: function()
45306     {
45307         return this.el.select('canvas',true).first();
45308     },
45309     
45310     canvasElCtx: function()
45311     {
45312         return this.el.select('canvas',true).first().dom.getContext('2d');
45313     },
45314     
45315     getImage: function(type)
45316     {
45317         if(this.is_empty) {
45318             return false;
45319         }
45320         
45321         // encryption ?
45322         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45323     },
45324     
45325     drawFromImage: function(img_src)
45326     {
45327         var img = new Image();
45328         
45329         img.onload = function(){
45330             this.canvasElCtx().drawImage(img, 0, 0);
45331         }.bind(this);
45332         
45333         img.src = img_src;
45334         
45335         this.is_empty = false;
45336     },
45337     
45338     selectImage: function()
45339     {
45340         this.fileEl().dom.click();
45341     },
45342     
45343     uploadImage: function(e)
45344     {
45345         var reader = new FileReader();
45346         
45347         reader.onload = function(e){
45348             var img = new Image();
45349             img.onload = function(){
45350                 this.reset();
45351                 this.canvasElCtx().drawImage(img, 0, 0);
45352             }.bind(this);
45353             img.src = e.target.result;
45354         }.bind(this);
45355         
45356         reader.readAsDataURL(e.target.files[0]);
45357     },
45358     
45359     // Bezier Point Constructor
45360     Point: (function () {
45361         function Point(x, y, time) {
45362             this.x = x;
45363             this.y = y;
45364             this.time = time || Date.now();
45365         }
45366         Point.prototype.distanceTo = function (start) {
45367             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45368         };
45369         Point.prototype.equals = function (other) {
45370             return this.x === other.x && this.y === other.y && this.time === other.time;
45371         };
45372         Point.prototype.velocityFrom = function (start) {
45373             return this.time !== start.time
45374             ? this.distanceTo(start) / (this.time - start.time)
45375             : 0;
45376         };
45377         return Point;
45378     }()),
45379     
45380     
45381     // Bezier Constructor
45382     Bezier: (function () {
45383         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45384             this.startPoint = startPoint;
45385             this.control2 = control2;
45386             this.control1 = control1;
45387             this.endPoint = endPoint;
45388             this.startWidth = startWidth;
45389             this.endWidth = endWidth;
45390         }
45391         Bezier.fromPoints = function (points, widths, scope) {
45392             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45393             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45394             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45395         };
45396         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45397             var dx1 = s1.x - s2.x;
45398             var dy1 = s1.y - s2.y;
45399             var dx2 = s2.x - s3.x;
45400             var dy2 = s2.y - s3.y;
45401             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45402             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45403             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45404             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45405             var dxm = m1.x - m2.x;
45406             var dym = m1.y - m2.y;
45407             var k = l2 / (l1 + l2);
45408             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45409             var tx = s2.x - cm.x;
45410             var ty = s2.y - cm.y;
45411             return {
45412                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45413                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45414             };
45415         };
45416         Bezier.prototype.length = function () {
45417             var steps = 10;
45418             var length = 0;
45419             var px;
45420             var py;
45421             for (var i = 0; i <= steps; i += 1) {
45422                 var t = i / steps;
45423                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45424                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45425                 if (i > 0) {
45426                     var xdiff = cx - px;
45427                     var ydiff = cy - py;
45428                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45429                 }
45430                 px = cx;
45431                 py = cy;
45432             }
45433             return length;
45434         };
45435         Bezier.prototype.point = function (t, start, c1, c2, end) {
45436             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45437             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45438             + (3.0 * c2 * (1.0 - t) * t * t)
45439             + (end * t * t * t);
45440         };
45441         return Bezier;
45442     }()),
45443     
45444     throttleStroke: function(fn, wait) {
45445       if (wait === void 0) { wait = 250; }
45446       var previous = 0;
45447       var timeout = null;
45448       var result;
45449       var storedContext;
45450       var storedArgs;
45451       var later = function () {
45452           previous = Date.now();
45453           timeout = null;
45454           result = fn.apply(storedContext, storedArgs);
45455           if (!timeout) {
45456               storedContext = null;
45457               storedArgs = [];
45458           }
45459       };
45460       return function wrapper() {
45461           var args = [];
45462           for (var _i = 0; _i < arguments.length; _i++) {
45463               args[_i] = arguments[_i];
45464           }
45465           var now = Date.now();
45466           var remaining = wait - (now - previous);
45467           storedContext = this;
45468           storedArgs = args;
45469           if (remaining <= 0 || remaining > wait) {
45470               if (timeout) {
45471                   clearTimeout(timeout);
45472                   timeout = null;
45473               }
45474               previous = now;
45475               result = fn.apply(storedContext, storedArgs);
45476               if (!timeout) {
45477                   storedContext = null;
45478                   storedArgs = [];
45479               }
45480           }
45481           else if (!timeout) {
45482               timeout = window.setTimeout(later, remaining);
45483           }
45484           return result;
45485       };
45486   }
45487   
45488 });
45489
45490  
45491
45492